From e6bd9558a269f2dcdff92e13d6625a611f8e93e9 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 13 Oct 2025 16:17:44 +0100 Subject: [PATCH 01/33] 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 c728b941bc89f7da7802cbae5ca3893323907afc Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 13 Oct 2025 16:23:01 +0100 Subject: [PATCH 02/33] 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 05fcbb2053de8a463c2904565f53c45493b4277f Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Fri, 28 Nov 2025 20:17:57 +0000 Subject: [PATCH 03/33] 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 43fd6aa8..0097a485 100644 --- a/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts +++ b/src/boilerplate/orchestration/javascript/nodes/boilerplate-generator.ts @@ -396,6 +396,7 @@ export function buildBoilerplateNode(nodeType: string, fields: any = {}): any { constructorParams = [], contractImports = [], isConstructor = false, + fullyPublicContract = false, } = fields; return { nodeType, @@ -404,6 +405,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 36a89fe7..8cac0d1e 100644 --- a/src/codeGenerators/orchestration/files/toOrchestration.ts +++ b/src/codeGenerators/orchestration/files/toOrchestration.ts @@ -1082,8 +1082,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 12a44985..34c0957f 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 103a8b23e5adadab812b8b464bc631230f8120fa Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 2 Dec 2025 15:35:48 +0000 Subject: [PATCH 04/33] 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 8cac0d1e..1b87d43c 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) || ``; @@ -1074,6 +1074,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'`, @@ -1081,8 +1082,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 992ddb8c0fa6a56ce983b767251c927cdae688e0 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 2 Dec 2025 17:54:51 +0000 Subject: [PATCH 05/33] 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 1b87d43c..9fb658fb 100644 --- a/src/codeGenerators/orchestration/files/toOrchestration.ts +++ b/src/codeGenerators/orchestration/files/toOrchestration.ts @@ -1080,6 +1080,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 057fedfb1c3561bf7d4cc8128f84d60a5cefaa36 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Thu, 4 Dec 2025 16:28:42 +0000 Subject: [PATCH 06/33] 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 8ea93ff65f97761c73133777894e02f00c7b248b Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 16:16:14 +0000 Subject: [PATCH 07/33] 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 056a9868..cf93d049 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; @@ -1152,7 +1153,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 af3159717efe01d6cb906fe7efa06dbbc06994d4 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 16:24:08 +0000 Subject: [PATCH 08/33] 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 34c0957f..686ae5ab 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) { @@ -1350,9 +1348,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]; @@ -1518,7 +1520,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 e8b17ab07796a7442303490970d6896ef10031d7 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 16:23:22 +0000 Subject: [PATCH 09/33] 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 711e810bde2b87f66401f7dccb7f0461e1b7b336 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Fri, 12 Dec 2025 15:49:58 +0000 Subject: [PATCH 10/33] 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 a67fc3100a48fcb8889f4639b454b1a86e95af64 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Sun, 14 Dec 2025 18:52:25 +0000 Subject: [PATCH 11/33] 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 23063ad8ddf912e3edecd12888a92143eb055702 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Sun, 14 Dec 2025 19:20:08 +0000 Subject: [PATCH 12/33] 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 f1e25d637e1195e9d0f799c605ecfb9444e97a0a Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 11:33:32 +0000 Subject: [PATCH 13/33] 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 668920db1b514f54ecac2713071474e7c77cca24 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 11:45:06 +0000 Subject: [PATCH 14/33] 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 457dab29ca566e12b9deb4c9e1bb3623de703f4e Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 13:19:12 +0000 Subject: [PATCH 15/33] 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 21510e5fd88f5dd0583dd7d123cbc111610a2476 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 18:11:12 +0000 Subject: [PATCH 16/33] 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 a22e25a060e3eab12594d5a975019d9523c8d4a8 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 18:11:43 +0000 Subject: [PATCH 17/33] 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 6bcd96a258794f86adbcfa16c56443f5c354902b Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 15:03:58 +0000 Subject: [PATCH 18/33] 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 8259adfe1de6e3f68940ada9ddd989cbf9dac31d Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 16:04:01 +0000 Subject: [PATCH 19/33] 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 89d5dcc5b25939fa5a5f36dc0664eb3046986e68 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Mon, 15 Dec 2025 16:07:03 +0000 Subject: [PATCH 20/33] 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 ce020fdefaeec5f6a4468dc774b2ba67ec280d18 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 10:10:06 +0000 Subject: [PATCH 21/33] 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 d312a2b2f4f6d0026397f3b2995ca64b8b675feb Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 11:40:11 +0000 Subject: [PATCH 22/33] 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 abfb8824a1281c5adfd21957217168c265ea8ed4 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 13:59:48 +0000 Subject: [PATCH 23/33] 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 0a76a0852016b0dc4a0f83dfefe47a8388a18cb3 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 14:21:09 +0000 Subject: [PATCH 24/33] 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 d5f796a1471e3170202a2fd56ed977f4e4f75080 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 15:08:18 +0000 Subject: [PATCH 25/33] 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 d78c15f989f7518d81b2c2c6b6f6fdf0520da1dd Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 16:02:15 +0000 Subject: [PATCH 26/33] 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 cf93d049..2db1172e 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 e7d9e7076f5caf793e57aa7642a5abe409734a95 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 16:24:51 +0000 Subject: [PATCH 27/33] 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 69e9abb03f77e35d44f16ee69de298cf97ebe706 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 16:56:22 +0000 Subject: [PATCH 28/33] 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 a903b13ef17064e7d50da4c20b129f1804852a5d Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 17:10:43 +0000 Subject: [PATCH 29/33] 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 e512a8de..3c778d5b 100644 --- a/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts +++ b/src/transformers/visitors/orchestrationInternalFunctionCallVisitor.ts @@ -572,6 +572,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 df9191ef5cc9e9c6ce4dded4a91c2bed93551b4f Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 17:27:35 +0000 Subject: [PATCH 30/33] 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 d9d73f6c3b2f7c81e7bade36ae09411494a2463c Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Tue, 16 Dec 2025 17:32:20 +0000 Subject: [PATCH 31/33] 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 de6edeef735aa2ddd109e6d4c185f5ea0dec1847 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 13:51:21 +0000 Subject: [PATCH 32/33] 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 bfb2e9db2b69b7016a7fb1d9e71f040f737ff5e3 Mon Sep 17 00:00:00 2001 From: Lydia Garms Date: Wed, 17 Dec 2025 20:13:51 +0000 Subject: [PATCH 33/33] 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 2db1172e..63b214e2 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`, @@ -1114,10 +1117,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) => {