diff --git a/examples/CRISP/sdk/src/constants.ts b/examples/CRISP/sdk/src/constants.ts index f2fd540441..0fff8b6607 100644 --- a/examples/CRISP/sdk/src/constants.ts +++ b/examples/CRISP/sdk/src/constants.ts @@ -5,3 +5,4 @@ // or FITNESS FOR A PARTICULAR PURPOSE. export const CRISP_SERVER_TOKEN_TREE_ENDPOINT = 'state/token-holders' +export const CRISP_SERVER_STATE_LITE_ENDPOINT = 'state/lite' diff --git a/examples/CRISP/sdk/src/index.ts b/examples/CRISP/sdk/src/index.ts index f6d3f918d6..a2dc538db4 100644 --- a/examples/CRISP/sdk/src/index.ts +++ b/examples/CRISP/sdk/src/index.ts @@ -4,5 +4,8 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -export * from "./token"; -export * from "./constants"; +export * from './token' +export * from './state' +export * from './constants' + +export type { IRoundDetails, ITokenDetails } from './types' diff --git a/examples/CRISP/sdk/src/state.ts b/examples/CRISP/sdk/src/state.ts new file mode 100644 index 0000000000..3a926121e6 --- /dev/null +++ b/examples/CRISP/sdk/src/state.ts @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +import { CRISP_SERVER_STATE_LITE_ENDPOINT } from './constants' + +import type { IRoundDetailsResponse, IRoundDetails, ITokenDetails } from './types' + +/** + * Get the details of a specific round + */ +export const getRoundDetails = async (serverUrl: string, e3Id: number): Promise => { + const response = await fetch(`${serverUrl}/${CRISP_SERVER_STATE_LITE_ENDPOINT}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ round_id: e3Id }), + }) + + const data = (await response.json()) as IRoundDetailsResponse + + return { + e3Id: BigInt(data.id), + tokenAddress: data.token_address, + balanceThreshold: BigInt(data.balance_threshold), + chainId: BigInt(data.chain_id), + enclaveAddress: data.enclave_address, + status: data.status, + voteCount: BigInt(data.vote_count), + startTime: BigInt(data.start_time), + duration: BigInt(data.duration), + expiration: BigInt(data.expiration), + startBlock: BigInt(data.start_block), + committeePublicKey: data.committee_public_key, + emojis: data.emojis, + } +} + +/** + * Get the token address, balance threshold and snapshot block for a specific round + * @param serverUrl - The base URL of the CRISP server + * @param e3Id - The e3Id of the round + * @returns The token address, balance threshold and snapshot block + */ +export const getRoundTokenDetails = async (serverUrl: string, e3Id: number): Promise => { + const roundDetails = await getRoundDetails(serverUrl, e3Id) + return { + tokenAddress: roundDetails.tokenAddress, + threshold: roundDetails.balanceThreshold, + snapshotBlock: roundDetails.startBlock, + } +} diff --git a/examples/CRISP/sdk/src/token.ts b/examples/CRISP/sdk/src/token.ts index a743ed5001..e0c10ded71 100644 --- a/examples/CRISP/sdk/src/token.ts +++ b/examples/CRISP/sdk/src/token.ts @@ -12,21 +12,17 @@ import { CRISP_SERVER_TOKEN_TREE_ENDPOINT } from './constants' * @param e3Id - The e3Id of the round */ export const getTreeData = async (serverUrl: string, e3Id: number) => { - try { - const response = await fetch(`${serverUrl}/${CRISP_SERVER_TOKEN_TREE_ENDPOINT}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ round_id: e3Id }), - }) + const response = await fetch(`${serverUrl}/${CRISP_SERVER_TOKEN_TREE_ENDPOINT}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ round_id: e3Id }), + }) - const hashes = await response.json() + const hashes = await response.json() - return hashes - } catch (error) { - console.error('Error fetching tree data:', error) - } + return hashes } /** @@ -38,17 +34,3 @@ export const generateMerkleProof = () => {} * Get the token balance at a specific block for a given address */ export const getBalanceAt = () => {} - -/** - * Interface representing the details of a specific round - */ -export interface IRoundDetails { - tokenAddress: string - snapshotBlock: string - threshold: string -} - -/** - * Get the details of a specific round - */ -export const getRoundDetails = () => {} diff --git a/examples/CRISP/sdk/src/types.ts b/examples/CRISP/sdk/src/types.ts new file mode 100644 index 0000000000..8daca5b4ea --- /dev/null +++ b/examples/CRISP/sdk/src/types.ts @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +/** + * Interface representing the details of a specific round returned by the CRISP server + */ +export interface IRoundDetailsResponse { + id: string + chain_id: string + enclave_address: string + status: string + vote_count: string + start_time: string + duration: string + expiration: string + start_block: string + committee_public_key: string[] + emojis: [string, string] + token_address: string + balance_threshold: string +} + +/** + * Interface representing the details of a specific round in a more convenient format + */ +export interface IRoundDetails { + e3Id: bigint + chainId: bigint + enclaveAddress: string + status: string + voteCount: bigint + startTime: bigint + duration: bigint + expiration: bigint + startBlock: bigint + committeePublicKey: string[] + emojis: [string, string] + tokenAddress: string + balanceThreshold: bigint +} + +/** + * Interface representing the token details required for participation in a round + */ +export interface ITokenDetails { + tokenAddress: string + threshold: bigint + snapshotBlock: bigint +} diff --git a/examples/CRISP/sdk/tests/constants.ts b/examples/CRISP/sdk/tests/constants.ts new file mode 100644 index 0000000000..ce3a8c6784 --- /dev/null +++ b/examples/CRISP/sdk/tests/constants.ts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +export const CRISP_SERVER_URL = 'http://localhost:4000' diff --git a/examples/CRISP/sdk/tests/state.test.ts b/examples/CRISP/sdk/tests/state.test.ts new file mode 100644 index 0000000000..5287ab4c37 --- /dev/null +++ b/examples/CRISP/sdk/tests/state.test.ts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LGPL-3.0-only +// +// This file is provided WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY +// or FITNESS FOR A PARTICULAR PURPOSE. + +import { describe, expect, it } from 'vitest' + +import { getRoundDetails, getRoundTokenDetails } from '../src/state' +import { CRISP_SERVER_URL } from './constants' +import { zeroAddress } from 'viem' + +describe('State', () => { + describe('getRoundDetails', () => { + it('should get the state for a given e3Id from the CRISP server', async () => { + const state = await getRoundDetails(CRISP_SERVER_URL, 0) + expect(state).toBeDefined() + }) + }) + + describe('getTokenDetails', () => { + it('should return the details of the token for a given e3Id from the CRISP server', async () => { + const tokenDetails = await getRoundTokenDetails(CRISP_SERVER_URL, 0) + expect(tokenDetails.tokenAddress).not.toBe(zeroAddress) + expect(tokenDetails.threshold).toBeGreaterThan(0) + expect(tokenDetails.snapshotBlock).toBeGreaterThan(0) + }) + }) +}) diff --git a/examples/CRISP/sdk/tests/token.test.ts b/examples/CRISP/sdk/tests/token.test.ts index 2dc6251a68..6bd95b68d7 100644 --- a/examples/CRISP/sdk/tests/token.test.ts +++ b/examples/CRISP/sdk/tests/token.test.ts @@ -7,12 +7,12 @@ import { describe, expect, it } from 'vitest' import { getTreeData } from '../src/token' +import { CRISP_SERVER_URL } from './constants' // @notice To run these tests you will need to have an instance of CRISP running locally describe('Token data fetching', () => { - const serverUrl = 'http://localhost:4000' it('should fetch token data from the CRISP server', async () => { - const data = await getTreeData(serverUrl, 0) + const data = await getTreeData(CRISP_SERVER_URL, 0) expect(data.length).toBeGreaterThan(0) }) }) diff --git a/examples/CRISP/server/src/server/indexer.rs b/examples/CRISP/server/src/server/indexer.rs index 3d097f7597..a00dc9f533 100644 --- a/examples/CRISP/server/src/server/indexer.rs +++ b/examples/CRISP/server/src/server/indexer.rs @@ -47,12 +47,10 @@ pub async fn register_e3_requested( info!("E3Requested: {:?}", event); async move { - repo.initialize_round().await?; - - // Convert custom params bytes back to token address and balance threshold. - let custom_params: CustomParams = - serde_json::from_slice(&event.e3.customParams) - .with_context(|| "Failed to parse custom params from E3 event")?; + // Convert custom params bytes back to token address and balance threshold. + let custom_params: CustomParams = + serde_json::from_slice(&event.e3.customParams) + .with_context(|| "Failed to parse custom params from E3 event")?; let token_address: Address = custom_params .token_address @@ -63,6 +61,9 @@ pub async fn register_e3_requested( BigUint::parse_bytes(custom_params.balance_threshold.as_bytes(), 10) .ok_or_else(|| eyre::eyre!("Invalid balance threshold"))?; + // save the e3 details + repo.initialize_round(custom_params.token_address, custom_params.balance_threshold).await?; + // Get token holders from Bitquery API or mocked data. let token_holders = if matches!(CONFIG.chain_id, 31337 | 1337) { info!( diff --git a/examples/CRISP/server/src/server/models.rs b/examples/CRISP/server/src/server/models.rs index a0e328191a..b46c7b033d 100644 --- a/examples/CRISP/server/src/server/models.rs +++ b/examples/CRISP/server/src/server/models.rs @@ -4,9 +4,7 @@ // without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. -use alloy::primitives::Address; use anyhow::Result; -use num_bigint::BigUint; use serde::{Deserialize, Deserializer, Serialize}; #[derive(Deserialize, Debug)] @@ -130,8 +128,12 @@ pub struct E3StateLite { pub committee_public_key: Vec, pub emojis: [String; 2], + + pub token_address: String, + pub balance_threshold: String, } + #[derive(Debug, Deserialize, Serialize)] pub struct E3 { // Identifiers @@ -165,6 +167,9 @@ pub struct E3 { // Emojis pub emojis: [String; 2], + + // Custom Parameters + pub custom_params: CustomParams, } #[derive(Debug, Deserialize, Serialize)] @@ -176,6 +181,8 @@ pub struct E3Crisp { pub votes_option_1: u64, pub votes_option_2: u64, pub token_holder_hashes: Vec, + pub token_address: String, + pub balance_threshold: String, } impl From for WebResultRequest { @@ -191,21 +198,3 @@ impl From for WebResultRequest { } } } - -impl From for E3StateLite { - fn from(e3: E3) -> Self { - E3StateLite { - id: e3.id, - chain_id: e3.chain_id, - enclave_address: e3.enclave_address, - status: e3.status, - vote_count: e3.vote_count, - start_time: e3.start_time, - start_block: e3.block_start, - duration: e3.duration, - expiration: e3.expiration, - committee_public_key: e3.committee_public_key, - emojis: e3.emojis, - } - } -} diff --git a/examples/CRISP/server/src/server/repo.rs b/examples/CRISP/server/src/server/repo.rs index 4cbd0275c2..6a96a081a3 100644 --- a/examples/CRISP/server/src/server/repo.rs +++ b/examples/CRISP/server/src/server/repo.rs @@ -85,33 +85,13 @@ impl CrispE3Repository { } pub async fn start_round(&mut self) -> Result<()> { - let start_time = chrono::Utc::now().timestamp() as u64; - let key = self.crisp_key(); - - self.store - .modify(&key, |e3_obj: Option| { - e3_obj.map(|mut e| { - e.start_time = start_time; - e - }) - }) - .await - .map_err(|_| eyre::eyre!("Could not update start_time for '{key}'"))?; - - self.store - .modify(&key, |e3_obj: Option| { - e3_obj.map(|mut e| { - e.status = "Active".to_string(); - e - }) - }) - .await - .map_err(|_| eyre::eyre!("Could not update status for '{key}'"))?; - - Ok(()) + let mut e3_crisp = self.get_crisp().await?; + e3_crisp.start_time = chrono::Utc::now().timestamp() as u64; + e3_crisp.status = "Active".to_string(); + self.set_crisp(e3_crisp).await } - pub async fn initialize_round(&mut self) -> Result<()> { + pub async fn initialize_round(&mut self, token_address: String, balance_threshold: String) -> Result<()> { self.set_crisp(E3Crisp { has_voted: vec![], start_time: 0u64, @@ -120,6 +100,8 @@ impl CrispE3Repository { votes_option_2: 0, emojis: generate_emoji(), token_holder_hashes: vec![], + token_address, + balance_threshold, }) .await } @@ -208,6 +190,8 @@ impl CrispE3Repository { start_block: e3.request_block, enclave_address: e3.enclave_address, committee_public_key: e3.committee_public_key, + token_address: e3_crisp.token_address, + balance_threshold: e3_crisp.balance_threshold, }) } @@ -260,7 +244,6 @@ impl CrispE3Repository { pub async fn set_token_holder_hashes(&mut self, hashes: Vec) -> Result<()> { let key = self.crisp_key(); - self.store .modify(&key, |e3_obj: Option| { e3_obj.map(|mut e| { @@ -270,7 +253,7 @@ impl CrispE3Repository { }) .await .map_err(|_| eyre::eyre!("Could not set token_holder_hashes for '{key}'"))?; - + Ok(()) } diff --git a/packages/enclave-contracts/.prettierignore b/packages/enclave-contracts/.prettierignore index c3995b0dbc..4dbc5c12db 100644 --- a/packages/enclave-contracts/.prettierignore +++ b/packages/enclave-contracts/.prettierignore @@ -20,3 +20,5 @@ coverage.json package-lock.json pnpm-lock.yaml yarn.lock + +deployed_contracts.json