diff --git a/examples/CRISP/scripts/evm_deploy.sh b/examples/CRISP/scripts/evm_deploy.sh index 7c3a7307bf..030c2b3675 100755 --- a/examples/CRISP/scripts/evm_deploy.sh +++ b/examples/CRISP/scripts/evm_deploy.sh @@ -4,5 +4,4 @@ set -euo pipefail wait-on tcp:8545 && \ (cd ../../packages/enclave-contracts && \ - rm -rf deployments/localhost && \ pnpm deploy:mocks --network localhost) diff --git a/examples/CRISP/sdk/.prettierignore b/examples/CRISP/sdk/.prettierignore new file mode 100644 index 0000000000..94b2ee0907 --- /dev/null +++ b/examples/CRISP/sdk/.prettierignore @@ -0,0 +1,3 @@ +dist +node_modules +.prettierrc diff --git a/examples/CRISP/sdk/.prettierrc b/examples/CRISP/sdk/.prettierrc new file mode 100644 index 0000000000..eccb654e0e --- /dev/null +++ b/examples/CRISP/sdk/.prettierrc @@ -0,0 +1,10 @@ +{ + "printWidth": 140, + "useTabs": false, + "tabWidth": 2, + "singleQuote": true, + "jsxSingleQuote": true, + "semi": false, + "trailingComma": "all", + "arrowParens": "always" +} diff --git a/examples/CRISP/sdk/package.json b/examples/CRISP/sdk/package.json new file mode 100644 index 0000000000..4b697c5681 --- /dev/null +++ b/examples/CRISP/sdk/package.json @@ -0,0 +1,39 @@ +{ + "name": "crisp-sdk", + "version": "0.0.1", + "type": "module", + "files": [ + "dist" + ], + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "author": { + "name": "gnosisguild", + "url": "https://github.com/gnosisguild" + }, + "homepage": "https://github.com/gnosisguild/enclave", + "scripts": { + "build": "tsc", + "test": "vitest --run", + "prettier:fix": "pnpm prettier --write .", + "prettier:check": "pnpm prettier --check ." + }, + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@types/chai": "^5.2.2", + "@types/node": "22.7.5", + "chai": "^6.2.0", + "prettier": "^3.2.5", + "vitest": "^1.6.1", + "typescript": "^5.0.0" + }, + "packageManager": "pnpm@10.7.1+sha512.2d92c86b7928dc8284f53494fb4201f983da65f0fb4f0d40baafa5cf628fa31dae3e5968f12466f17df7e97310e30f343a648baea1b9b350685dafafffdf5808" +} diff --git a/examples/CRISP/sdk/src/constants.ts b/examples/CRISP/sdk/src/constants.ts new file mode 100644 index 0000000000..f2fd540441 --- /dev/null +++ b/examples/CRISP/sdk/src/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_TOKEN_TREE_ENDPOINT = 'state/token-holders' diff --git a/examples/CRISP/sdk/src/index.ts b/examples/CRISP/sdk/src/index.ts new file mode 100644 index 0000000000..983d1bb4d5 --- /dev/null +++ b/examples/CRISP/sdk/src/index.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 * from "./token"; diff --git a/examples/CRISP/sdk/src/token.ts b/examples/CRISP/sdk/src/token.ts new file mode 100644 index 0000000000..a743ed5001 --- /dev/null +++ b/examples/CRISP/sdk/src/token.ts @@ -0,0 +1,54 @@ +// 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_TOKEN_TREE_ENDPOINT } from './constants' + +/** + * Get the merkle tree data from the CRISP server + * @param serverUrl - The base URL of the CRISP server + * @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 hashes = await response.json() + + return hashes + } catch (error) { + console.error('Error fetching tree data:', error) + } +} + +/** + * Generate a Merkle proof for a given address to prove inclusion in the voters' list + */ +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/tests/token.test.ts b/examples/CRISP/sdk/tests/token.test.ts new file mode 100644 index 0000000000..2dc6251a68 --- /dev/null +++ b/examples/CRISP/sdk/tests/token.test.ts @@ -0,0 +1,18 @@ +// 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 { getTreeData } from '../src/token' + +// @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) + expect(data.length).toBeGreaterThan(0) + }) +}) diff --git a/examples/CRISP/sdk/tsconfig.json b/examples/CRISP/sdk/tsconfig.json new file mode 100644 index 0000000000..90e5742226 --- /dev/null +++ b/examples/CRISP/sdk/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true, + "outDir": "dist", + "declaration": true + }, + "include": ["src/**/*.ts"] +} diff --git a/examples/CRISP/server/src/server/indexer.rs b/examples/CRISP/server/src/server/indexer.rs index 010a5abc8e..3d097f7597 100644 --- a/examples/CRISP/server/src/server/indexer.rs +++ b/examples/CRISP/server/src/server/indexer.rs @@ -47,6 +47,8 @@ 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) @@ -134,7 +136,7 @@ pub async fn register_e3_activated( info!("Handling E3 request with id {}", e3_id); async move { - repo.initialize_round().await?; + repo.start_round().await?; current_round_repo .set_current_round(CurrentRound { id: e3_id }) diff --git a/examples/CRISP/server/src/server/repo.rs b/examples/CRISP/server/src/server/repo.rs index 33ff57f1d2..4cbd0275c2 100644 --- a/examples/CRISP/server/src/server/repo.rs +++ b/examples/CRISP/server/src/server/repo.rs @@ -8,7 +8,6 @@ use super::{ database::generate_emoji, models::{CurrentRound, E3Crisp, E3StateLite, WebResultRequest}, }; -use chrono::Utc; use e3_sdk::indexer::{models::E3 as EnclaveE3, DataStore, E3Repository, SharedStore}; use eyre::Result; use log::info; @@ -85,13 +84,38 @@ impl CrispE3Repository { Ok(e3_crisp) } - pub async fn initialize_round(&mut self) -> Result<()> { - let start_time = Utc::now().timestamp() as u64; + 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(()) + } + + pub async fn initialize_round(&mut self) -> Result<()> { self.set_crisp(E3Crisp { has_voted: vec![], - start_time, - status: "Active".to_string(), + start_time: 0u64, + status: "Requested".to_string(), votes_option_1: 0, votes_option_2: 0, emojis: generate_emoji(), @@ -236,6 +260,7 @@ 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| { @@ -245,11 +270,13 @@ impl CrispE3Repository { }) .await .map_err(|_| eyre::eyre!("Could not set token_holder_hashes for '{key}'"))?; + Ok(()) } pub async fn get_token_holder_hashes(&self) -> Result> { let e3_crisp = self.get_crisp().await?; + Ok(e3_crisp.token_holder_hashes) } diff --git a/examples/CRISP/server/src/server/routes/state.rs b/examples/CRISP/server/src/server/routes/state.rs index 1674cd011c..26cbec7cf2 100644 --- a/examples/CRISP/server/src/server/routes/state.rs +++ b/examples/CRISP/server/src/server/routes/state.rs @@ -9,10 +9,12 @@ use crate::server::{ models::{GetRoundRequest, WebhookPayload}, CONFIG, }; -use alloy::primitives::{U256, Bytes}; use actix_web::{web, HttpResponse, Responder}; +use alloy::primitives::{Bytes, U256}; +use e3_sdk::evm_helpers::contracts::{ + EnclaveContract, EnclaveContractFactory, EnclaveWrite, ReadWrite, +}; use log::{error, info}; -use e3_sdk::evm_helpers::contracts::{EnclaveContract, EnclaveContractFactory, EnclaveWrite, ReadWrite}; pub fn setup_routes(config: &mut web::ServiceConfig) { config.service( @@ -20,30 +22,37 @@ pub fn setup_routes(config: &mut web::ServiceConfig) { .route("/result", web::post().to(get_round_result)) .route("/all", web::get().to(get_all_round_results)) .route("/lite", web::post().to(get_round_state_lite)) - // Do we need protection on this endpoint? technically they would need to send a valid proof for it to - // be included on chain - .route("/add-result", web::post().to(handle_program_server_result)), + // Do we need protection on this endpoint? technically they would need to send a valid proof for it to + // be included on chain + .route("/add-result", web::post().to(handle_program_server_result)) + // Get the token holders hashes for a given round + .route("/token-holders", web::post().to(get_token_holders_hashes)), ); } /// Webhook callback from program server -/// +/// /// # Arguments /// * `data` - The request data containing the result from the program server -/// +/// /// # Returns /// * A JSON response indicating the success of the operation async fn handle_program_server_result(data: web::Json) -> impl Responder { let incoming = data.into_inner(); - info!("Received program server result for E3 ID: {:?}", incoming.e3_id); + info!( + "Received program server result for E3 ID: {:?}", + incoming.e3_id + ); // Create the contract let contract: EnclaveContract = match EnclaveContractFactory::create_write( &CONFIG.http_rpc_url, &CONFIG.enclave_address, &CONFIG.private_key, - ).await { + ) + .await + { Ok(contract) => contract, Err(e) => { info!("Failed to create contract: {:?}", e); @@ -70,15 +79,17 @@ async fn handle_program_server_result(data: web::Json) -> impl R } }; - info!("Ciphertext output published successfully for E3 ID: {} with tx: {}", incoming.e3_id, pending_tx.transaction_hash); - + info!( + "Ciphertext output published successfully for E3 ID: {} with tx: {}", + incoming.e3_id, pending_tx.transaction_hash + ); + HttpResponse::Ok().json(format!( "Ciphertext output published successfully for E3 ID: {}", incoming.e3_id )) } - /// Get the result for a given round /// /// # Arguments @@ -149,3 +160,24 @@ async fn get_round_state_lite( Err(_) => HttpResponse::InternalServerError().body("Failed to get E3 state"), } } + +/// Get the hashes of token holders for a given round +/// The hash is hash(address, token balance) +/// # Arguments +/// * `GetRoundRequest` - The request data containing the round ID +/// # Returns +/// * A JSON response containing the list of token holder hashes +async fn get_token_holders_hashes( + data: web::Json, + store: web::Data, +) -> impl Responder { + let incoming = data.into_inner(); + + match store.e3(incoming.round_id).get_token_holder_hashes().await { + Ok(hashes) => HttpResponse::Ok().json(hashes), + Err(e) => { + error!("Error getting token holders hashes: {:?}", e); + HttpResponse::InternalServerError().body("Failed to get token holders hashes") + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 983c64fe5e..784cc96ead 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,7 +118,7 @@ importers: version: 3.0.2(@nomicfoundation/hardhat-verify@3.0.1(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) '@nomicfoundation/hardhat-toolbox': specifier: ^5.0.0 - version: 5.0.0(a2c664a2c86c3ae5dd9cd312b280c590) + version: 5.0.0(9456a2560cd4b23b39399d823783d224) '@nomicfoundation/hardhat-toolbox-viem': specifier: ^5.0.0 version: 5.0.0(57ef2920aa19fe9eb07b2e80a48eb774) @@ -331,6 +331,27 @@ importers: specifier: ^3.3.0 version: 3.5.0(vite@5.4.19(@types/node@22.7.5)) + examples/CRISP/sdk: + devDependencies: + '@types/chai': + specifier: ^5.2.2 + version: 5.2.2 + '@types/node': + specifier: 22.7.5 + version: 22.7.5 + chai: + specifier: ^6.2.0 + version: 6.2.0 + prettier: + specifier: ^3.2.5 + version: 3.6.2 + typescript: + specifier: 5.8.3 + version: 5.8.3 + vitest: + specifier: ^1.6.1 + version: 1.6.1(@types/node@22.7.5) + packages/enclave-config: dependencies: tsup: @@ -3553,6 +3574,9 @@ packages: '@types/chai@4.3.20': resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/concat-stream@1.6.1': resolution: {integrity: sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==} @@ -3571,6 +3595,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -4504,6 +4531,10 @@ packages: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} + chai@6.2.0: + resolution: {integrity: sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==} + engines: {node: '>=18'} + chainsaw@0.1.0: resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} @@ -12206,12 +12237,12 @@ snapshots: '@nomicfoundation/edr-linux-x64-musl': 0.12.0-next.5 '@nomicfoundation/edr-win32-x64-msvc': 0.12.0-next.5 - '@nomicfoundation/hardhat-chai-matchers@2.1.0(@nomicfoundation/hardhat-ethers@3.1.0(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(chai@5.3.3)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@nomicfoundation/hardhat-chai-matchers@2.1.0(@nomicfoundation/hardhat-ethers@3.1.0(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(chai@6.2.0)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@nomicfoundation/hardhat-ethers': 3.1.0(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@types/chai-as-promised': 7.1.8 - chai: 5.3.3 - chai-as-promised: 7.1.2(chai@5.3.3) + chai: 6.2.0 + chai-as-promised: 7.1.2(chai@6.2.0) deep-eql: 4.1.4 ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) hardhat: 3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -12425,9 +12456,9 @@ snapshots: hardhat: 3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) viem: 2.30.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@nomicfoundation/hardhat-toolbox@5.0.0(a2c664a2c86c3ae5dd9cd312b280c590)': + '@nomicfoundation/hardhat-toolbox@5.0.0(9456a2560cd4b23b39399d823783d224)': dependencies: - '@nomicfoundation/hardhat-chai-matchers': 2.1.0(@nomicfoundation/hardhat-ethers@3.1.0(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(chai@5.3.3)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-chai-matchers': 2.1.0(@nomicfoundation/hardhat-ethers@3.1.0(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(chai@6.2.0)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@nomicfoundation/hardhat-ethers': 3.1.0(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@nomicfoundation/hardhat-ignition-ethers': 3.0.2(@nomicfoundation/hardhat-ethers@3.1.0(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-ignition@3.0.2(@nomicfoundation/hardhat-verify@3.0.1(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-verify@3.0.1(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@nomicfoundation/ignition-core@3.0.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@nomicfoundation/hardhat-verify': 3.0.1(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)) @@ -12436,7 +12467,7 @@ snapshots: '@types/chai': 4.3.20 '@types/mocha': 10.0.10 '@types/node': 22.7.5 - chai: 5.3.3 + chai: 6.2.0 ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) hardhat: 3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) hardhat-gas-reporter: 1.0.10(bufferutil@4.0.9)(hardhat@3.0.4(bufferutil@4.0.9)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) @@ -13536,6 +13567,10 @@ snapshots: '@types/chai@4.3.20': {} + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + '@types/concat-stream@1.6.1': dependencies: '@types/node': 22.7.5 @@ -13556,6 +13591,8 @@ snapshots: dependencies: '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} + '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.8 @@ -15075,9 +15112,9 @@ snapshots: ccount@2.0.1: {} - chai-as-promised@7.1.2(chai@5.3.3): + chai-as-promised@7.1.2(chai@6.2.0): dependencies: - chai: 5.3.3 + chai: 6.2.0 check-error: 1.0.3 chai-as-promised@8.0.2(chai@4.5.0): @@ -15108,6 +15145,8 @@ snapshots: loupe: 3.2.1 pathval: 2.0.1 + chai@6.2.0: {} + chainsaw@0.1.0: dependencies: traverse: 0.3.9 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 553cb8700f..4fca7486ee 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,6 +3,7 @@ packages: - docs - examples/CRISP - examples/CRISP/client + - examples/CRISP/sdk - packages/enclave-config - packages/enclave-react - packages/enclave-sdk