From f14d856bd8c8b7ee1ba63e660c09742e3901729d Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 13 Oct 2025 16:17:44 +0100 Subject: [PATCH 01/35] turn off error for fully public contracts --- src/transformers/visitors/ownership/errorChecksVisitor.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/transformers/visitors/ownership/errorChecksVisitor.ts b/src/transformers/visitors/ownership/errorChecksVisitor.ts index 3e5d7769..145b8628 100644 --- a/src/transformers/visitors/ownership/errorChecksVisitor.ts +++ b/src/transformers/visitors/ownership/errorChecksVisitor.ts @@ -220,13 +220,6 @@ export default { // bindings are contract scope level, so we track global states here const { scope } = path; - if (state.isContractPublic) { - throw new Error( - 'The contract is fully public and does not use secret variables, making it incompatible with the transpiler. ' + - 'Ensure your contract manipulates secret variables to generate a valid ZApp.', - ); - } - for (const [, binding] of Object.entries(scope.bindings)) { if (!(binding instanceof VariableBinding)) continue; binding.prelimTraversalErrorChecks(); From d4cac9baa94994b45527b401c2c2041eea4bd4e8 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 13 Oct 2025 16:23:01 +0100 Subject: [PATCH 02/35] formatting --- .../visitors/toContractVisitor.ts | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 129d63c5..552a2bfb 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -162,23 +162,30 @@ export default { }, exit(path: NodePath, state: any) { - const { node, parent, scope } = path; - const sourceUnitNodes = parent._newASTPointer[0].nodes; - const contractNodes = node._newASTPointer; - let parameterList: any = {}; - let functionName: string; - let returnParameterList: any = {}; - let returnfunctionName: string; - for ([functionName, parameterList] of Object.entries(state.circuitParams)) { - if(state.returnpara){ - for ([returnfunctionName, returnParameterList] of Object.entries(state.returnpara)){ - if(functionName === returnfunctionName ){ - parameterList = parameterList && returnParameterList ? {... parameterList, ... returnParameterList} : parameterList; - state.circuitParams[ functionName ] = parameterList; - } - } + const { node, parent, scope } = path; + const sourceUnitNodes = parent._newASTPointer[0].nodes; + const contractNodes = node._newASTPointer; + let parameterList: any = {}; + let functionName: string; + let returnParameterList: any = {}; + let returnfunctionName: string; + for ([functionName, parameterList] of Object.entries( + state.circuitParams, + )) { + if (state.returnpara) { + for ([returnfunctionName, returnParameterList] of Object.entries( + state.returnpara, + )) { + if (functionName === returnfunctionName) { + parameterList = + parameterList && returnParameterList + ? { ...parameterList, ...returnParameterList } + : parameterList; + state.circuitParams[functionName] = parameterList; } } + } + } const contractIndex = sourceUnitNodes.findIndex( From 081deeff9894406829cc48e8ac27154c33f95d47 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Fri, 28 Nov 2025 20:17:57 +0000 Subject: [PATCH 03/35] chore(setup): remove zk setup when contract is fully public --- src/boilerplate/common/bin/setup-public | 109 ++++++++++++++++++ .../javascript/nodes/boilerplate-generator.ts | 2 + .../orchestration/files/toOrchestration.ts | 12 +- .../visitors/toOrchestrationVisitor.ts | 5 + 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/boilerplate/common/bin/setup-public diff --git a/src/boilerplate/common/bin/setup-public b/src/boilerplate/common/bin/setup-public new file mode 100644 index 00000000..75bc5167 --- /dev/null +++ b/src/boilerplate/common/bin/setup-public @@ -0,0 +1,109 @@ +#!/bin/bash +set -e + +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' + +while getopts "n:a:m:k:r:s:" arg; do + case $arg in + n) + network=$OPTARG + echo networkvalue $OPTARG + ;; + a) + account=$OPTARG + echo accountvalue $OPTARG + ;; + m) + mnemonic=$OPTARG + echo mnemonicvalue $OPTARG + ;; + k) + key=$OPTARG + echo keyvalue $OPTARG + ;; + r) + rpc=$OPTARG + echo rpcvalue $OPTARG + ;; + s) + setup=$OPTARG + echo setup $OPTARG + ;; + esac +done + +cp docker-compose.zapp.override.default.yml docker-compose.zapp.override.yml + +cp deploy_default.sh deploy.sh + +cp config/default_standard.js config/default.js + +cp bin/default_startup bin/startup + + +rm -rf proving-files + +perl -i -pe "s,docker-compose.zapp.yml -f docker-compose.zapp.override.yml,docker-compose.zapp.yml,g" package.json + +if [[ $network == 'amoy' ]] || [[ $network == 'sepolia' ]] || [[ $network == 'goerli' ]] || [[ $network == 'cardona' ]]|| [[ $network == 'zkEVM' ]] || [[ $network == 'base-mainnet' ]] +then +perl -i -pe "s,DEFAULT_ACCOUNT: '',DEFAULT_ACCOUNT: \'$account\',g" docker-compose.zapp.override.yml +perl -i -pe "s,DEFAULT_ACCOUNT_MNEMONIC: '',DEFAULT_ACCOUNT_MNEMONIC: \'$mnemonic\',g" docker-compose.zapp.override.yml +perl -i -pe "s,KEY: '',KEY: \'$key\',g" docker-compose.zapp.override.yml +perl -i -pe "s,docker-compose.zapp.yml up,docker-compose.zapp.yml -f docker-compose.zapp.override.yml up,g" bin/startup +perl -i -pe "s,docker-compose.zapp.yml,docker-compose.zapp.yml -f docker-compose.zapp.override.yml,g" package.json +perl -i -pe "s,docker compose -f docker-compose.zapp.yml -f docker-compose.zapp.override.yml up -d ganache, ## up ganache service for ganache,g" bin/startup +perl -i -pe "s,! nc -z localhost 8545,false,g" bin/startup +fi + +if [[ $network == 'amoy' ]] +then +perl -i -pe "if (!\$found1 && s/RPC_URL: ''/RPC_URL: 'wss:\/\/polygon-amoy.g.alchemy.com\/v2\/$rpc'/) { \$found1 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "if (!\$found2 && s/RPC_URL: ''/RPC_URL: 'https:\/\/polygon-amoy.g.alchemy.com\/v2\/$rpc'/) { \$found2 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "if (!\$found3 && s/RPC_URL: ''/RPC_URL: 'wss:\/\/polygon-amoy.g.alchemy.com\/v2\/$rpc'/) { \$found3 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "s,migrations/deploy.js,migrations/deploy.js --network amoy,g" deploy.sh +fi + + +if [[ $network == 'sepolia' ]] +then +perl -i -pe "s,RPC_URL: '',RPC_URL: \'wss://sepolia.infura.io/ws/v3//$rpc\',g" docker-compose.zapp.override.yml +perl -i -pe "s,migrations/deploy.js,migrations/deploy.js --network sepolia,g" deploy.sh +fi + +if [[ $network == 'goerli' ]] +then +perl -i -pe "s,RPC_URL: '',RPC_URL: \'wss://goerli.infura.io/ws/v3/$rpc\',g" docker-compose.zapp.override.yml +perl -i -pe "s,migrations/deploy.js,migrations/deploy.js --network goerli,g" deploy.sh +fi + +if [[ $network == 'cardona' ]] +then +perl -i -pe "if (!\$found1 && s/RPC_URL: ''/RPC_URL: 'wss:\/\/polygon-zkevm-cardona.blastapi.io\/$rpc'/) { \$found1 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "if (!\$found2 && s/RPC_URL: ''/RPC_URL: 'https:\/\/polygon-zkevm-cardona.blastapi.io\/$rpc'/) { \$found2 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "if (!\$found3 && s/RPC_URL: ''/RPC_URL: 'wss:\/\/polygon-zkevm-cardona.blastapi.io\/$rpc'/) { \$found3 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "s,migrations/deploy.js,migrations/deploy.js --network cardona,g" deploy.sh +perl -i -pe "s,defaultGasPrice: 30000000000,defaultGasPrice: 5,g" config/default.js +fi + +if [[ $network == 'zkEVM' ]] +then +perl -i -pe "if (!\$found1 && s/RPC_URL: ''/RPC_URL: 'wss:\/\/polygon-zkevm-mainnet.blastapi.io\/$rpc'/) { \$found1 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "if (!\$found2 && s/RPC_URL: ''/RPC_URL: 'https:\/\/polygon-zkevm-mainnet.blastapi.io\/$rpc'/) { \$found2 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "if (!\$found3 && s/RPC_URL: ''/RPC_URL: 'wss:\/\/polygon-zkevm-mainnet.blastapi.io\/$rpc'/) { \$found3 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "s,migrations/deploy.js,migrations/deploy.js --network zkEVM,g" deploy.sh +perl -i -pe "s,defaultGasPrice: 30000000000,defaultGasPrice: 5,g" config/default.js +fi + +if [[ $network == 'base-mainnet' ]] +then +perl -i -pe "if (!\$found1 && s/RPC_URL: ''/RPC_URL: 'wss:\/\/base-mainnet.g.alchemy.com\/v2\/$rpc'/) { \$found1 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "if (!\$found2 && s/RPC_URL: ''/RPC_URL: 'https:\/\/base-mainnet.g.alchemy.com\/v2\/$rpc'/) { \$found2 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "if (!\$found3 && s/RPC_URL: ''/RPC_URL: 'wss:\/\/base-mainnet.g.alchemy.com\/v2\/$rpc'/) { \$found3 = 1 }" docker-compose.zapp.override.yml +perl -i -pe "s,migrations/deploy.js,migrations/deploy.js --network base-mainnet,g" deploy.sh +perl -i -pe "s,defaultGasPrice: 30000000000,//defaultGasPrice: 30000000000,g" config/default.js +fi + +printf "\n${GREEN}*** Finished! ***${NC}\n" diff --git a/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts b/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts index 2b7796c4..069f3a82 100644 --- a/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts +++ b/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts @@ -366,6 +366,7 @@ export function buildBoilerplateNode(nodeType: string, fields: any = {}): any { constructorParams = [], contractImports = [], isConstructor = false, + fullyPublicContract = false, } = fields; return { nodeType, @@ -374,6 +375,7 @@ export function buildBoilerplateNode(nodeType: string, fields: any = {}): any { constructorParams, contractImports, isConstructor, + fullyPublicContract, }; } case 'EditableCommitmentCommonFilesBoilerplate': { diff --git a/src/codeGenerators/orchestration/files/toOrchestration.ts b/src/codeGenerators/orchestration/files/toOrchestration.ts index 96e7e6cd..4039cca8 100644 --- a/src/codeGenerators/orchestration/files/toOrchestration.ts +++ b/src/codeGenerators/orchestration/files/toOrchestration.ts @@ -1066,8 +1066,16 @@ export default function fileGenerator(node: any) { ].join('\n'), 'orchestration', ); - - let readPath = path.resolve(fileURLToPath(import.meta.url), '../../../../../src/boilerplate/common/bin/setup'); + const { fullyPublicContract } = node; + let readPath = fullyPublicContract + ? path.resolve( + fileURLToPath(import.meta.url), + '../../../../../src/boilerplate/common/bin/setup-public', + ) + : path.resolve( + fileURLToPath(import.meta.url), + '../../../../../src/boilerplate/common/bin/setup', + ); const setupScript = { filepath: 'bin/setup', file: fs.readFileSync(readPath, 'utf8') }; files.push(setupScript); readPath = path.resolve(fileURLToPath(import.meta.url), '../../../../../src/boilerplate/common/bin/startup'); diff --git a/src/transformers/visitors/toOrchestrationVisitor.ts b/src/transformers/visitors/toOrchestrationVisitor.ts index 6bd98710..8ef77719 100644 --- a/src/transformers/visitors/toOrchestrationVisitor.ts +++ b/src/transformers/visitors/toOrchestrationVisitor.ts @@ -372,6 +372,7 @@ const visitor = { const { node, parent, scope } = path; node._newASTPointer = parent._newASTPointer; const contractName = `${node.name}Shield`; + state.fullyPublicContract = true; if (scope.indicators.zkSnarkVerificationRequired) { const newNode = buildNode('File', { fileName: 'test', @@ -468,6 +469,9 @@ const visitor = { file.nodes[0].constructorParams = state.constructorParams; file.nodes[0].contractImports = state.contractImports; } + if (file.nodeType === 'SetupCommonFilesBoilerplate') { + file.fullyPublicContract = state.fullyPublicContract; + } } // Internal Call Visitor path.traverse(explode(internalCallVisitor), state); @@ -490,6 +494,7 @@ const visitor = { enter(path: NodePath, state: any) { const { node, parent, scope } = path; if (scope.modifiesSecretState()) { + state.fullyPublicContract = false; const contractName = `${parent.name}Shield`; const fnName = path.getUniqueFunctionName(); node.fileName = fnName; From 8f96adc4601689c1b0af8bc9319e682509a37572 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 2 Dec 2025 15:35:48 +0000 Subject: [PATCH 04/35] chore(contracts): fix contract deployment to support fully public contracts --- migrations/deploy.js | 62 ----------------- migrations/metadata.js | 66 ------------------- .../common/migrations/deploy-public.js | 39 +++++++++++ src/codeGenerators/common.ts | 20 +++++- .../orchestration/files/toOrchestration.ts | 5 +- 5 files changed, 59 insertions(+), 133 deletions(-) delete mode 100644 migrations/deploy.js delete mode 100644 migrations/metadata.js create mode 100644 src/boilerplate/common/migrations/deploy-public.js diff --git a/migrations/deploy.js b/migrations/deploy.js deleted file mode 100644 index d87be2fa..00000000 --- a/migrations/deploy.js +++ /dev/null @@ -1,62 +0,0 @@ - -const hre = require('hardhat') -const fs = require('fs'); -const c = require('config'); -CUSTOM_PROOF_IMPORT -const saveMetadata = require('./metadata').saveMetadata - -const functionNames = [FUNCTION_NAMES]; -const vkInput = []; -let vk = []; -functionNames.forEach((name) => { - const vkJson = JSON.parse( - fs.readFileSync(`/app/orchestration/common/db/${name}_vk.key`, "utf-8") - ); - if (vkJson.scheme) { - vk = Object.values(vkJson).slice(2).flat(Infinity); - } else { - vk = Object.values(vkJson).flat(Infinity); - } - vkInput.push(vk); -}); - - -async function main () { - try { - - const chainId = (await hre.ethers.provider.getNetwork()).chainId - const Verifier = await hre.ethers.getContractFactory('Verifier'); - const verifier = await Verifier.deploy(); - await verifier.waitForDeployment(); - const verifierAddress = await verifier.getAddress(); - let blockNumber = await hre.ethers.provider.getBlockNumber(); - let deployTx = await verifier.deploymentTransaction().wait() - console.log('Verifier deployed to:', contractAddress, 'tx hash:', deployTx.hash); - saveMetadata(verifierAddress, 'Verifier', chainId, blockNumber, deployTx.hash); - console.log('Verifier deployed to:', verifierAddress); - - CUSTOM_CONTRACTS - // Deploy AssignShield with Verifier and vkInput - const CONTRACT_NAME = await hre.ethers.getContractFactory('CONTRACT_NAME'); - const contractShield = await CONTRACT_NAME.deploy(verifierAddress, vkInput); - await contractShield.waitForDeployment(); - const contractAddress = await contractShield.getAddress(); - blockNumber = await hre.ethers.provider.getBlockNumber(); - console.log('CONTRACT_NAME deployed to:', contractAddress); - deployTx = await contractShield.deploymentTransaction().wait() - console.log('CONTRACT_NAME deployed to:', contractAddress, 'tx hash:', deployTx.hash); - saveMetadata(contractAddress, 'CONTRACT_NAME', chainId, blockNumber, deployTx.hash); - console.log("Deployment successful! Exiting..."); - - // Ensure all pending tasks are completed before exit - setTimeout(() => process.exit(0), 1000); -} catch (error) { - console.error('Deployment failed:', error); - process.exit(1); -} -} - -main().catch((error) => { - console.error(error) - process.exitCode = 1 -}) \ No newline at end of file diff --git a/migrations/metadata.js b/migrations/metadata.js deleted file mode 100644 index f168989b..00000000 --- a/migrations/metadata.js +++ /dev/null @@ -1,66 +0,0 @@ -const fs = require('fs') - -function saveMetadata ( - contractDeployedAddress, - contractName, - networkId, - blockNumber, - transactionHash -) { - - const projectFolder = process.env.PROJECT_PATH || '/app' - const buildFolder = projectFolder + '/build/contracts/' - - if (!fs.existsSync(buildFolder)) { - fs.mkdirSync(buildFolder, { recursive: true }); - } - - const deployedFileName = buildFolder + contractName + '.json' - - let deployedMetadata = { - contractName: '', - abi: {}, - networks: {} - } - const deployedPositionMetadata = { - address: '', - transactionHash: '', - blockNumber: 0 - } - - if (fs.existsSync(deployedFileName)) { - const oldDeployedMetadata = JSON.parse(fs.readFileSync(deployedFileName, 'utf-8')) - if (oldDeployedMetadata.contractName === contractName) { - deployedMetadata = oldDeployedMetadata - } - } - - - const hardhatArtifactContractPath = './artifacts/contracts' - // console.log("hardhatArtifactContractPath: ", hardhatArtifactContractPath); - const hardhatArtifactPath = - hardhatArtifactContractPath + - '/' + contractName + '.sol' + - '/' + - contractName + - '.json' - // console.log("hardhatArtifactPath: ", hardhatArtifactPath); - - const compilationData = fs.readFileSync(hardhatArtifactPath, 'utf-8') - const abi = JSON.parse(compilationData).abi - const contractNameFromHardhat = JSON.parse(compilationData).contractName - - deployedMetadata.abi = abi - deployedMetadata.contractName = contractNameFromHardhat - deployedPositionMetadata.address = contractDeployedAddress - deployedPositionMetadata.blockNumber = blockNumber - deployedPositionMetadata.transactionHash = transactionHash - deployedMetadata.networks[networkId] = deployedPositionMetadata - - console.log('Writing: ...') - fs.writeFileSync(deployedFileName, JSON.stringify(deployedMetadata, null, 2), "utf-8") -} - -module.exports = { - saveMetadata -} \ No newline at end of file diff --git a/src/boilerplate/common/migrations/deploy-public.js b/src/boilerplate/common/migrations/deploy-public.js new file mode 100644 index 00000000..84e011e5 --- /dev/null +++ b/src/boilerplate/common/migrations/deploy-public.js @@ -0,0 +1,39 @@ + +const hre = require('hardhat') +const fs = require('fs'); +const c = require('config'); +CUSTOM_CONTRACT_IMPORT +CUSTOM_PROOF_IMPORT +const saveMetadata = require('./metadata').saveMetadata + + +async function main () { + try { + + const chainId = (await hre.ethers.provider.getNetwork()).chainId; + + CUSTOM_CONTRACTS + // Deploy AssignShield + const CONTRACT_NAME = await hre.ethers.getContractFactory('CONTRACT_NAME'); + const contractShield = await CONTRACT_NAME.deploy(CUSTOM_INPUTS); + await contractShield.waitForDeployment(); + const contractAddress = await contractShield.getAddress(); + blockNumber = await hre.ethers.provider.getBlockNumber(); + console.log('CONTRACT_NAME deployed to:', contractAddress); + deployTx = await contractShield.deploymentTransaction().wait() + console.log('CONTRACT_NAME deployed to:', contractAddress, 'tx hash:', deployTx.hash); + saveMetadata(contractAddress, 'CONTRACT_NAME', "", chainId, blockNumber, deployTx.hash); + console.log("Deployment successful! Exiting..."); + + // Ensure all pending tasks are completed before exit + setTimeout(() => process.exit(0), 1000); +} catch (error) { + console.error('Deployment failed:', error); + process.exit(1); +} +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) \ No newline at end of file diff --git a/src/codeGenerators/common.ts b/src/codeGenerators/common.ts index 0bcfcafc..8d9cef5c 100644 --- a/src/codeGenerators/common.ts +++ b/src/codeGenerators/common.ts @@ -18,8 +18,9 @@ export interface localFile { export const collectImportFiles = ( file: string, context: string, + fullyPublicContract?: boolean, contextDirPath?: string, - fileName: string = '', + fileName = '', ) => { const lines = file.split('\n'); let ImportStatementList: string[]; @@ -99,7 +100,12 @@ export const collectImportFiles = ( const relPath = path.relative('.', absPath); const exists = fs.existsSync(relPath); if (!exists) continue; - const f = fs.readFileSync(relPath, 'utf8'); + let readPath = relPath; + // When the contract is fully public, we use deploy-public.js instead of deploy.js + if (fullyPublicContract && readPath.includes('deploy.js')) { + readPath = relPath.replace('deploy.js', 'deploy-public.js'); + } + const f = fs.readFileSync(readPath, 'utf8'); const n = path.basename(absPath, path.extname(absPath)); const shortRelPath = path.relative(path.resolve(fileURLToPath(import.meta.url), '../../../'), absPath); const writePath = context === 'orchestration' ? path.join( @@ -129,7 +135,15 @@ export const collectImportFiles = ( file: f, }); - localFiles = localFiles.concat(collectImportFiles(f, context, path.dirname(relPath), context === 'contract' ? n : '')); + localFiles = localFiles.concat( + collectImportFiles( + f, + context, + fullyPublicContract, + path.dirname(relPath), + context === 'contract' ? n : '', + ), + ); } // remove duplicate files after recursion: diff --git a/src/codeGenerators/orchestration/files/toOrchestration.ts b/src/codeGenerators/orchestration/files/toOrchestration.ts index 4039cca8..285f0ca2 100644 --- a/src/codeGenerators/orchestration/files/toOrchestration.ts +++ b/src/codeGenerators/orchestration/files/toOrchestration.ts @@ -400,7 +400,7 @@ const prepareMigrationsFile = (file: localFile, node: any) => { file.file = file.file.replace(/CONTRACT_NAME/g, node.contractName); file.file = file.file.replace( /FUNCTION_NAMES/g, - `'${node.functionNames.join(`', '`)}'`, + node.functionNames.length ? `'${node.functionNames.join(`', '`)}'` : ``, ); // collect any extra constructor parameters const constructorParamNames = node.constructorParams?.filter((obj: any) => !obj.isSecret).map((obj: any) => obj.name) || ``; @@ -1058,6 +1058,7 @@ export default function fileGenerator(node: any) { case 'SetupCommonFilesBoilerplate': { // complex setup files which require some setting up: + const { fullyPublicContract } = node; const files = collectImportFiles( [ `import './common/write-vk.mjs'`, @@ -1065,8 +1066,8 @@ export default function fileGenerator(node: any) { `import './common/migrations/deploy.js'`, ].join('\n'), 'orchestration', + fullyPublicContract, ); - const { fullyPublicContract } = node; let readPath = fullyPublicContract ? path.resolve( fileURLToPath(import.meta.url), From e54b3fd72be7c594c61a6437f2b6af1bf57b5175 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 2 Dec 2025 17:54:51 +0000 Subject: [PATCH 05/35] chore(orchestration): include common orchestration files for fully public contracts --- src/codeGenerators/orchestration/files/toOrchestration.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/codeGenerators/orchestration/files/toOrchestration.ts b/src/codeGenerators/orchestration/files/toOrchestration.ts index 285f0ca2..cc657961 100644 --- a/src/codeGenerators/orchestration/files/toOrchestration.ts +++ b/src/codeGenerators/orchestration/files/toOrchestration.ts @@ -1064,6 +1064,12 @@ export default function fileGenerator(node: any) { `import './common/write-vk.mjs'`, `import './common/zkp-setup.mjs'`, `import './common/migrations/deploy.js'`, + `import './common/web3.mjs'`, + `import './common/contract.mjs'`, + `import './common/timber.mjs'`, + `import './common/number-theory.mjs'`, + `import './common/mongo.mjs'`, + `import './common/hash-lookup.mjs'`, ].join('\n'), 'orchestration', fullyPublicContract, From 1983234d0afd552b66c4dd10b24e6e0b93133c43 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Thu, 4 Dec 2025 16:28:42 +0000 Subject: [PATCH 06/35] fix(solidity-types): allow IdentifierPath and InheritanceSpecifier solidity types to avoid zappify bug --- src/traverse/Binding.ts | 2 ++ src/traverse/Scope.ts | 3 ++- src/types/solidity-types.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/traverse/Binding.ts b/src/traverse/Binding.ts index 536c3c56..3e3f554c 100644 --- a/src/traverse/Binding.ts +++ b/src/traverse/Binding.ts @@ -63,6 +63,8 @@ export class Binding { case 'DoWhileStatement': case 'EnumDefinition': case 'NewExpression': + case 'InheritanceSpecifier': + case 'IdentifierPath': return false; default: logger.error(`Hitherto unknown nodeType '${nodeType}'`); diff --git a/src/traverse/Scope.ts b/src/traverse/Scope.ts index 385ba6bc..d5eaba89 100644 --- a/src/traverse/Scope.ts +++ b/src/traverse/Scope.ts @@ -237,7 +237,6 @@ export class Scope { referencedIndicator.update(path); } - if (!referencedNode.stateVariable && referencedNode.nodeType !== 'FunctionDefinition') { functionDefScope.indicators[referencedId].update(path); } @@ -290,6 +289,8 @@ export class Scope { case 'DoWhileStatement': case 'EnumDefinition': case 'NewExpression': + case 'InheritanceSpecifier': + case 'IdentifierPath': break; // And again, if we haven't recognized the nodeType then we'll throw an diff --git a/src/types/solidity-types.ts b/src/types/solidity-types.ts index 22d3951c..2ca45cfb 100644 --- a/src/types/solidity-types.ts +++ b/src/types/solidity-types.ts @@ -74,6 +74,7 @@ export function getVisitableKeys(nodeType: string): string[] { case 'MsgSender': case 'EnumDefinition': case 'NewExpression': + case 'IdentifierPath': return []; // And again, if we haven't recognized the nodeType then we'll throw an From 52b6634746c5e83e370cc68a1b39133f09c6331a Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 16:16:14 +0000 Subject: [PATCH 07/35] formatting --- .../javascript/raw/toOrchestration.ts | 4 +- .../contract/solidity/toContract.ts | 50 +++++++++---------- .../orchestration/files/toOrchestration.ts | 20 ++++---- src/transformers/visitors/toCircuitVisitor.ts | 2 - 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts index c4f927ab..b2a5ab09 100644 --- a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts +++ b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts @@ -481,7 +481,8 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { case 'FunctionDefinition': // the main function class - if (node.name !== 'cnstrctr') {functionSig.push( + if (node.name !== 'cnstrctr') { + functionSig.push( `export class ${(node.name).charAt(0).toUpperCase() + node.name.slice(1)}Manager { constructor(web3) { this.web3 = web3; @@ -1117,7 +1118,6 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { ], }; } - return { statements: [ `${returnsCallPublic} diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index dd3e810f..47e465ed 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -95,9 +95,8 @@ function codeGenerator(node: any) { const functionSignature = `${functionType} (${codeGenerator(node.parameters)}) ${node.visibility} ${node.stateMutability} ${returnType.length > 0 ? `returns (${returnType})`: ``}{`; let body = codeGenerator(node.body); let msgSigCheck = body.slice(body.indexOf('bytes4 sig'), body.indexOf('verify') ) - if(!node.msgSigRequired) - body = body.replace(msgSigCheck, ' '); - return ` + if (!node.msgSigRequired) body = body.replace(msgSigCheck, ' '); + return ` ${functionSignature} ${body} @@ -173,10 +172,9 @@ function codeGenerator(node: any) { } case 'Return': - return ` `; - case 'Break': + case 'Break': return `break;`; case 'Continue': @@ -230,37 +228,35 @@ function codeGenerator(node: any) { } } - - case 'IfStatement': - { - let trueStatements: any = ``; - let falseStatements: any= ``; - let initialStatements: any= codeGenerator(node.condition); - for (let i =0; i { // remove any duplicates from fnction parameters fnParam = [...new Set(fnParam)]; // Adding Return parameters - let returnParams: string[] = []; - let returnParamsName = fn.returnParameters.parameters - .filter((paramnode: any) => (paramnode.isSecret || paramnode.typeName.name === 'bool')) - .map(paramnode => (paramnode.name)) || []; // Adapt - if(returnParamsName.length > 0){ + const returnParams: string[] = []; + const returnParamsName = + fn.returnParameters.parameters + .filter( + (paramnode: any) => + paramnode.isSecret || paramnode.typeName.name === 'bool', + ) + .map(paramnode => paramnode.name) || []; // Adapt + if (returnParamsName?.length > 0) { returnParamsName.forEach(param => { - if(param !== 'true') - returnParams.push(param+'_newCommitmentValue'); - else - returnParams.push('bool'); + if (param !== 'true') returnParams.push(param + '_newCommitmentValue'); + else returnParams.push('bool'); }); } diff --git a/src/transformers/visitors/toCircuitVisitor.ts b/src/transformers/visitors/toCircuitVisitor.ts index b1cbfe47..667ce2c6 100644 --- a/src/transformers/visitors/toCircuitVisitor.ts +++ b/src/transformers/visitors/toCircuitVisitor.ts @@ -1492,10 +1492,8 @@ const visitor = { parent._newASTPointer[path.containerName] = newNode; return; } - if (path.isExternalFunctionCall() || path.isExportedSymbol()) { // External function calls are the fiddliest of things, because they must be retained in the Solidity contract, rather than brought into the circuit. With this in mind, it's easiest (from the pov of writing this transpiler) if External function calls appear at the very start or very end of a function. If they appear interspersed around the middle, we'd either need multiple circuits per Zolidity function, or we'd need a set of circuit parameters (non-secret params / return-params) per external function call, and both options are too painful for now. - // ignore external function calls; they'll be retained in Solidity, so won't be copied over to a circuit. state.skipSubNodes = true; } From 1078e1b68c0d0a7a9ca1025b3c20dacf48ac409e Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 16:24:08 +0000 Subject: [PATCH 08/35] fix(orchestration): skip blocks for fully public contracts - we don't need the logic in orchestration --- .../visitors/toOrchestrationVisitor.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/transformers/visitors/toOrchestrationVisitor.ts b/src/transformers/visitors/toOrchestrationVisitor.ts index 8ef77719..22b7806e 100644 --- a/src/transformers/visitors/toOrchestrationVisitor.ts +++ b/src/transformers/visitors/toOrchestrationVisitor.ts @@ -580,10 +580,9 @@ const visitor = { ); } } - } else { state.skipSubNodes = true; - } + } if (node.kind === 'constructor') { state.constructorParams ??= []; for (const param of node.parameters.parameters) { @@ -597,7 +596,6 @@ const visitor = { ); } } - }, exit(path: NodePath, state: any) { @@ -1338,9 +1336,13 @@ const visitor = { }, Block: { - enter(path: NodePath) { + enter(path: NodePath, state: any) { const { node, parent } = path; - + const fnDefPath = path.getAncestorOfType('FunctionDefinition'); + if (fnDefPath && !fnDefPath.scope.modifiesSecretState()) { + state.skipSubNodes = true; + return; + } // ts complains if I don't include a number in this list if (['trueBody', 'falseBody', 99999999].includes(path.containerName)) { node._newASTPointer = parent._newASTPointer[path.containerName]; @@ -1506,7 +1508,7 @@ const visitor = { if (node.expression.nodeType === 'Assignment' || node.expression.nodeType === 'UnaryOperation') { let { leftHandSide: lhs } = node.expression; if (!lhs) lhs = node.expression.subExpression; - indicator = scope.getReferencedIndicator(lhs, true); + indicator = scope.getReferencedIndicator(lhs, true); let name = indicator.isMapping ? indicator.name From ec9b6f82aecf9a318775324e49088387db1e04b2 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 16:23:22 +0000 Subject: [PATCH 09/35] chore(contracts) support new expressions --- .../contract/solidity/toContract.ts | 6 ++++ .../visitors/toContractVisitor.ts | 35 ++++++++++++------- src/types/solidity-types.ts | 13 +++++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index 47e465ed..299a0314 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -295,6 +295,12 @@ function codeGenerator(node: any) { return `${expression}.${node.memberName}`; } + case 'NewExpression': { + return `new ${node.typeName}(${node.arguments + .map(codeGenerator) + .join(', ')})`; + } + case 'IndexAccess': { const baseExpression = codeGenerator(node.baseExpression); const indexExpression = codeGenerator(node.indexExpression); diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 552a2bfb..a1b57387 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -882,11 +882,11 @@ DoWhileStatement: { // Like External function calls ,it's easiest (from the pov of writing this transpiler) if Event calls appear at the very start or very end of a function. // TODO: need a warning message to this effect ^^^ if (parent.nodeType === 'EmitStatement') { - newNode = buildNode('FunctionCall'); - node._newASTPointer = newNode; - parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); - return; - } + newNode = buildNode('FunctionCall'); + node._newASTPointer = newNode; + parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); + return; + } if (path.isExternalFunctionCall()) { // External function calls are the fiddliest of things, because they must be retained in the Solidity contract, rather than brought into the circuit. With this in mind, it's easiest (from the pov of writing this transpiler) if External function calls appear at the very start or very end of a function. If they appear interspersed around the middle, we'd either need multiple circuits per Zolidity function, or we'd need a set of circuit parameters (non-secret params / return-params) per external function call, and both options are too painful for now. @@ -964,18 +964,29 @@ DoWhileStatement: { node._newASTPointer = newNode; parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); } - if (node.kind !== 'typeConversion') { - newNode = buildNode('FunctionCall'); + if (node.kind === 'typeConversion') { + newNode = buildNode('TypeConversion', { + type: node.typeDescriptions.typeString, + }); + node._newASTPointer = newNode; + parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); + return; + } + if (node.expression.nodeType === 'NewExpression') { + newNode = buildNode('NewExpression', { + typeName: node.expression.typeName.name, + arguments: node.arguments, + argumentTypes: node.expression.argumentTypes, + }); node._newASTPointer = newNode; parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); state.skipSubNodes = true; return; } - newNode = buildNode('TypeConversion', { - type: node.typeDescriptions.typeString, - }); - node._newASTPointer = newNode; - parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); + newNode = buildNode('FunctionCall'); + node._newASTPointer = newNode; + parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); + state.skipSubNodes = true; }, }, } diff --git a/src/types/solidity-types.ts b/src/types/solidity-types.ts index 2ca45cfb..aa738b14 100644 --- a/src/types/solidity-types.ts +++ b/src/types/solidity-types.ts @@ -302,6 +302,19 @@ export function buildNode(nodeType: string, fields: any = {}): any { }), }); } + case 'NewExpression': { + const { + typeName = {}, + arguments: args = {}, + argumentTypes = {}, + } = fields; + return { + nodeType, + typeName, + arguments: args, + argumentTypes, + }; + } case 'VariableDeclarationStatement': { const { declarations = [], initialValue = {} } = fields; return { From 74c25060f72176cfeacb7ca2f68c8000f066bd1e Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Fri, 12 Dec 2025 15:49:58 +0000 Subject: [PATCH 10/35] fix(contracts): semi colons for unary statements --- src/codeGenerators/contract/solidity/toContract.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index 299a0314..9e95cc15 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -165,7 +165,7 @@ function codeGenerator(node: any) { let expr = codeGenerator(node.expression); if (typeof expr === 'undefined' || expr === null) return ''; let semicolon = ''; - if (node.expression.nodeType === 'FunctionCall') { + if (node.expression.nodeType === 'FunctionCall' || node.expression.nodeType === 'UnaryOperation') { semicolon = ';'; } return `${expr}${semicolon}`; @@ -203,11 +203,11 @@ function codeGenerator(node: any) { return `${codeGenerator(node.expression)}(${codeGenerator(node.arguments)})`; case 'UnaryOperation': - if (node.operator === '!'){ + if (node.operator === '!') { return `${node.operator}${codeGenerator(node.subExpression)}`; } - if (node.prefix === true) return `${node.operator}${codeGenerator(node.subExpression)};`; - return `${codeGenerator(node.subExpression)} ${node.operator};`; + if (node.prefix === true) return `${node.operator}${codeGenerator(node.subExpression)}`; + return `${codeGenerator(node.subExpression)}${node.operator}`; case 'EmitStatement': return `\t \t \t \temit ${codeGenerator(node.eventCall)};`; From 0bc7952c01e989647555687522f2ed65eaa4d0e1 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Sun, 14 Dec 2025 18:52:25 +0000 Subject: [PATCH 11/35] fix(contracts): mappings in returns in contracts --- .../visitors/toContractVisitor.ts | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index a1b57387..2fd1a2ee 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -378,42 +378,43 @@ export default { ParameterList: { enter(path: NodePath, state: any) { const { node, parent, scope } = path; - let returnName : string[] =[]; - if(path.key === 'parameters'){ - const newNode = buildNode('ParameterList'); - node._newASTPointer = newNode.parameters; - parent._newASTPointer[path.containerName] = newNode; - } else if(path.key === 'returnParameters'){ - parent.body.statements.forEach(node => { - if(node.nodeType === 'Return'){ - if(node.expression.nodeType === 'TupleExpression'){ - node.expression.components.forEach(component => { - if(component.name){ - returnName?.push(component.name); + const returnName: string[] = []; + if (path.key === 'parameters') { + const newNode = buildNode('ParameterList'); + node._newASTPointer = newNode.parameters; + parent._newASTPointer[path.containerName] = newNode; + } else if (path.key === 'returnParameters') { + parent.body.statements.forEach(node => { + if (node.nodeType === 'Return') { + if (node.expression.nodeType === 'TupleExpression') { + node.expression.components.forEach(component => { + if (component.name) { + returnName?.push(component.name); + } else returnName?.push(component.value); + }); + } else if (node.expression.nodeType === 'IndexAccess') { + returnName?.push( + `${node.expression.baseExpression.name}[${node.expression.indexExpression.name}]`, + ); + } else if (node.expression.name) { + returnName?.push(node.expression.name); + } else { + returnName?.push(node.expression.value); } - else - returnName?.push(component.value); - }); - } else{ - if(node.expression.name) - returnName?.push(node.expression.name); - else - returnName?.push(node.expression.value); - } - } - }); + } + }); + node.parameters.forEach((node, index) => { + if (node.nodeType === 'VariableDeclaration') { + node.name = returnName[index]; + } + }); - node.parameters.forEach((node, index) => { - if(node.nodeType === 'VariableDeclaration'){ - node.name = returnName[index]; + const newNode = buildNode('ParameterList'); + node._newASTPointer = newNode.parameters; + parent._newASTPointer[path.containerName] = newNode; } - }); + }, - const newNode = buildNode('ParameterList'); - node._newASTPointer = newNode.parameters; - parent._newASTPointer[path.containerName] = newNode; - } - }, exit(path: NodePath, state: any){ const { node, parent, scope } = path; if(path.key === 'returnParameters'){ From 35ecf25ef5b46729466bd8143f4624149489fa9c Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Sun, 14 Dec 2025 19:20:08 +0000 Subject: [PATCH 12/35] fix(contracts): semicolons after internal function calls --- .../contract/solidity/toContract.ts | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index 9e95cc15..21d868b1 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -148,7 +148,7 @@ function codeGenerator(node: any) { if(node.initialValue.nodeType === 'InternalFunctionCall'){ if(node.interactsWithSecret) return ; return ` - ${declarations} = ${initialValue.replace(/\s+/g,' ').trim()}`; + ${declarations} = ${initialValue.replace(/\s+/g,' ').trim()};`; } return ` ${declarations} = ${initialValue};`; @@ -165,7 +165,11 @@ function codeGenerator(node: any) { let expr = codeGenerator(node.expression); if (typeof expr === 'undefined' || expr === null) return ''; let semicolon = ''; - if (node.expression.nodeType === 'FunctionCall' || node.expression.nodeType === 'UnaryOperation') { + if ( + node.expression.nodeType === 'FunctionCall' || + node.expression.nodeType === 'UnaryOperation' || + node.expression.nodeType === 'InternalFunctionCall' + ) { semicolon = ';'; } return `${expr}${semicolon}`; @@ -217,15 +221,16 @@ function codeGenerator(node: any) { const args = node.arguments.map(codeGenerator); return `${expression}(${args.join(', ')})`; } - case 'InternalFunctionCall' :{ - if(node.parameters ){ - if(node.internalFunctionInteractsWithSecret) - return `\t \t \t \t ${node.name} (${node.parameters});` - return `\t \t \t \t ${node.name} (${node.parameters.map(codeGenerator)});` - } else { - const args = node.arguments.map(codeGenerator); - return `\t \t \t \t${node.name} (${args.join(', ')});` + case 'InternalFunctionCall': { + if (node.parameters) { + if (node.internalFunctionInteractsWithSecret) + return `\t \t \t \t ${node.name} (${node.parameters})`; + return `\t \t \t \t ${node.name} (${node.parameters.map( + codeGenerator, + )})`; } + const args = node.arguments.map(codeGenerator); + return `\t \t \t \t${node.name} (${args.join(', ')})`; } case 'IfStatement': { From 72e333fed512fcd2842a876b39012d5b92021fb4 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 11:33:32 +0000 Subject: [PATCH 13/35] chore(contracts): support conditionals --- src/codeGenerators/contract/solidity/toContract.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index 21d868b1..2938d0bc 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -181,6 +181,15 @@ function codeGenerator(node: any) { case 'Break': return `break;`; + case 'Conditional': { + const condition = codeGenerator(node.condition); + const trueBody = codeGenerator(node.trueExpression); + const falseBody = codeGenerator(node.falseExpression); + return `${condition} ? + ${trueBody} + : ${falseBody}`; + } + case 'Continue': return 'continue;'; From fc0b11b99790124e453913a48bcd2a3e731af343 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 11:45:06 +0000 Subject: [PATCH 14/35] chore(contracts): support member access --- src/transformers/visitors/toContractVisitor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 2fd1a2ee..fb66a78f 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -861,7 +861,7 @@ DoWhileStatement: { const { node, parent, scope } = path; let newNode: any; // If this node is a require statement, it might include arguments which themselves are expressions which need to be traversed. So rather than build a corresponding 'assert' node upon entry, we'll first traverse into the arguments, build their nodes, and then upon _exit_ build the assert node. - if (path.isRequireStatement() || path.isRevertStatement() || (node.expression.memberName && node.expression.memberName === 'push')) { + if (path.isRequireStatement() || path.isRevertStatement() || (node.expression.memberName && node.expression.memberName === 'push') || node.expression.nodeType === 'MemberAccess') { // If the 'require' statement contains secret state variables, we'll presume the circuit will perform that logic, so we'll do nothing in the contract. const subState = { interactsWithSecret: false }; path.traversePathsFast(interactsWithSecretVisitor, subState); From 74ab4f7c06837a72ba33515f4b969f1748e0cc20 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 13:19:12 +0000 Subject: [PATCH 15/35] fix(contracts): empty strings returned in contracts --- src/codeGenerators/contract/solidity/toContract.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index 2938d0bc..b65fd837 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -89,7 +89,8 @@ function codeGenerator(node: any) { // We check that params.name is defined because otherwise this is a commitment if(!params.isSecret && params.name != undefined && params.typeDescriptions.typeString != 'bool') { returnType.push(params.typeDescriptions.typeString); - returnParams.push(params.name); + if (params.name === "") returnParams.push("\"\""); + else returnParams.push(params.name); } }) const functionSignature = `${functionType} (${codeGenerator(node.parameters)}) ${node.visibility} ${node.stateMutability} ${returnType.length > 0 ? `returns (${returnType})`: ``}{`; From c20a143b3fd542e626574c1f0cea28aff2b5fca1 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 18:11:12 +0000 Subject: [PATCH 16/35] fix(contracts): public booleans should be returned --- src/codeGenerators/contract/solidity/toContract.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index b65fd837..f3dbc4ce 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -83,16 +83,15 @@ function codeGenerator(node: any) { break; } - // add any public return here, node.returnParameters.parameters.forEach(params => { // We check that params.name is defined because otherwise this is a commitment - if(!params.isSecret && params.name != undefined && params.typeDescriptions.typeString != 'bool') { + if(!params.isSecret && params.name != undefined) { returnType.push(params.typeDescriptions.typeString); if (params.name === "") returnParams.push("\"\""); else returnParams.push(params.name); } - }) + }); const functionSignature = `${functionType} (${codeGenerator(node.parameters)}) ${node.visibility} ${node.stateMutability} ${returnType.length > 0 ? `returns (${returnType})`: ``}{`; let body = codeGenerator(node.body); let msgSigCheck = body.slice(body.indexOf('bytes4 sig'), body.indexOf('verify') ) From b1f8eb4abdaf38610da70129458c2f9fb7d9b227 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 18:11:43 +0000 Subject: [PATCH 17/35] fix(contracts): returns for public nested mappings --- src/transformers/visitors/toContractVisitor.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index fb66a78f..088bc895 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -393,9 +393,15 @@ export default { } else returnName?.push(component.value); }); } else if (node.expression.nodeType === 'IndexAccess') { - returnName?.push( - `${node.expression.baseExpression.name}[${node.expression.indexExpression.name}]`, - ); + if (node.expression.baseExpression.nodeType === 'IndexAccess') { + returnName?.push( + `${node.expression.baseExpression.baseExpression.name}[${node.expression.baseExpression.indexExpression.name}][${node.expression.indexExpression.name}]`, + ); + } else { + returnName?.push( + `${node.expression.baseExpression.name}[${node.expression.indexExpression.name}]`, + ); + } } else if (node.expression.name) { returnName?.push(node.expression.name); } else { From 126ca71a5bacc10fce64ed8f703599b94de0e6ce Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 15:03:58 +0000 Subject: [PATCH 18/35] fix(contracts): fix tuple expressions --- src/transformers/visitors/toContractVisitor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 088bc895..9f1d527d 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -570,7 +570,7 @@ export default { enter(path: NodePath) { const { node, parent } = path; const newNode = buildNode(node.nodeType); - node._newASTPointer = newNode.components; + node._newASTPointer = newNode; parent._newASTPointer[path.containerName] = newNode; }, }, From a3bf9c42f871ea564a5f15c910eb5859c8bce43e Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 16:04:01 +0000 Subject: [PATCH 19/35] fix(contracts): address() undefined in contracts --- src/codeGenerators/contract/solidity/toContract.ts | 5 ++++- src/transformers/visitors/toContractVisitor.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index f3dbc4ce..1ec5d3cb 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -296,7 +296,10 @@ function codeGenerator(node: any) { return codeGenerator(node.typeName); case 'ElementaryTypeName': - return node.typeDescriptions.typeString; + if (node.typeDescriptions.typeString) { + return node.typeDescriptions.typeString; + } + return node.name; case 'MsgSender': return 'msg.sender'; diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 9f1d527d..9a67e4a2 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -964,6 +964,7 @@ DoWhileStatement: { }); node._newASTPointer = newNode; parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); + state.skipSubNodes = true; return; } From fc347aa11808feab11e7a3ed83b724c99bee49d4 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 16:07:03 +0000 Subject: [PATCH 20/35] fix(contracts): space after delete operator --- src/codeGenerators/contract/solidity/toContract.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/codeGenerators/contract/solidity/toContract.ts b/src/codeGenerators/contract/solidity/toContract.ts index 1ec5d3cb..f52cd827 100644 --- a/src/codeGenerators/contract/solidity/toContract.ts +++ b/src/codeGenerators/contract/solidity/toContract.ts @@ -219,7 +219,12 @@ function codeGenerator(node: any) { if (node.operator === '!') { return `${node.operator}${codeGenerator(node.subExpression)}`; } - if (node.prefix === true) return `${node.operator}${codeGenerator(node.subExpression)}`; + if (node.prefix === true) { + if (node.operator === 'delete') { + return `${node.operator} ${codeGenerator(node.subExpression)}`; + } + return `${node.operator}${codeGenerator(node.subExpression)}`; + } return `${codeGenerator(node.subExpression)}${node.operator}`; case 'EmitStatement': From 89e7dc9d2ec2b7d0bdfb8037c428dbf534de37bf Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 10:10:06 +0000 Subject: [PATCH 21/35] fix(contracts): replace ERC with ERCShield in contract --- src/transformers/visitors/toContractVisitor.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 9a67e4a2..2bb6ea1c 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -795,8 +795,16 @@ DoWhileStatement: { enter(path: NodePath) { const { node, parent } = path; const { name } = node; - - const newNode = buildNode('Identifier', { name }); + const contractName = + path.getAncestorOfType('ContractDefinition')?.node.name; + let newNode; + if ( + node.typeDescriptions.typeString === `type(contract ${contractName})` + ) { + newNode = buildNode('Identifier', { name: `${name}Shield` }); + } else { + newNode = buildNode('Identifier', { name }); + } // node._newASTPointer = // no pointer needed, because this is a leaf, so we won't be recursing any further. parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); From bced425366a023c01f692e63fdb83181cd81b584 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 11:40:11 +0000 Subject: [PATCH 22/35] chore(contracts): copy .zol contract into shield contract for fully public contracts --- src/index.ts | 6 +++-- src/transformers/ownership.ts | 13 ++++++++--- src/transformers/toContract.ts | 23 ++++++++++++++++++- .../visitors/ownership/errorChecksVisitor.ts | 2 ++ 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 48854e39..edb36ee3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,13 +23,15 @@ const zappify = (options: any) => { let path = checks(zolAST); - path = ownership(path, options); + const result = ownership(path, options); + path = result.updatedASTPath; + const { isContractPublic, contractName } = result; options.circuitAST = toCircuit(zolAST, options); toOrchestration(path, options); - toContract(zolAST, options); + toContract(zolAST, options, isContractPublic, contractName); return zolAST; }; diff --git a/src/transformers/ownership.ts b/src/transformers/ownership.ts index 110c4de8..18affeae 100644 --- a/src/transformers/ownership.ts +++ b/src/transformers/ownership.ts @@ -13,6 +13,8 @@ function transformation1(ast: any, options?: any) { stopTraversal: false, skipSubNodes: false, options, + isContractPublic: undefined, + contractName: undefined, }; // We'll start by calling the traverser function with our ast and a visitor. @@ -20,16 +22,21 @@ function transformation1(ast: any, options?: any) { ast.traverse(explode(ownershipVisitor), state); logger.verbose('Performing final error checks on the zol AST...'); ast.traverse(explode(errorChecksVisitor), state); + const { isContractPublic, contractName } = state; // At the end of our transformer function we'll return the new ast that we // just created. - return ast; + return { ast, isContractPublic, contractName }; } // A transformer function which will accept an ast. export default function ownership(astPath: any, options?: any) { logger.verbose('Performing ownership checks on the zol AST...'); - const updatedASTPath = transformation1(astPath, options); + const { + ast: updatedASTPath, + isContractPublic, + contractName, + } = transformation1(astPath, options); logger.verbose('Owners assigned.'); - return updatedASTPath; + return { updatedASTPath, isContractPublic, contractName }; } diff --git a/src/transformers/toContract.ts b/src/transformers/toContract.ts index f73c4bbe..e3337650 100644 --- a/src/transformers/toContract.ts +++ b/src/transformers/toContract.ts @@ -8,7 +8,28 @@ import codeGenerator from '../codeGenerators/contract/solidity/toContract.js'; import { transformation1 } from './visitors/common.js'; // A transformer function which will accept an ast. -export default function toContract(ast: object, options: any) { +export default function toContract( + ast: object, + options: any, + isContractPublic: boolean, + contractName: string, +) { + if (isContractPublic) { + // If the contract is fully public, we copy the original contract into the shield contract + const shieldFileName = `${contractName}Shield.sol`; + const shieldContractFilePath = `${options.contractsDirPath}/${shieldFileName}`; + fs.copyFileSync(options.inputFilePath, shieldContractFilePath); + + // Replace all instances of the contract name with contractNameShield + let fileContent = fs.readFileSync(shieldContractFilePath, 'utf8'); + // Use word boundaries to replace the contract name everywhere it appears + const contractNameRegex = new RegExp(`\\b${contractName}\\b`, 'g'); + fileContent = fileContent.replace(contractNameRegex, `${contractName}Shield`); + fs.writeFileSync(shieldContractFilePath, fileContent); + + return; + } + // transpile to a contract AST: const state = { stopTraversal: false, diff --git a/src/transformers/visitors/ownership/errorChecksVisitor.ts b/src/transformers/visitors/ownership/errorChecksVisitor.ts index 145b8628..ea81ca8a 100644 --- a/src/transformers/visitors/ownership/errorChecksVisitor.ts +++ b/src/transformers/visitors/ownership/errorChecksVisitor.ts @@ -220,6 +220,8 @@ export default { // bindings are contract scope level, so we track global states here const { scope } = path; + state.contractName = path.node.name; + for (const [, binding] of Object.entries(scope.bindings)) { if (!(binding instanceof VariableBinding)) continue; binding.prelimTraversalErrorChecks(); From 2626d05f7479987700c9c63fc08277615ef29374 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 13:59:48 +0000 Subject: [PATCH 23/35] fix(unsupportedChecks): remove error message for unsupported while loops - these can be supported if fully public and we already have an error for the secret case --- src/transformers/visitors/checks/unsupportedVisitor.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/transformers/visitors/checks/unsupportedVisitor.ts b/src/transformers/visitors/checks/unsupportedVisitor.ts index da1897b2..5273e816 100644 --- a/src/transformers/visitors/checks/unsupportedVisitor.ts +++ b/src/transformers/visitors/checks/unsupportedVisitor.ts @@ -59,15 +59,6 @@ export default { }, }, - WhileStatement: { - enter(node: any) { - throw new ZKPError( - 'While statements are unsupported in zero-knowledge proof circuits because they cannot handle dynamic loops.', - node, - ); - }, - }, - IfStatement: { enter(node: any) { if (['Identifier', 'Literal'].includes(node.condition.nodeType)) From 0095def6728d4b8e08eb185a7a62c5d649b5fd79 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 14:21:09 +0000 Subject: [PATCH 24/35] fix(scope): zappify error for fully public contract --- src/traverse/Scope.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/traverse/Scope.ts b/src/traverse/Scope.ts index d5eaba89..90022b25 100644 --- a/src/traverse/Scope.ts +++ b/src/traverse/Scope.ts @@ -237,7 +237,11 @@ export class Scope { referencedIndicator.update(path); } - if (!referencedNode.stateVariable && referencedNode.nodeType !== 'FunctionDefinition') { + if ( + !referencedNode.stateVariable && + referencedNode.nodeType !== 'FunctionDefinition' && + functionDefScope.indicators[referencedId] + ) { functionDefScope.indicators[referencedId].update(path); } From bcf73526f3aa0498db1088c56e302f71dea56253 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 15:08:18 +0000 Subject: [PATCH 25/35] fix(traverse): zappify error due to no declaration for abi when checking if there is an external contract call --- src/traverse/NodePath.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/traverse/NodePath.ts b/src/traverse/NodePath.ts index 7471e665..c549bd79 100644 --- a/src/traverse/NodePath.ts +++ b/src/traverse/NodePath.ts @@ -655,7 +655,8 @@ export default class NodePath { isExternalContractInstance(node: any = this.node): boolean { const varDecNode = this.getReferencedNode(node); - return this.isExternalContractInstanceDeclaration(varDecNode); + if (varDecNode) return this.isExternalContractInstanceDeclaration(varDecNode); + return this.isExternalContractInstanceDeclaration(); } isExternalFunctionCall(): boolean { From 30c8b97a6883cd39d5c7ed0c2221fae1bb1d6848 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 16:02:15 +0000 Subject: [PATCH 26/35] fix(orchestration): include function signatre when a return is a boolean --- .../orchestration/javascript/raw/toOrchestration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts index b2a5ab09..c5e47c18 100644 --- a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts +++ b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts @@ -582,10 +582,10 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { statements: lines, }; } - if(rtnparams.includes('bool: bool')) { + if (rtnparams.includes('bool: bool')) { return { signature: [ - ` + `${functionSig} \n async ${node.name}(${params} ${states}) {`, `\n const bool = true; \n return { ${txReturns} ${rtnparams}, ${publicReturns} }; \n} From 15d30acbca5b337f835c780f5e36705de6e9d17f Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 16:24:51 +0000 Subject: [PATCH 27/35] fix(contracts): include internal function calls in the contracts if they don't interact with secret --- src/transformers/visitors/checks/interactsWithSecretVisitor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transformers/visitors/checks/interactsWithSecretVisitor.ts b/src/transformers/visitors/checks/interactsWithSecretVisitor.ts index d223686b..8f12556a 100644 --- a/src/transformers/visitors/checks/interactsWithSecretVisitor.ts +++ b/src/transformers/visitors/checks/interactsWithSecretVisitor.ts @@ -63,7 +63,7 @@ export default { exit(path: NodePath) { const expressionPath = path.getAncestorOfType('ExpressionStatement') || path.parentPath; - if (path.isExternalFunctionCall()) { + if (path.isExternalFunctionCall() || path.isInternalFunctionCall()) { path.markContainsPublic(); expressionPath.traversePathsFast(markSubtreeInteractsWithPublic, { publicPath: path, From d6c8fecfb196feb86fecfb319c8ce20093dde6ec Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 16:56:22 +0000 Subject: [PATCH 28/35] fix(unsupportedchecks): turn off unsupported pure error for fully public contracts --- src/transformers/visitors/ownership/errorChecksVisitor.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/transformers/visitors/ownership/errorChecksVisitor.ts b/src/transformers/visitors/ownership/errorChecksVisitor.ts index ea81ca8a..00811c18 100644 --- a/src/transformers/visitors/ownership/errorChecksVisitor.ts +++ b/src/transformers/visitors/ownership/errorChecksVisitor.ts @@ -181,7 +181,7 @@ export default { exit(path: NodePath, state: any) { const { scope } = path; if (path.node.stateMutability === 'pure'){ - throw new TODOError(`We currently do not support pure functions.`, path.node); + state.containsPureFunction = true; } if (path.node.stateMutability === 'view' && path.node.body.containsSecret){ throw new TODOError(`We currently do not support view functions that involve secret variables.`, path.node); @@ -220,6 +220,10 @@ export default { // bindings are contract scope level, so we track global states here const { scope } = path; + if (!state.isContractPublic && state.containsPureFunction) { + throw new TODOError(`We currently do not support pure functions.`, path.node); + } + state.contractName = path.node.name; for (const [, binding] of Object.entries(scope.bindings)) { From 0b0809cbcd5fb5b0222a1a3c15715f89ac690e96 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 17:10:43 +0000 Subject: [PATCH 29/35] fix(orchestration): exit orchestrationInternalFunctionCallVisitor if the function is public --- .../visitors/orchestrationInternalFunctionCallVisitor.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts b/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts index d8e61447..61365564 100644 --- a/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts +++ b/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts @@ -500,6 +500,14 @@ const internalCallVisitor = { }) }, }, +FunctionDefinition: { + enter(path: NodePath, state: any) { + const { node, parent, scope } = path; + if (!scope.modifiesSecretState()) { + state.skipSubNodes = true; + } + }, +}, FunctionCall: { enter(path: NodePath, state: any) { const { node, parent, scope } = path; From 3611b5b889d0119100dcbae3587453d5467ff9d7 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 17:27:35 +0000 Subject: [PATCH 30/35] fix(contracts): error generating contracts AST where the original condition, trueBody and falseBody of an if statement are included instead of the new contract specific ones --- src/transformers/visitors/toContractVisitor.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 2bb6ea1c..46fa9574 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -9,6 +9,7 @@ import { VariableBinding } from '../../traverse/Binding.js'; import { ContractDefinitionIndicator,FunctionDefinitionIndicator } from '../../traverse/Indicator.js'; import { interactsWithSecretVisitor, parentnewASTPointer, internalFunctionCallVisitor } from './common.js'; import { param } from 'express/lib/request.js'; +import { exit } from 'process'; // here we find any public state variables which interact with secret states // and hence need to be included in the verification calculation @@ -488,14 +489,19 @@ export default { state.skipSubNodes=true; return; } - const newNode = buildNode(node.nodeType, { - condition: node.condition, - trueBody: node.trueBody, - falseBody: node.falseBody - }); + const newNode = buildNode(node.nodeType, {}); node._newASTPointer = newNode; parentnewASTPointer(parent, path, newNode, parent._newASTPointer[path.containerName]); }, + + exit(path: NodePath) { + const { node, parent } = path; + node._newASTPointer.condition = node.condition._newASTPointer; + node._newASTPointer.trueBody = node.trueBody._newASTPointer; + node._newASTPointer.falseBody = node.falseBody + ? node.falseBody._newASTPointer + : {}; + }, }, ForStatement: { From c1136bca4366e35babe757a2c5f593531e58e17b Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 17:32:20 +0000 Subject: [PATCH 31/35] chore(contracts): support conditionals --- src/transformers/visitors/toContractVisitor.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index 46fa9574..da974f52 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -572,6 +572,15 @@ export default { }, }, + Conditional: { + enter(path: NodePath) { + const { node, parent } = path; + const newNode = buildNode('Conditional', {}); + node._newASTPointer = newNode; + parentnewASTPointer(parent, path, newNode , parent._newASTPointer[path.containerName]); + }, + }, + TupleExpression: { enter(path: NodePath) { const { node, parent } = path; From 7991ceb4edcd71516b149ec02dd53b2e8b29fb4b Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 13:51:21 +0000 Subject: [PATCH 32/35] fix(contracts): zappify errors during contract compilation for fully public contracts --- .../nodes/ContractBoilerplateGenerator.ts | 113 +++++++++--------- .../raw/ContractBoilerplateGenerator.ts | 52 ++++---- .../visitors/toContractVisitor.ts | 28 +++-- 3 files changed, 99 insertions(+), 94 deletions(-) diff --git a/src/boilerplate/contract/solidity/nodes/ContractBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/nodes/ContractBoilerplateGenerator.ts index 08a4be52..ab14f3d6 100644 --- a/src/boilerplate/contract/solidity/nodes/ContractBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/nodes/ContractBoilerplateGenerator.ts @@ -114,68 +114,69 @@ class ContractBoilerplateGenerator { let paramtype: string; let params : any[]; let functionName: string; - - for ([functionName, parameterList] of Object.entries(circuitParams)) { - for ([paramtype, params] of Object.entries(parameterList)){ - const returnpara = {}; - if(paramtype === 'returnParameters'){ - returnpara[ paramtype ] = params; - delete parameterList[ paramtype ]; - } - const newList: string[] = []; - - params?.forEach(circuitParamNode => { - switch (circuitParamNode.bpType) { - case 'nullification': - if (circuitParamNode.isNullified) { - newList.push('nullifier'); - } else { - // we use a nullification node for accessed, not nullified, states - newList.push('checkNullifier') - } - break; - case 'newCommitment': - newList.push(circuitParamNode.bpType); - break; - case 'oldCommitmentExistence': - if (!newList.includes(circuitParamNode.bpType)) newList.push(circuitParamNode.bpType); - break; - case 'encryption': - - returnpara['encryptionParameters'] ??= []; - returnpara['encryptionParameters'].push(circuitParamNode.bpType); - break; - case undefined: { - if ( - circuitParamNode.nodeType === 'VariableDeclaration' && - !circuitParamNode.isPrivate && - !newList.some(str => str === circuitParamNode.name) - ){ - if (circuitParamNode.typeName?.members) { - newList.push(...circuitParamNode.typeName.members.map(m => `${circuitParamNode.name}.${m.name}`)); + if (circuitParams) { + for ([functionName, parameterList] of Object.entries(circuitParams)) { + for ([paramtype, params] of Object.entries(parameterList)) { + const returnpara = {}; + if(paramtype === 'returnParameters'){ + returnpara[ paramtype ] = params; + delete parameterList[ paramtype ]; + } + const newList: string[] = []; + + params?.forEach(circuitParamNode => { + switch (circuitParamNode.bpType) { + case 'nullification': + if (circuitParamNode.isNullified) { + newList.push('nullifier'); + } else { + // we use a nullification node for accessed, not nullified, states + newList.push('checkNullifier') + } + break; + case 'newCommitment': + newList.push(circuitParamNode.bpType); break; - } else if (circuitParamNode.typeName?.name.includes(`[`)) { - // TODO arrays of structs/structs of arrays/more robust soln - const arrayLen = circuitParamNode.typeName?.name.match(/(?<=\[)(\d+)(?=\])/); - for (let index = 0; index < +arrayLen[0]; index++) { - newList.push(`${circuitParamNode.name}[${index}]`); + case 'oldCommitmentExistence': + if (!newList.includes(circuitParamNode.bpType)) newList.push(circuitParamNode.bpType); + break; + case 'encryption': + + returnpara['encryptionParameters'] ??= []; + returnpara['encryptionParameters'].push(circuitParamNode.bpType); + break; + case undefined: { + if ( + circuitParamNode.nodeType === 'VariableDeclaration' && + !circuitParamNode.isPrivate && + !newList.some(str => str === circuitParamNode.name) + ){ + if (circuitParamNode.typeName?.members) { + newList.push(...circuitParamNode.typeName.members.map(m => `${circuitParamNode.name}.${m.name}`)); + break; + } else if (circuitParamNode.typeName?.name.includes(`[`)) { + // TODO arrays of structs/structs of arrays/more robust soln + const arrayLen = circuitParamNode.typeName?.name.match(/(?<=\[)(\d+)(?=\])/); + for (let index = 0; index < +arrayLen[0]; index++) { + newList.push(`${circuitParamNode.name}[${index}]`); + } + break; + } else newList.push(circuitParamNode.name); } + } + break; + + default: break; - } else newList.push(circuitParamNode.name); } - } - break; - - default: - break; + }); + parameterList[ paramtype ] = newList; + parameterList = {...parameterList, ...returnpara}; } - }); - parameterList[ paramtype ] = newList; - parameterList = {...parameterList, ...returnpara}; + circuitParams[ functionName ] = parameterList; + } } - circuitParams[ functionName ] = parameterList; - - } + const constructorContainsSecret = Object.values(this.scope.bindings).some((binding: any) => binding.node.kind === 'constructor'); return { nullifiersRequired, oldCommitmentAccessRequired, newCommitmentsRequired, containsAccessedOnlyState, encryptionRequired, constructorContainsSecret, circuitParams, isjoinSplitCommitmentsFunction}; }, diff --git a/src/boilerplate/contract/solidity/raw/ContractBoilerplateGenerator.ts b/src/boilerplate/contract/solidity/raw/ContractBoilerplateGenerator.ts index 911c7dbf..dea9db85 100644 --- a/src/boilerplate/contract/solidity/raw/ContractBoilerplateGenerator.ts +++ b/src/boilerplate/contract/solidity/raw/ContractBoilerplateGenerator.ts @@ -226,32 +226,34 @@ class ContractBoilerplateGenerator { ]; const verifyInputs: string[] = []; const joinSplitCommitmentsInputs: string[] = []; - for (let [name, _params] of Object.entries(circuitParams)) { - if (_params) - for (let [type, _inputs] of Object.entries(_params)) { - const counter = { - customInputs: 0, - newNullifiers: 0, - checkNullifiers: 0, - newCommitments: 0, - encryption: 0, - }; - _inputs?.map(i => verifyInputsMap(type, i, counter)); - - - - } - - if(_params && !(Object.keys(_params).includes('returnParameters')) &&!(Object.keys(_params).includes('encryptionParameters'))) verifyInput.push(` - inputs[k++] = 1;`) - - verifyInputs.push(` - if (functionId == uint(FunctionNames.${name})) { - uint k = 0; - ${verifyInput.join('')} + if (circuitParams) { + for (let [name, _params] of Object.entries(circuitParams)) { + if (_params) + for (let [type, _inputs] of Object.entries(_params)) { + const counter = { + customInputs: 0, + newNullifiers: 0, + checkNullifiers: 0, + newCommitments: 0, + encryption: 0, + }; + _inputs?.map(i => verifyInputsMap(type, i, counter)); + + + + } - }`) - verifyInput =[]; + if(_params && !(Object.keys(_params).includes('returnParameters')) &&!(Object.keys(_params).includes('encryptionParameters'))) verifyInput.push(` + inputs[k++] = 1;`) + + verifyInputs.push(` + if (functionId == uint(FunctionNames.${name})) { + uint k = 0; + ${verifyInput.join('')} + + }`) + verifyInput =[]; + } } const verification: string[] = [ diff --git a/src/transformers/visitors/toContractVisitor.ts b/src/transformers/visitors/toContractVisitor.ts index da974f52..a3d895d1 100644 --- a/src/transformers/visitors/toContractVisitor.ts +++ b/src/transformers/visitors/toContractVisitor.ts @@ -170,19 +170,21 @@ export default { let functionName: string; let returnParameterList: any = {}; let returnfunctionName: string; - for ([functionName, parameterList] of Object.entries( - state.circuitParams, - )) { - if (state.returnpara) { - for ([returnfunctionName, returnParameterList] of Object.entries( - state.returnpara, - )) { - if (functionName === returnfunctionName) { - parameterList = - parameterList && returnParameterList - ? { ...parameterList, ...returnParameterList } - : parameterList; - state.circuitParams[functionName] = parameterList; + if (state.circuitParams) { + for ([functionName, parameterList] of Object.entries( + state.circuitParams, + )) { + if (state.returnpara) { + for ([returnfunctionName, returnParameterList] of Object.entries( + state.returnpara, + )) { + if (functionName === returnfunctionName) { + parameterList = + parameterList && returnParameterList + ? { ...parameterList, ...returnParameterList } + : parameterList; + state.circuitParams[functionName] = parameterList; + } } } } From ca6780a7b42a42bcb2158ca80a5791c7455d61b7 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 20:13:51 +0000 Subject: [PATCH 33/35] fix(orchestration): error due to constructorTx not being created as the db didn't exist --- .../javascript/raw/toOrchestration.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts index c5e47c18..a9bd42ea 100644 --- a/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts +++ b/src/boilerplate/orchestration/javascript/raw/toOrchestration.ts @@ -719,7 +719,10 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { })); } } - if (node.isConstructor) lines.push(`\nfs.writeFileSync("/app/orchestration/common/db/constructorTx.json", JSON.stringify(tx, null, 4));`) + if (node.isConstructor) + lines.push( + `\nfs.writeFileSync("/app/orchestration/common/db/constructorTx.json", JSON.stringify(tx, null, 4));`, + ); return { statements: [ `\n// Write new commitment preimage to db: \n`, @@ -1079,10 +1082,13 @@ export const OrchestrationCodeBoilerPlate: any = (node: any) => { statements: [ `\n\n// Save transaction for the constructor: \nconst tx = { ${lines}}; + \n if (!fs.existsSync("/app/orchestration/common/db")) { + \n fs.mkdirSync("/app/orchestration/common/db", { recursive: true }); + \n } \nfs.writeFileSync("/app/orchestration/common/db/constructorTx.json", JSON.stringify(tx, null, 4));` - ] - } - } + ], + }; + } if (node.publicInputs[0]) { node.publicInputs.forEach((input: any) => { From 7fc683fce33eccd81a1d458e3181afe978a53496 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Thu, 18 Dec 2025 13:29:20 +0000 Subject: [PATCH 34/35] lyd/fixingTestRunner --- apitest.js | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/apitest.js b/apitest.js index 54f39985..0a493f55 100644 --- a/apitest.js +++ b/apitest.js @@ -1,4 +1,3 @@ - import chai from 'chai'; import chaiHttp from 'chai-http'; import chaiAsPromised from 'chai-as-promised'; @@ -16,7 +15,6 @@ chai.use(chaiHttp); chai.use(chaiAsPromised); const testedZapps = []; -const zappLogs = new Map(); // Store logs for each zapp const displayedZapps = new Set(); // Track which ZApps have already had logs displayed const getUserFriendlyTestNames = async folderPath => { @@ -55,10 +53,9 @@ const callZAppAPIs = async (zappName, apiRequests, preHook, cnstrctrInputs) => { console.log(logResult.stderr); } // Store logs and mark as displayed to prevent duplicate display in afterEach - zappLogs.set(zappName, { - stdout: logResult.stdout, - stderr: logResult.stderr, - }); + const logFilePath = `./test-logs/${zappName}-failed.log`; + fs.mkdirSync('./test-logs', { recursive: true }); + fs.writeFileSync(logFilePath, `=== ${zappName} APIACTIONS FAILED ===\n${logResult.stdout}\n${logResult.stderr || ''}`); displayedZapps.add(zappName); } shell.cd('../..'); @@ -117,10 +114,9 @@ const callZAppAPIs = async (zappName, apiRequests, preHook, cnstrctrInputs) => { console.log(logResult.stderr); } // Store logs and mark as displayed to prevent duplicate display in afterEach - zappLogs.set(zappName, { - stdout: logResult.stdout, - stderr: logResult.stderr, - }); + const logFilePath = `../../test-logs/${zappName}-api-failed.log`; + fs.mkdirSync('../../test-logs', { recursive: true }); + fs.writeFileSync(logFilePath, `=== ${zappName} API FAILED ===\n${logResult.stdout}\n${logResult.stderr || ''}`); displayedZapps.add(zappName); } // Clean up and re-throw @@ -129,16 +125,15 @@ const callZAppAPIs = async (zappName, apiRequests, preHook, cnstrctrInputs) => { throw error; } shell.cd(`./temp-zapps/${zappName}`); - // Capture logs silently and store them + // Capture logs silently and write to file const logResult = shell.exec( 'docker compose -f docker-compose.zapp.yml logs', { silent: true }, ); if (logResult.code === 0) { - zappLogs.set(zappName, { - stdout: logResult.stdout, - stderr: logResult.stderr, - }); + const logFilePath = `../../test-logs/${zappName}.log`; + fs.mkdirSync('../../test-logs', { recursive: true }); + fs.writeFileSync(logFilePath, `=== ${zappName} logs ===\n${logResult.stdout}\n${logResult.stderr || ''}`); } if ( shell.exec('docker compose -f docker-compose.zapp.yml down -v').code !== 0 @@ -683,14 +678,26 @@ function displayLogsForZapp(zappName) { if (displayedZapps.has(zappName)) { return; // Skip - already displayed } - const logs = zappLogs.get(zappName); - if (logs) { + + // Try to read logs from file instead of memory + const logFilePath = `./test-logs/${zappName}.log`; + const failedLogPath = `./test-logs/${zappName}-failed.log`; + const apiFailedLogPath = `./test-logs/${zappName}-api-failed.log`; + + let logContent = null; + + // Check which log file exists + if (fs.existsSync(apiFailedLogPath)) { + logContent = fs.readFileSync(apiFailedLogPath, 'utf8'); + } else if (fs.existsSync(failedLogPath)) { + logContent = fs.readFileSync(failedLogPath, 'utf8'); + } else if (fs.existsSync(logFilePath)) { + logContent = fs.readFileSync(logFilePath, 'utf8'); + } + + if (logContent) { console.log(`\n=== Logs for ${zappName} ===`); - console.log(logs.stdout); - if (logs.stderr) { - console.log(`Stderr for ${zappName}:`); - console.log(logs.stderr); - } + console.log(logContent); // Mark this ZApp as having logs displayed displayedZapps.add(zappName); } else { From 983868b0a983dc30b283556cd6af1840e14bab5b Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Thu, 18 Dec 2025 17:22:10 +0000 Subject: [PATCH 35/35] try fix for test runner --- .github/workflows/main.yml | 12 ++++++++++++ apitest.js | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f8ff1a52..d0631abb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,6 +39,9 @@ jobs: test-zapp: runs-on: ubuntu-latest + + env: + NODE_OPTIONS: "--max-old-space-size=3072" steps: - uses: actions/checkout@v3 @@ -46,6 +49,15 @@ jobs: with: node-version: '16.17.0' + - name: Check system resources + run: | + echo "=== Memory Info ===" + free -h + echo "=== Disk Space ===" + df -h + echo "=== CPU Info ===" + nproc + - name: run zappify run: | npm ci && ./bin/start diff --git a/apitest.js b/apitest.js index 0a493f55..abf0b59e 100644 --- a/apitest.js +++ b/apitest.js @@ -121,6 +121,8 @@ const callZAppAPIs = async (zappName, apiRequests, preHook, cnstrctrInputs) => { } // Clean up and re-throw shell.exec('docker compose -f docker-compose.zapp.yml down -v'); + // Also clean node_modules on failure + shell.exec('rm -rf node_modules', { silent: true }); shell.cd('../..'); throw error; } @@ -141,7 +143,19 @@ const callZAppAPIs = async (zappName, apiRequests, preHook, cnstrctrInputs) => { shell.echo('docker stop failed'); shell.exit(1); } + + // Clean up node_modules to free memory for next ZApp + console.log(`Cleaning up node_modules for ${zappName}...`); + shell.exec('rm -rf node_modules', { silent: true }); + shell.cd('../..'); + + // Force garbage collection if available + if (global.gc) { + console.log('Running garbage collection...'); + global.gc(); + } + await new Promise(resolve => { setTimeout(resolve, 5000); });