@unirep/contracts
Version:
Client library for contracts related functions which are used in UniRep protocol.
240 lines (239 loc) • 13 kB
JavaScript
;
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;