UNPKG

@unirep/contracts

Version:

Client library for contracts related functions which are used in UniRep protocol.

240 lines (239 loc) 13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.deployUnirep = exports.deployVerifierHelper = exports.deployVerifierHelpers = exports.deployVerifiers = exports.deployVerifier = exports.retryAsNeeded = void 0; const ethers_1 = require("ethers"); const circuits_1 = require("@unirep/circuits"); const poseidon_solidity_1 = require("poseidon-solidity"); const global_factory_1 = __importDefault(require("global-factory")); const typechain_1 = require("../typechain"); const utils_1 = require("./utils"); /** * The current supported verifier helpers. */ const VerifierHelpers = { epochKey: circuits_1.Circuit.epochKey, epochKeyLite: circuits_1.Circuit.epochKeyLite, reputation: circuits_1.Circuit.reputation, }; /** * Create the verififier helper contract name. * Capitalize the first character and add `VerifierHelper` at the end. * @param circuitName The name of the circuit */ const createVerifierHelperName = (circuitName) => { const verifierName = Object.keys(VerifierHelpers).find((key) => VerifierHelpers[key] == circuitName); if (verifierName === undefined) { throw new Error('Invalid verifier helper circuit'); } return `${verifierName.charAt(0).toUpperCase() + verifierName.slice(1)}VerifierHelper`; }; /** * Try a function several times. * @param fn The function will be executed. * @param maxRetry The maximum number of trying functions. */ const retryAsNeeded = async (fn, maxRetry = 10) => { let retryCount = 0; let backoff = 1000; for (;;) { try { return await fn(); } catch (err) { if (++retryCount > maxRetry) throw err; backoff *= 2; console.log(`Failed, waiting ${backoff}ms`); await new Promise((r) => setTimeout(r, backoff)); } } }; exports.retryAsNeeded = retryAsNeeded; /** * @param deployer A signer or an ethereum wallet * @param circuitName Name of the circuit, which can be chosen from `Circuit` * @param prover The prover which provides `vkey` of the circuit * @returns The deployed verifier smart contract */ const deployVerifier = async (deployer, circuitName, prover) => { const contractName = (0, utils_1.createVerifierName)(circuitName); console.log(`Deploying ${contractName}`); let artifacts; if (prover) { const vkey = await prover.getVKey(circuitName); artifacts = await (0, utils_1.compileVerifier)(contractName, vkey); } else { const verifierPath = `contracts/verifiers/${contractName}.sol/${contractName}.json`; artifacts = (0, utils_1.tryPath)(verifierPath); } const { bytecode, abi } = artifacts; const _verifierFactory = new ethers_1.ethers.ContractFactory(abi, bytecode, deployer); const verifierFactory = await (0, global_factory_1.default)(_verifierFactory); const verifierContract = await (0, exports.retryAsNeeded)(() => verifierFactory.deploy()); return verifierContract; }; exports.deployVerifier = deployVerifier; /** * @param deployer A signer or an ethereum wallet * @param prover The prover which provides `vkey` of the circuit * @returns All deployed verifier smart contracts */ const deployVerifiers = async (deployer, prover) => { let verifiers = {}; for (const circuit in circuits_1.Circuit) { const verifierContract = await (0, exports.deployVerifier)(deployer, circuit, prover); verifiers[circuit] = verifierContract.address; } return verifiers; }; exports.deployVerifiers = deployVerifiers; /** * @param deployer A signer or an ethereum wallet * @param prover The prover which provides `vkey` of the circuit * @returns All deployed verifier helper contracts */ const deployVerifierHelpers = async (unirepAddress, deployer, prover) => { let verifierHelpers = {}; for (const verifierHelper in VerifierHelpers) { const verifierContract = await (0, exports.deployVerifierHelper)(unirepAddress, deployer, VerifierHelpers[verifierHelper], prover); verifierHelpers[verifierHelper] = verifierContract; } return verifierHelpers; }; exports.deployVerifierHelpers = deployVerifierHelpers; /** * @param deployer A signer or an ethereum wallet * @param circuitName Name of the circuit, which can be chosen from `Circuit` * @param prover The prover which provides `vkey` of the circuit * @returns The deployed verifier helper contracts */ const deployVerifierHelper = async (unirepAddress, deployer, circuitName, prover) => { const verifier = await (0, exports.deployVerifier)(deployer, circuitName, prover); const contractName = createVerifierHelperName(circuitName); console.log(`Deploying ${contractName}`); let artifacts; if (prover) { const vkey = await prover.getVKey(contractName); artifacts = await (0, utils_1.compileVerifier)(contractName, vkey); } else { const verifierPath = `contracts/verifierHelpers/${contractName}.sol/${contractName}.json`; artifacts = (0, utils_1.tryPath)(verifierPath); } const { bytecode, abi } = artifacts; const _helperFactory = new ethers_1.ethers.ContractFactory(abi, bytecode, deployer); const helperFactory = await (0, global_factory_1.default)(_helperFactory); const helperContract = await (0, exports.retryAsNeeded)(() => helperFactory.deploy(unirepAddress, verifier.address)); await helperContract.deployed(); return helperContract; }; exports.deployVerifierHelper = deployVerifierHelper; /** * Deploy the unirep contract and verifier contracts with given `deployer` and settings * @param deployer A signer who will deploy the contracts * @param settings The settings that the deployer can define. See [`CircuitConfig`](https://developer.unirep.io/docs/circuits-api/circuit-config) * @param prover The prover which provides `vkey` of the circuit * @returns The Unirep smart contract * @example * ```ts * import { ethers } from 'ethers' * import { Unirep } from '@unirep/contracts' * import { deployUnirep } from '@unirep/contracts/deploy' * const privateKey = 'YOUR/PRIVATE/KEY' * const provider = 'YOUR/ETH/PROVIDER' * const deployer = new ethers.Wallet(privateKey, provider); * const unirepContract: Unirep = await deployUnirep(deployer) * ``` * * :::caution * The default circuit configuration is set in [`CircuitConfig.ts`](https://github.com/Unirep/Unirep/blob/1a3c9c944925ec125a7d7d8bfa9990466389477b/packages/circuits/src/CircuitConfig.ts).<br/> * Please make sure the `CircuitConfig` matches your [`prover`](circuits-api/interfaces/src.Prover.md). * If you don't compile circuits on your own, please don't change the `_settings` and `prover`.<br/> * See the current prover and settings of deployed contracts: [🤝 Testnet Deployment](https://developer.unirep.io/docs/testnet-deployment). * ::: */ const deployUnirep = async (deployer, settings, prover) => { var _a; if (!deployer.provider) { throw new Error('Deployer must have provider'); } const config = new circuits_1.CircuitConfig({ ...circuits_1.CircuitConfig.default, ...settings }); const { EPOCH_TREE_DEPTH, STATE_TREE_DEPTH, HISTORY_TREE_DEPTH, NUM_EPOCH_KEY_NONCE_PER_EPOCH, FIELD_COUNT, SUM_FIELD_COUNT, REPL_NONCE_BITS, REPL_FIELD_BITS, } = new circuits_1.CircuitConfig(settings); console.log('-----------------------------------------------------------------'); console.log(`Epoch tree depth: ${EPOCH_TREE_DEPTH}`); console.log(`State tree depth: ${STATE_TREE_DEPTH}`); console.log(`History tree depth: ${HISTORY_TREE_DEPTH}`); console.log(`Number of epoch keys per epoch: ${NUM_EPOCH_KEY_NONCE_PER_EPOCH}`); console.log(`Total fields per user: ${FIELD_COUNT}`); console.log(`Sum fields per user: ${SUM_FIELD_COUNT}`); console.log(`Replacement field nonce bits: ${REPL_NONCE_BITS}`); console.log(`Replacement field data bits: ${REPL_FIELD_BITS}`); console.log('-----------------------------------------------------------------'); console.log(`Make sure these match what you expect!`); console.log('-----------------------------------------------------------------'); if ((await deployer.provider.getCode(poseidon_solidity_1.PoseidonT3.proxyAddress)) === '0x') { await (0, exports.retryAsNeeded)(() => deployer.sendTransaction({ to: poseidon_solidity_1.PoseidonT3.from, value: poseidon_solidity_1.PoseidonT3.gas, })); await (0, exports.retryAsNeeded)(() => { var _a; return (_a = deployer.provider) === null || _a === void 0 ? void 0 : _a.sendTransaction(poseidon_solidity_1.PoseidonT3.tx); }); } if ((await deployer.provider.getCode(poseidon_solidity_1.PoseidonT3.address)) === '0x') { // nothing to do, contract is already deployed await (0, exports.retryAsNeeded)(() => deployer.sendTransaction({ to: poseidon_solidity_1.PoseidonT3.proxyAddress, data: poseidon_solidity_1.PoseidonT3.data, })); } const incPath = '@zk-kit/incremental-merkle-tree.sol/IncrementalBinaryTree.sol/IncrementalBinaryTree.json'; const incArtifacts = (0, utils_1.tryPath)(incPath); const _incrementalMerkleTreeFactory = new ethers_1.ethers.ContractFactory(incArtifacts.abi, (0, utils_1.linkLibrary)(incArtifacts.bytecode, { ['poseidon-solidity/PoseidonT3.sol:PoseidonT3']: poseidon_solidity_1.PoseidonT3.address, }), deployer); const incrementalMerkleTreeFactory = await (0, global_factory_1.default)(_incrementalMerkleTreeFactory); const incrementalMerkleTreeLib = await (0, exports.retryAsNeeded)(() => incrementalMerkleTreeFactory.deploy()); await incrementalMerkleTreeLib.deployed(); const reusableMerklePath = 'contracts/libraries/ReusableMerkleTree.sol/ReusableMerkleTree.json'; const reusableMerkleArtifacts = (0, utils_1.tryPath)(reusableMerklePath); const _reusableMerkleFactory = new ethers_1.ethers.ContractFactory(reusableMerkleArtifacts.abi, (0, utils_1.linkLibrary)(reusableMerkleArtifacts.bytecode, { ['poseidon-solidity/PoseidonT3.sol:PoseidonT3']: poseidon_solidity_1.PoseidonT3.address, }), deployer); const reusableMerkleFactory = await (0, global_factory_1.default)(_reusableMerkleFactory); const reusableMerkleContract = await (0, exports.retryAsNeeded)(() => reusableMerkleFactory.deploy()); await reusableMerkleContract.deployed(); const lazyMerklePath = 'contracts/libraries/LazyMerkleTree.sol/LazyMerkleTree.json'; const lazyMerkleArtifacts = (0, utils_1.tryPath)(lazyMerklePath); const _lazyMerkleFactory = new ethers_1.ethers.ContractFactory(lazyMerkleArtifacts.abi, (0, utils_1.linkLibrary)(lazyMerkleArtifacts.bytecode, { ['poseidon-solidity/PoseidonT3.sol:PoseidonT3']: poseidon_solidity_1.PoseidonT3.address, }), deployer); const lazyMerkleFactory = await (0, global_factory_1.default)(_lazyMerkleFactory); const lazyMerkleContract = await (0, exports.retryAsNeeded)(() => lazyMerkleFactory.deploy()); await lazyMerkleContract.deployed(); const verifiers = await (0, exports.deployVerifiers)(deployer, prover); console.log('Deploying Unirep'); const c = await (0, exports.retryAsNeeded)(async () => (await (0, global_factory_1.default)(new typechain_1.Unirep__factory({ ['@zk-kit/incremental-merkle-tree.sol/IncrementalBinaryTree.sol:IncrementalBinaryTree']: incrementalMerkleTreeLib.address, ['contracts/libraries/ReusableMerkleTree.sol:ReusableMerkleTree']: reusableMerkleContract.address, ['contracts/libraries/LazyMerkleTree.sol:LazyMerkleTree']: lazyMerkleContract.address, ['poseidon-solidity/PoseidonT3.sol:PoseidonT3']: poseidon_solidity_1.PoseidonT3.address, }, deployer))).deploy(config.contractConfig, verifiers[circuits_1.Circuit.signup], verifiers[circuits_1.Circuit.userStateTransition])); await (0, exports.retryAsNeeded)(() => { var _a; return (_a = c.deployTransaction) === null || _a === void 0 ? void 0 : _a.wait(); }); // Print out deployment info console.log('-----------------------------------------------------------------'); console.log('Bytecode size of Unirep:', Math.floor(typechain_1.Unirep__factory.bytecode.length / 2), 'bytes'); if (c.deployTransaction) { const receipt = await ((_a = deployer.provider) === null || _a === void 0 ? void 0 : _a.getTransactionReceipt(c.deployTransaction.hash)); console.log('Gas cost of deploying Unirep:', receipt === null || receipt === void 0 ? void 0 : receipt.gasUsed.toString()); } else { console.log('Re-using existing Unirep deployment'); } console.log(`Deployed to: ${c.address}`); console.log('-----------------------------------------------------------------'); return c; }; exports.deployUnirep = deployUnirep;