From e407762b89b593088686cf5edcc5bf708ec26c87 Mon Sep 17 00:00:00 2001 From: Alexander Filippov Date: Mon, 18 Dec 2023 17:18:45 +0300 Subject: [PATCH 01/17] [WIP] Add MPCGuard, modify deploy scripts --- .env.example | 5 +- contracts/src/interfaces/IZkBobPool.sol | 8 + .../src/zkbob/ZkBobDirectDepositQueue.sol | 3 +- contracts/src/zkbob/ZkBobPool.sol | 28 +- contracts/src/zkbob/manager/MPCGuard.sol | 166 ++++++++++++ .../src/zkbob/utils/CustomABIDecoder.sol | 31 +++ migrations/1_initial_migration.js | 2 - migrations/2_pool_impl.js | 49 ++-- migrations/3_pool.js | 247 ++++++++++-------- tronbox.js | 2 +- 10 files changed, 400 insertions(+), 141 deletions(-) create mode 100644 contracts/src/zkbob/manager/MPCGuard.sol diff --git a/.env.example b/.env.example index db07382..35eec4c 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,8 @@ export POOL_ID=16776966 export ADMIN=TASrJ76QANNPRgdDHHikWWApQzxh3HPku4 export OWNER=TASrJ76QANNPRgdDHHikWWApQzxh3HPku4 -export USE_STAGE_VERIFIERS=TRUE +# possible values: stage (default), prod, offchain +export VERIFIERS=offchain export INITIAL_ROOT=11469701942666298368112882412133877458305516134926649826543144744382391691533 export RELAYER=TASrJ76QANNPRgdDHHikWWApQzxh3HPku4 export FEE_RECEIVER=TASrJ76QANNPRgdDHHikWWApQzxh3HPku4 @@ -13,3 +14,5 @@ export PRIVATE_KEY= export POOL_IMPL=41fa7f6e7f0d58744de29fd86a63f4b9ee9a520cab export QUEUE_PROXY=41c85435d9c9094f2d7c210763b589fa44c96a39ce + +export MPC_GUARDS="TS8zrYZH2yNiiTNvn4GKJWcp1nJqQQFg3G,TK3Gx9Yy4tDJhYznm8QXAssLzdtpx3J2Yr,TE4BvV4sczfo4q82eF5qkHwv8gTTD2ccry" \ No newline at end of file diff --git a/contracts/src/interfaces/IZkBobPool.sol b/contracts/src/interfaces/IZkBobPool.sol index b9511f1..08369fa 100644 --- a/contracts/src/interfaces/IZkBobPool.sol +++ b/contracts/src/interfaces/IZkBobPool.sol @@ -6,4 +6,12 @@ interface IZkBobPool { function pool_id() external view returns (uint256); function recordDirectDeposit(address _sender, uint256 _amount) external; + + function appendDirectDeposits( + uint256 _root_after, + uint256[] calldata _indices, + uint256 _out_commit, + uint256[8] memory _batch_deposit_proof, + uint256[8] memory _tree_proof + ) external; } diff --git a/contracts/src/zkbob/ZkBobDirectDepositQueue.sol b/contracts/src/zkbob/ZkBobDirectDepositQueue.sol index eaa53eb..9ea5b40 100644 --- a/contracts/src/zkbob/ZkBobDirectDepositQueue.sol +++ b/contracts/src/zkbob/ZkBobDirectDepositQueue.sol @@ -52,7 +52,8 @@ contract ZkBobDirectDepositQueue is IZkBobDirectDeposits, IZkBobDirectDepositQue event CompleteDirectDepositBatch(uint256[] indices); constructor(address _pool, address _token, uint256 _denominator) { - require(Address.isContract(_token), "ZkBobDirectDepositQueue: not a contract"); + // TODO: uncomment this check + //require(Address.isContract(_token), "ZkBobDirectDepositQueue: not a contract"); pool = _pool; token = _token; TOKEN_DENOMINATOR = _denominator; diff --git a/contracts/src/zkbob/ZkBobPool.sol b/contracts/src/zkbob/ZkBobPool.sol index 4bf1871..8840274 100644 --- a/contracts/src/zkbob/ZkBobPool.sol +++ b/contracts/src/zkbob/ZkBobPool.sol @@ -68,10 +68,12 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk ZkBobAccounting(_precision) { require(__pool_id <= MAX_POOL_ID, "ZkBobPool: exceeds max pool id"); - require(Address.isContract(_token), "ZkBobPool: not a contract"); - require(Address.isContract(address(_transfer_verifier)), "ZkBobPool: not a contract"); - require(Address.isContract(address(_tree_verifier)), "ZkBobPool: not a contract"); - require(Address.isContract(address(_batch_deposit_verifier)), "ZkBobPool: not a contract"); + // TODO: uncomment this check + //require(Address.isContract(_token), "ZkBobPool: not a contract"); + + // require(Address.isContract(address(_transfer_verifier)), "ZkBobPool: not a contract"); + // require(Address.isContract(address(_tree_verifier)), "ZkBobPool: not a contract"); + // require(Address.isContract(address(_batch_deposit_verifier)), "ZkBobPool: not a contract"); require(Address.isContract(_direct_deposit_queue), "ZkBobPool: not a contract"); pool_id = __pool_id; token = _token; @@ -214,10 +216,10 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk require(nullifiers[nullifier] == 0, "ZkBobPool: doublespend detected"); require(_transfer_index() <= _pool_index, "ZkBobPool: transfer index out of bounds"); - require(transfer_verifier.verifyProof(_transfer_pub(), _transfer_proof()), "ZkBobPool: bad transfer proof"); - require( - tree_verifier.verifyProof(_tree_pub(roots[_pool_index]), _tree_proof()), "ZkBobPool: bad tree proof" - ); + // require(transfer_verifier.verifyProof(_transfer_pub(), _transfer_proof()), "ZkBobPool: bad transfer proof"); + // require( + // tree_verifier.verifyProof(_tree_pub(roots[_pool_index]), _tree_proof()), "ZkBobPool: bad tree proof" + // ); nullifiers[nullifier] = uint256(keccak256(abi.encodePacked(_transfer_out_commit(), _transfer_delta()))); _pool_index += 128; @@ -301,12 +303,12 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk uint256 _pool_index = txCount << 7; // verify that _out_commit corresponds to zero output account + 16 chosen notes + 111 empty notes - require( - batch_deposit_verifier.verifyProof([hashsum], _batch_deposit_proof), "ZkBobPool: bad batch deposit proof" - ); + // require( + // batch_deposit_verifier.verifyProof([hashsum], _batch_deposit_proof), "ZkBobPool: bad batch deposit proof" + // ); - uint256[3] memory tree_pub = [roots[_pool_index], _root_after, _out_commit]; - require(tree_verifier.verifyProof(tree_pub, _tree_proof), "ZkBobPool: bad tree proof"); + // uint256[3] memory tree_pub = [roots[_pool_index], _root_after, _out_commit]; + // require(tree_verifier.verifyProof(tree_pub, _tree_proof), "ZkBobPool: bad tree proof"); _pool_index += 128; roots[_pool_index] = _root_after; diff --git a/contracts/src/zkbob/manager/MPCGuard.sol b/contracts/src/zkbob/manager/MPCGuard.sol new file mode 100644 index 0000000..cf9e375 --- /dev/null +++ b/contracts/src/zkbob/manager/MPCGuard.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: CC0-1.0 + +pragma solidity ^0.8.0; + +import "../../../src/zkbob/ZkBobPool.sol"; +import "../../utils/Ownable.sol"; +import "../utils/CustomABIDecoder.sol"; + +import "../../interfaces/IZkBobPool.sol"; + +contract MPCGuard is Ownable, CustomABIDecoder { + address[] public guards; + + address operator; + + address public immutable pool; + + uint256 constant SIGNATURE_SIZE = 64; + + constructor(address _operator, address _pool) { + pool = _pool; + _setOperator(_operator); + } + + /** + * @dev Throws if called by any account other than the current relayer operator. + */ + modifier onlyOperator() { + require(operator == _msgSender(), "ZkBobPool: not an operator"); + _; + } + + function _setOperator(address _operator) internal { + operator = _operator; + } + + function setOperator(address _operator) external onlyOwner { + _setOperator(_operator); + } + + function setGuards(address[] calldata _guards) external onlyOwner { + guards = _guards; + } + + function digest(bytes memory data) internal returns (bytes32) { + return ECDSA.toEthSignedMessageHash(keccak256(data)); + } + + modifier calldataVerified() { + (uint8 count, bytes calldata signatures) = _mpc_signatures(); + require(count == guards.length, "MPCWrapper: wrong quorum"); + ZkBobPool poolContract = ZkBobPool(pool); + uint256 currentRoot = poolContract.roots(poolContract.pool_index()); + uint256 transferRoot = poolContract.roots(_transfer_index()); + require( + checkQuorum( + signatures, + digest(abi.encodePacked(_mpc_message(), transferRoot, currentRoot)) + ), + "MPCWrapper: wrong quorum" + ); + _; + } + + function checkQuorum( + bytes calldata signatures, + bytes32 _digest + ) internal view returns (bool) { + uint256 offset = 0; + assembly { + offset := signatures.offset + } + for (uint256 index = 0; index < guards.length; index++) { + bytes32 r; + bytes32 vs; + assembly { + r := calldataload(offset) + vs := calldataload(add(32, offset)) + offset := add(offset, 64) + } + address signer = ECDSA.recover(_digest, r, vs); + if (signer != guards[index]) { + return false; + } + } + return true; + } + + function transact() external calldataVerified onlyOperator { + return propagate(); + } + + /** + * @notice _tree_proof must be uint256[8] memory to avoid + * https://soliditylang.org/blog/2022/08/08/calldata-tuple-reencoding-head-overflow-bug/ + */ + function appendDirectDepositsMPC( + uint256 _root_after, + uint256[] calldata _indices, + uint256 _out_commit, + uint256[8] calldata _batch_deposit_proof, + uint256[8] memory _tree_proof, + bytes calldata signatures + ) external onlyOperator { + require( + signatures.length == guards.length * SIGNATURE_SIZE, + "MPCWrapper: wrong quorum" + ); + + ZkBobPool poolContract = ZkBobPool(pool); + + bytes memory mpc_message = abi.encodePacked( + ZkBobPool(pool).appendDirectDeposits.selector, + _root_after, + _indices, + _out_commit, + _batch_deposit_proof, + _tree_proof, + poolContract.roots(poolContract.pool_index()) + ); + + require(checkQuorum(signatures, digest(mpc_message))); + IZkBobPool(pool).appendDirectDeposits( + _root_after, + _indices, + _out_commit, + _batch_deposit_proof, + _tree_proof + ); + } + + function propagate() internal { + address contractAddress = pool; + uint256 _calldatasize = _mpc_signatures_pos(); //we don't need to propagate signatures + assembly { + // Copy msg.data. We take full control of memory in this inline assembly + // block because it will not return to Solidity code. We overwrite the + // Solidity scratch pad at memory position 0. + calldatacopy(0, 0, _calldatasize) + + // Call the implementation. + // out and outsize are 0 because we don't know the size yet. + let result := call( + gas(), + contractAddress, + 0, + 0, + _calldatasize, + 0, + 0 + ) + + // Copy the returned data. + returndatacopy(0, 0, returndatasize()) + + switch result + // delegatecall returns 0 on error. + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } +} diff --git a/contracts/src/zkbob/utils/CustomABIDecoder.sol b/contracts/src/zkbob/utils/CustomABIDecoder.sol index 85838f3..3ffe596 100644 --- a/contracts/src/zkbob/utils/CustomABIDecoder.sol +++ b/contracts/src/zkbob/utils/CustomABIDecoder.sol @@ -190,4 +190,35 @@ contract CustomABIDecoder { function _memo_permit_holder() internal pure returns (address r) { r = address(uint160(_loaduint256(memo_permit_holder_pos + memo_permit_holder_size - uint256_size))); } + + function _mpc_signatures_pos() internal pure returns (uint256 pos) { + uint256 t = _tx_type(); + if (t == 3 || t == 0) { + pos = _sign_r_vs_pos() + sign_r_vs_size; + } else { + pos = _sign_r_vs_pos(); + } + } + + function _mpc_message() internal pure returns (bytes calldata message) { + uint256 message_length = _mpc_signatures_pos(); + assembly { + message.offset := 0 + message.length := message_length + } + } + + uint256 constant signatures_count_size = 1; + + function _mpc_signatures() internal pure returns (uint8 count, bytes calldata signatures) { + uint256 offset = _mpc_signatures_pos(); + count = uint8(_loaduint256(offset + signatures_count_size - uint256_size)); + uint256 length = count * sign_r_vs_size; + + offset = offset + signatures_count_size; + assembly { + signatures.offset := offset + signatures.length := length + } + } } diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js index 4976c4b..94323b1 100644 --- a/migrations/1_initial_migration.js +++ b/migrations/1_initial_migration.js @@ -1,6 +1,4 @@ var Migrations = artifacts.require("Migrations"); -const TronWeb = require('tronweb'); - module.exports = async function(deployer) { await deployer.deploy(Migrations); diff --git a/migrations/2_pool_impl.js b/migrations/2_pool_impl.js index 665a597..58cdc5d 100644 --- a/migrations/2_pool_impl.js +++ b/migrations/2_pool_impl.js @@ -1,5 +1,4 @@ const TronWeb = require('tronweb'); -const { ethers } = require('ethers'); var EIP1967Proxy = artifacts.require("EIP1967Proxy"); var TransferVerifierProd = artifacts.require("TransferVerifier.sol"); @@ -11,40 +10,54 @@ var DelegatedDepositVerifierStage = artifacts.require("DelegatedDepositVerifierS var ZkBobPoolERC20 = artifacts.require("ZkBobPoolERC20"); module.exports = async function(deployer) { - const usdt = TronWeb.address.toHex(process.env.TOKEN); + const usdt = TronWeb.address.fromHex(process.env.TOKEN); + const zeroAddress = TronWeb.address.fromHex('410000000000000000000000000000000000000001'); + + // 1. Deploy verifiers var transferVerifier; var treeUpdateVerifier; var delegatedDepositVerifier; - if (process.env.USE_STAGE_VERIFIERS.toLowerCase() == 'true') { - await deployer.deploy(TransferVerifierStage); - transferVerifier = await TransferVerifierStage.deployed(); - await deployer.deploy(TreeUpdateVerifierStage); - treeUpdateVerifier = await TreeUpdateVerifierStage.deployed(); - await deployer.deploy(DelegatedDepositVerifierStage); - delegatedDepositVerifier = await DelegatedDepositVerifierStage.deployed(); - } else { + if (process.env.VERIFIERS.toLowerCase() == 'prod') { + console.log("Deploying prod verifiers"); await deployer.deploy(TransferVerifierProd); - transferVerifier = await TransferVerifierProd.deployed(); + transferVerifier = await TransferVerifierProd.deployed().address; await deployer.deploy(TreeUpdateVerifierProd); - treeUpdateVerifier = await TreeUpdateVerifierProd.deployed(); + treeUpdateVerifier = await TreeUpdateVerifierProd.deployed().address; await deployer.deploy(DelegatedDepositVerifierProd); - delegatedDepositVerifier = await DelegatedDepositVerifierProd.deployed(); + delegatedDepositVerifier = await DelegatedDepositVerifierProd.deployed().address; + } else if (process.env.VERIFIERS.toLowerCase() == 'offchain') { + console.log("Using offchain verifiers"); + // We don't deploy verifiers in this case + // We assume that they are not used in the pool contract + transferVerifier = zeroAddress; + treeUpdateVerifier = zeroAddress; + delegatedDepositVerifier = zeroAddress; + } else { + console.log("Deploying stage verifiers"); + await deployer.deploy(TransferVerifierStage); + transferVerifier = await TransferVerifierStage.deployed().address; + await deployer.deploy(TreeUpdateVerifierStage); + treeUpdateVerifier = await TreeUpdateVerifierStage.deployed().address; + await deployer.deploy(DelegatedDepositVerifierStage); + delegatedDepositVerifier = await DelegatedDepositVerifierStage.deployed().address; } + // 2. Deploy direct deposit queue proxy const deployerAddress = TronWeb.address.toHex(deployer.options.options.from); - await deployer.deploy(EIP1967Proxy, deployerAddress, usdt, []); + await deployer.deploy(EIP1967Proxy, deployerAddress, zeroAddress, []); const queueProxy = await EIP1967Proxy.deployed(); console.log('Queue proxy: ', queueProxy.address); + // 3. Deploy pool implementation await deployer.deploy( ZkBobPoolERC20, process.env.POOL_ID, usdt, - transferVerifier.address, - treeUpdateVerifier.address, - delegatedDepositVerifier.address, + transferVerifier, + treeUpdateVerifier, + delegatedDepositVerifier, queueProxy.address, - '410000000000000000000000000000000000000000', + zeroAddress, 1, 1000000, ); diff --git a/migrations/3_pool.js b/migrations/3_pool.js index 07c0be5..3fe1dba 100644 --- a/migrations/3_pool.js +++ b/migrations/3_pool.js @@ -7,6 +7,7 @@ var ZkBobDirectDepositQueue = artifacts.require("ZkBobDirectDepositQueue"); var MutableOperatorManager = artifacts.require("MutableOperatorManager"); var ZkAddress = artifacts.require("ZkAddress"); var Base58 = artifacts.require("Base58"); +var MPCGuard = artifacts.require("MPCGuard"); const abiCoder = new ethers.AbiCoder(); const ADDRESS_PREFIX_REGEX = /^(41)/; @@ -37,16 +38,99 @@ function encodeParams(inputs) { console.log(ex); } return parameters +} + +async function setGuards(deployer, tronWeb, mpcGuard) { + // parse guards addresses from process.env.MPC_GUARDS + const guards = process.env.MPC_GUARDS.split(','); + var transaction = await tronWeb.transactionBuilder.triggerSmartContract( + mpcGuard, + 'setGuards(address[])', + {}, + [ + {type: 'address[]', value: guards}, + ], + ); + var signed = await tronWeb.trx.sign( + transaction.transaction, + deployer.options.options.privateKey, + ); + return await tronWeb.trx.sendRawTransaction(signed); +} + +async function setOperatorManager(deployer, tronWeb, contractAddress, operatorManager) { + var transaction = await tronWeb.transactionBuilder.triggerSmartContract( + contractAddress, + 'setOperatorManager(address)', + {}, + [ + {type: 'address', value: tronWeb.address.fromHex(operatorManager)}, + ], + ); + var signed = await tronWeb.trx.sign( + transaction.transaction, + deployer.options.options.privateKey, + ); + return await tronWeb.trx.sendRawTransaction(signed); +} +async function transferOwnership(deployer, tronWeb, contractAddress, newOwner) { + var transaction = await tronWeb.transactionBuilder.triggerSmartContract( + contractAddress, + 'transferOwnership(address)', + {}, + [ + {type: 'address', value: tronWeb.address.fromHex(newOwner)}, + ], + ); + var signed = await tronWeb.trx.sign( + transaction.transaction, + deployer.options.options.privateKey, + ); + return await tronWeb.trx.sendRawTransaction(signed); +} + +async function setAdmin(deployer, tronWeb, contractAddress, newAdmin) { + var transaction = await tronWeb.transactionBuilder.triggerSmartContract( + contractAddress, + 'setAdmin(address)', + {}, + [ + {type: 'address', value: tronWeb.address.fromHex(newAdmin)}, + ], + ); + var signed = await tronWeb.trx.sign( + transaction.transaction, + deployer.options.options.privateKey, + ); + return await tronWeb.trx.sendRawTransaction(signed); +} + +function assert(condition, message) { + if (!condition) { + throw new Error(message || "Assertion failed"); + } +} + +async function assertSuccess(tronWeb, result, message) { + assert(result.result, message); + + var info = await tronWeb.trx.getTransactionInfo(result.txid); + while (!info.receipt) { + await new Promise(r => setTimeout(r, 1000)); + info = await tronWeb.trx.getTransactionInfo(result.txid); + } + assert(info.receipt.result == 'SUCCESS', message); } module.exports = async function(deployer) { - const usdt = TronWeb.address.toHex(process.env.TOKEN); + const usdt = TronWeb.address.fromHex(process.env.TOKEN); const tronWeb = new TronWeb({ fullNode: deployer.options.options.fullHost, solidityNode: deployer.options.options.fullHost, }, deployer.options.options.privateKey); + // 1. Deploy pool proxy const deployerAddress = TronWeb.address.toHex(deployer.options.options.from); await deployer.deploy(EIP1967Proxy, deployerAddress, usdt, []); const poolProxy = await EIP1967Proxy.deployed(); @@ -62,6 +146,7 @@ module.exports = async function(deployer) { {type: 'uint256', value: '0'}, // zkBobDirectDepositCap ]); + // 2. Initialize pool var selector = ZkBobPoolERC20.web3.eth.abi.encodeFunctionSignature("initialize(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)"); selector += params; console.log(poolProxy.address) @@ -84,12 +169,20 @@ module.exports = async function(deployer) { return; } + // 3. Deploy direct deposit queue implementation await deployer.deploy(Base58); await deployer.link(Base58, ZkAddress); await deployer.deploy(ZkAddress); await deployer.link(ZkAddress, ZkBobDirectDepositQueue); - await deployer.deploy(ZkBobDirectDepositQueue, poolProxy.address, usdt, 1); + await deployer.deploy( + ZkBobDirectDepositQueue, + TronWeb.address.fromHex(poolProxy.address), + usdt, + 1, + ); const queueImpl = await ZkBobDirectDepositQueue.deployed(); + + // 4. Upgrade direct deposit queue proxy transaction = await tronWeb.transactionBuilder.triggerSmartContract( process.env.QUEUE_PROXY, 'upgradeTo(address)', @@ -110,125 +203,69 @@ module.exports = async function(deployer) { return; } + // 5. Deploy MPCGuard with process.env.RELAYER as operator + await deployer.deploy( + MPCGuard, + tronWeb.address.fromHex(process.env.RELAYER), // operator + poolProxy.address, // pool + ); + const mpcGuard = await MPCGuard.deployed(); + + // 6. Set guards + result = await setGuards(deployer, tronWeb, mpcGuard.address); + await assertSuccess(tronWeb, result, 'Could not set guards'); + console.log("Set guards to " + process.env.MPC_GUARDS); + + // 7. Deploy MutableOperatorManager with MPCGuard as operator await deployer.deploy( MutableOperatorManager, - tronWeb.address.fromHex(process.env.RELAYER), // relayer + mpcGuard.address, // mpc guard tronWeb.address.fromHex(process.env.FEE_RECEIVER), // feeReceiver process.env.RELAYER_URL, // url ); const operatorManager = await MutableOperatorManager.deployed(); - transaction = await tronWeb.transactionBuilder.triggerSmartContract( - process.env.QUEUE_PROXY, - 'setOperatorManager(address)', - {}, - [ - {type: 'address', value: operatorManager.address}, - ] - ); - signed = await tronWeb.trx.sign( - transaction.transaction, - deployer.options.options.privateKey, - ); - result = await tronWeb.trx.sendRawTransaction( - signed - ); - if (!result.result) { - console.log('Could not set operator manager for queue proxy'); - return; - } + // 8. Set operator manager + result = await setOperatorManager(deployer, tronWeb, process.env.QUEUE_PROXY, operatorManager.address); + await assertSuccess(tronWeb, result, 'Could not set operator manager for queue proxy'); + console.log("Set operator manager for queue proxy to " + operatorManager.address); - transaction = await tronWeb.transactionBuilder.triggerSmartContract( - poolProxy.address, - 'setOperatorManager(address)', - {}, - [ - {type: 'address', value: operatorManager.address}, - ] - ); - signed = await tronWeb.trx.sign( - transaction.transaction, - deployer.options.options.privateKey, - ); - result = await tronWeb.trx.sendRawTransaction( - signed - ); - if (!result.result) { - console.log('Could not set operator manager for pool'); - return; - } + result = await setOperatorManager(deployer, tronWeb, poolProxy.address, operatorManager.address); + await assertSuccess(tronWeb, result, 'Could not set operator manager for pool proxy'); + console.log("Set operator manager for pool proxy to " + operatorManager.address); + console.log('MPCGuard: ', tronWeb.address.fromHex(mpcGuard.address)); console.log('Operator: ', tronWeb.address.fromHex(operatorManager.address)); console.log('Pool: ', tronWeb.address.fromHex(poolProxy.address)); console.log('Direct deposit queue: ', tronWeb.address.fromHex(process.env.QUEUE_PROXY)); - + // 9. Transfer ownership if (process.env.OWNER) { - transaction = await tronWeb.transactionBuilder.triggerSmartContract( - poolProxy.address, - 'transferOwnership(address)', - {}, - [ - {type: 'address', value: tronWeb.address.toHex(process.env.OWNER)}, - ], - ); - signed = await tronWeb.trx.sign( - transaction.transaction, - deployer.options.options.privateKey, - ); - result = await tronWeb.trx.sendRawTransaction( - signed - ); - - transaction = await tronWeb.transactionBuilder.triggerSmartContract( - process.env.QUEUE_PROXY, - 'transferOwnership(address)', - {}, - [ - {type: 'address', value: tronWeb.address.toHex(process.env.OWNER)}, - ], - ); - signed = await tronWeb.trx.sign( - transaction.transaction, - deployer.options.options.privateKey, - ); - result = await tronWeb.trx.sendRawTransaction( - signed - ); + result = await transferOwnership(deployer, tronWeb, poolProxy.address, process.env.OWNER); + await assertSuccess(tronWeb, result, 'Could not transfer ownership of pool proxy'); + console.log("Transfer ownership of pool proxy to " + process.env.OWNER); + + result = await transferOwnership(deployer, tronWeb, process.env.QUEUE_PROXY, process.env.OWNER); + await assertSuccess(tronWeb, result, 'Could not transfer ownership of queue proxy'); + console.log("Transfer ownership of queue proxy to " + process.env.OWNER); + + result = await transferOwnership(deployer, tronWeb, operatorManager.address, process.env.OWNER); + await assertSuccess(tronWeb, result, 'Could not transfer ownership of operator manager'); + console.log("Transfer ownership of operator manager to " + process.env.OWNER); + + result = await transferOwnership(deployer, tronWeb, mpcGuard.address, process.env.OWNER); + await assertSuccess(tronWeb, result, 'Could not transfer ownership of MPC guard'); + console.log("Transfer ownership of MPC guard to " + process.env.OWNER); } + // 10. Set admin if (tronWeb.address.toHex(process.env.ADMIN) != deployerAddress) { - transaction = await tronWeb.transactionBuilder.triggerSmartContract( - poolProxy.address, - 'setAdmin(address)', - {}, - [ - {type: 'address', value: tronWeb.address.toHex(process.env.ADMIN)}, - ], - ); - signed = await tronWeb.trx.sign( - transaction.transaction, - deployer.options.options.privateKey, - ); - result = await tronWeb.trx.sendRawTransaction( - signed - ); - - transaction = await tronWeb.transactionBuilder.triggerSmartContract( - process.env.QUEUE_PROXY, - 'setAdmin(address)', - {}, - [ - {type: 'address', value: tronWeb.address.toHex(process.env.ADMIN)}, - ], - ); - signed = await tronWeb.trx.sign( - transaction.transaction, - deployer.options.options.privateKey, - ); - result = await tronWeb.trx.sendRawTransaction( - signed - ); - } + result = await setAdmin(deployer, tronWeb, poolProxy.address, process.env.ADMIN); + await assertSuccess(tronWeb, result, 'Could not set admin of pool proxy'); + console.log("Set admin of pool proxy to " + process.env.ADMIN); + result = await setAdmin(deployer, tronWeb, process.env.QUEUE_PROXY, process.env.ADMIN); + await assertSuccess(tronWeb, result, 'Could not set admin of queue proxy'); + console.log("Set admin of queue proxy to " + process.env.ADMIN); + } }; diff --git a/tronbox.js b/tronbox.js index 9d5711a..d227d7a 100644 --- a/tronbox.js +++ b/tronbox.js @@ -30,7 +30,7 @@ Then, run the migration with: nile: { privateKey: process.env.PRIVATE_KEY_NILE, userFeePercentage: 100, - feeLimit: 1000 * 1e6, + feeLimit: 6000 * 1e6, fullHost: 'https://nile.trongrid.io', network_id: '3' }, From ba77273539759d634a779fe79eee3b8b103b85e1 Mon Sep 17 00:00:00 2001 From: Artyom Yusupov Date: Wed, 11 Oct 2023 20:21:26 +0400 Subject: [PATCH 02/17] Added transfer in withdrawFee --- contracts/src/zkbob/ZkBobPool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/zkbob/ZkBobPool.sol b/contracts/src/zkbob/ZkBobPool.sol index 8840274..ea7ca05 100644 --- a/contracts/src/zkbob/ZkBobPool.sol +++ b/contracts/src/zkbob/ZkBobPool.sol @@ -347,7 +347,7 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk ); uint256 fee = accumulatedFee[_operator] * TOKEN_DENOMINATOR; require(fee > 0, "ZkBobPool: no fee to withdraw"); - IERC20(token).safeTransfer(_to, fee); + IERC20(token).transfer(_to, fee); accumulatedFee[_operator] = 0; emit WithdrawFee(_operator, fee); } From f92fc852ff79244033dd25d54a718f04c5d97f51 Mon Sep 17 00:00:00 2001 From: Alexander Filippov Date: Mon, 18 Dec 2023 19:27:26 +0300 Subject: [PATCH 03/17] Minor fixes --- .env.example | 34 +++++++++++++++++++---------- migrations/2_pool_impl.js | 6 ++++-- migrations/3_pool.js | 45 +++++++++++++++++---------------------- tronbox.js | 12 +---------- 4 files changed, 48 insertions(+), 49 deletions(-) diff --git a/.env.example b/.env.example index 35eec4c..bf8d961 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,30 @@ +# all addresses in base58 + +export INITIAL_ROOT=11469701942666298368112882412133877458305516134926649826543144744382391691533 export POOL_ID=16776966 -export ADMIN=TASrJ76QANNPRgdDHHikWWApQzxh3HPku4 -export OWNER=TASrJ76QANNPRgdDHHikWWApQzxh3HPku4 + # possible values: stage (default), prod, offchain export VERIFIERS=offchain -export INITIAL_ROOT=11469701942666298368112882412133877458305516134926649826543144744382391691533 -export RELAYER=TASrJ76QANNPRgdDHHikWWApQzxh3HPku4 -export FEE_RECEIVER=TASrJ76QANNPRgdDHHikWWApQzxh3HPku4 -export RELAYER_URL=https://relayer-mvp.zkbob.com +# underlaying token +export TOKEN=TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf -export TOKEN=41abdbbd1e6728e236097f2a2b7f9b4eb8b115c1eb +# admin of the pool and direct deposit queue proxies +export ADMIN= +# owner of the pool, direct deposit queue, operator manager, and mpc guard +export OWNER= -export PRIVATE_KEY= +# address of the master relayer +export RELAYER= +# address of the fee receiver +export FEE_RECEIVER= +export RELAYER_URL=https://relayer-mvp.zkbob.com +# list of mpc guards (comma separated) +export MPC_GUARDS="TS8zrYZH2yNiiTNvn4GKJWcp1nJqQQFg3G,TK3Gx9Yy4tDJhYznm8QXAssLzdtpx3J2Yr,TE4BvV4sczfo4q82eF5qkHwv8gTTD2ccry" -export POOL_IMPL=41fa7f6e7f0d58744de29fd86a63f4b9ee9a520cab -export QUEUE_PROXY=41c85435d9c9094f2d7c210763b589fa44c96a39ce +export PRIVATE_KEY= +export PRIVATE_KEY_NILE= +export PRIVATE_KEY_MAINNET= -export MPC_GUARDS="TS8zrYZH2yNiiTNvn4GKJWcp1nJqQQFg3G,TK3Gx9Yy4tDJhYznm8QXAssLzdtpx3J2Yr,TE4BvV4sczfo4q82eF5qkHwv8gTTD2ccry" \ No newline at end of file +# this values should be filled after 2_pool_impl.js migration +export POOL_IMPL= +export QUEUE_PROXY= diff --git a/migrations/2_pool_impl.js b/migrations/2_pool_impl.js index 58cdc5d..68d5ff0 100644 --- a/migrations/2_pool_impl.js +++ b/migrations/2_pool_impl.js @@ -11,6 +11,7 @@ var ZkBobPoolERC20 = artifacts.require("ZkBobPoolERC20"); module.exports = async function(deployer) { const usdt = TronWeb.address.fromHex(process.env.TOKEN); + // 410000000000000000000000000000000000000000 does not work for some reason const zeroAddress = TronWeb.address.fromHex('410000000000000000000000000000000000000001'); // 1. Deploy verifiers @@ -46,7 +47,6 @@ module.exports = async function(deployer) { const deployerAddress = TronWeb.address.toHex(deployer.options.options.from); await deployer.deploy(EIP1967Proxy, deployerAddress, zeroAddress, []); const queueProxy = await EIP1967Proxy.deployed(); - console.log('Queue proxy: ', queueProxy.address); // 3. Deploy pool implementation await deployer.deploy( @@ -62,5 +62,7 @@ module.exports = async function(deployer) { 1000000, ); const poolImpl = await ZkBobPoolERC20.deployed(); - console.log('Pool implementation: ', poolImpl.address); + + console.log('Pool implementation: ', TronWeb.address.fromHex(poolImpl.address)); + console.log('Queue proxy: ', TronWeb.address.fromHex(queueProxy.address)); }; diff --git a/migrations/3_pool.js b/migrations/3_pool.js index 3fe1dba..3fe3207 100644 --- a/migrations/3_pool.js +++ b/migrations/3_pool.js @@ -106,21 +106,21 @@ async function setAdmin(deployer, tronWeb, contractAddress, newAdmin) { return await tronWeb.trx.sendRawTransaction(signed); } -function assert(condition, message) { - if (!condition) { +async function assertSuccess(tronWeb, result, message) { + if (!result.result) { + console.log("Result: " + result); throw new Error(message || "Assertion failed"); } -} - -async function assertSuccess(tronWeb, result, message) { - assert(result.result, message); var info = await tronWeb.trx.getTransactionInfo(result.txid); while (!info.receipt) { await new Promise(r => setTimeout(r, 1000)); info = await tronWeb.trx.getTransactionInfo(result.txid); } - assert(info.receipt.result == 'SUCCESS', message); + if (info.receipt.result != 'SUCCESS') { + console.log("Info: " + info); + throw new Error(message || "Assertion failed"); + } } module.exports = async function(deployer) { @@ -149,7 +149,6 @@ module.exports = async function(deployer) { // 2. Initialize pool var selector = ZkBobPoolERC20.web3.eth.abi.encodeFunctionSignature("initialize(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)"); selector += params; - console.log(poolProxy.address) var transaction = await tronWeb.transactionBuilder.triggerSmartContract( poolProxy.address, 'upgradeToAndCall(address,bytes)', @@ -164,10 +163,8 @@ module.exports = async function(deployer) { deployer.options.options.privateKey, ); var result = await tronWeb.trx.sendRawTransaction(signed); - if (!result.result) { - console.log('Could not initialize pool'); - return; - } + await assertSuccess(tronWeb, result, 'Could not initialize pool'); + console.log("Initialized pool"); // 3. Deploy direct deposit queue implementation await deployer.deploy(Base58); @@ -198,10 +195,8 @@ module.exports = async function(deployer) { result = await tronWeb.trx.sendRawTransaction( signed ); - if (!result.result) { - console.log('Could not upgrade queue proxy'); - return; - } + await assertSuccess(tronWeb, result, 'Could not upgrade direct deposit queue proxy'); + console.log("Upgraded direct deposit queue proxy"); // 5. Deploy MPCGuard with process.env.RELAYER as operator await deployer.deploy( @@ -228,19 +223,14 @@ module.exports = async function(deployer) { // 8. Set operator manager result = await setOperatorManager(deployer, tronWeb, process.env.QUEUE_PROXY, operatorManager.address); await assertSuccess(tronWeb, result, 'Could not set operator manager for queue proxy'); - console.log("Set operator manager for queue proxy to " + operatorManager.address); + console.log("Set operator manager for queue proxy to " + TronWeb.address.fromHex(operatorManager.address)); result = await setOperatorManager(deployer, tronWeb, poolProxy.address, operatorManager.address); await assertSuccess(tronWeb, result, 'Could not set operator manager for pool proxy'); - console.log("Set operator manager for pool proxy to " + operatorManager.address); - - console.log('MPCGuard: ', tronWeb.address.fromHex(mpcGuard.address)); - console.log('Operator: ', tronWeb.address.fromHex(operatorManager.address)); - console.log('Pool: ', tronWeb.address.fromHex(poolProxy.address)); - console.log('Direct deposit queue: ', tronWeb.address.fromHex(process.env.QUEUE_PROXY)); + console.log("Set operator manager for pool proxy to " + TronWeb.address.fromHex(operatorManager.address)); // 9. Transfer ownership - if (process.env.OWNER) { + if (process.env.OWNER && tronWeb.address.toHex(process.env.OWNER) != deployerAddress) { result = await transferOwnership(deployer, tronWeb, poolProxy.address, process.env.OWNER); await assertSuccess(tronWeb, result, 'Could not transfer ownership of pool proxy'); console.log("Transfer ownership of pool proxy to " + process.env.OWNER); @@ -259,7 +249,7 @@ module.exports = async function(deployer) { } // 10. Set admin - if (tronWeb.address.toHex(process.env.ADMIN) != deployerAddress) { + if (process.env.ADMIN && tronWeb.address.toHex(process.env.ADMIN) != deployerAddress) { result = await setAdmin(deployer, tronWeb, poolProxy.address, process.env.ADMIN); await assertSuccess(tronWeb, result, 'Could not set admin of pool proxy'); console.log("Set admin of pool proxy to " + process.env.ADMIN); @@ -268,4 +258,9 @@ module.exports = async function(deployer) { await assertSuccess(tronWeb, result, 'Could not set admin of queue proxy'); console.log("Set admin of queue proxy to " + process.env.ADMIN); } + + console.log('MPCGuard: ', tronWeb.address.fromHex(mpcGuard.address)); + console.log('Operator: ', tronWeb.address.fromHex(operatorManager.address)); + console.log('Pool: ', tronWeb.address.fromHex(poolProxy.address)); + console.log('Direct deposit queue: ', tronWeb.address.fromHex(process.env.QUEUE_PROXY)); }; diff --git a/tronbox.js b/tronbox.js index d227d7a..308085a 100644 --- a/tronbox.js +++ b/tronbox.js @@ -5,18 +5,8 @@ module.exports = { mainnet: { // Don't put your private key here: privateKey: process.env.PRIVATE_KEY_MAINNET, - /* -Create a .env file (it must be gitignored) containing something like - - export PRIVATE_KEY_MAINNET=4E7FEC...656243 - -Then, run the migration with: - - source .env && tronbox migrate --network mainnet - - */ userFeePercentage: 100, - feeLimit: 1000 * 1e6, + feeLimit: 6000 * 1e6, fullHost: 'https://api.trongrid.io', network_id: '1' }, From 3d0dbafcc612f301ad10d9e4c617a54bf2205578 Mon Sep 17 00:00:00 2001 From: Alexander Filippov Date: Mon, 18 Dec 2023 19:47:19 +0300 Subject: [PATCH 04/17] Update MPCGuard.sol --- contracts/src/zkbob/manager/MPCGuard.sol | 53 +++++++----------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/contracts/src/zkbob/manager/MPCGuard.sol b/contracts/src/zkbob/manager/MPCGuard.sol index cf9e375..491bbf4 100644 --- a/contracts/src/zkbob/manager/MPCGuard.sol +++ b/contracts/src/zkbob/manager/MPCGuard.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: CC0-1.0 +// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; @@ -17,7 +17,7 @@ contract MPCGuard is Ownable, CustomABIDecoder { uint256 constant SIGNATURE_SIZE = 64; - constructor(address _operator, address _pool) { + constructor(address _operator, address _pool) Ownable() { pool = _pool; _setOperator(_operator); } @@ -42,7 +42,7 @@ contract MPCGuard is Ownable, CustomABIDecoder { guards = _guards; } - function digest(bytes memory data) internal returns (bytes32) { + function digest(bytes memory data) internal pure returns (bytes32) { return ECDSA.toEthSignedMessageHash(keccak256(data)); } @@ -54,18 +54,14 @@ contract MPCGuard is Ownable, CustomABIDecoder { uint256 transferRoot = poolContract.roots(_transfer_index()); require( checkQuorum( - signatures, - digest(abi.encodePacked(_mpc_message(), transferRoot, currentRoot)) + signatures, digest(abi.encodePacked(_mpc_message(), transferRoot, currentRoot, poolContract.pool_id())) ), "MPCWrapper: wrong quorum" ); _; } - function checkQuorum( - bytes calldata signatures, - bytes32 _digest - ) internal view returns (bool) { + function checkQuorum(bytes calldata signatures, bytes32 _digest) internal view returns (bool) { uint256 offset = 0; assembly { offset := signatures.offset @@ -101,11 +97,11 @@ contract MPCGuard is Ownable, CustomABIDecoder { uint256[8] calldata _batch_deposit_proof, uint256[8] memory _tree_proof, bytes calldata signatures - ) external onlyOperator { - require( - signatures.length == guards.length * SIGNATURE_SIZE, - "MPCWrapper: wrong quorum" - ); + ) + external + onlyOperator + { + require(signatures.length == guards.length * SIGNATURE_SIZE, "MPCWrapper: wrong quorum"); ZkBobPool poolContract = ZkBobPool(pool); @@ -116,17 +112,12 @@ contract MPCGuard is Ownable, CustomABIDecoder { _out_commit, _batch_deposit_proof, _tree_proof, - poolContract.roots(poolContract.pool_index()) + poolContract.roots(poolContract.pool_index()), + poolContract.pool_id() ); require(checkQuorum(signatures, digest(mpc_message))); - IZkBobPool(pool).appendDirectDeposits( - _root_after, - _indices, - _out_commit, - _batch_deposit_proof, - _tree_proof - ); + IZkBobPool(pool).appendDirectDeposits(_root_after, _indices, _out_commit, _batch_deposit_proof, _tree_proof); } function propagate() internal { @@ -140,27 +131,15 @@ contract MPCGuard is Ownable, CustomABIDecoder { // Call the implementation. // out and outsize are 0 because we don't know the size yet. - let result := call( - gas(), - contractAddress, - 0, - 0, - _calldatasize, - 0, - 0 - ) + let result := call(gas(), contractAddress, 0, 0, _calldatasize, 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. - case 0 { - revert(0, returndatasize()) - } - default { - return(0, returndatasize()) - } + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } } } } From 671dbefde7430d40fed7d607584e4f20424daf3b Mon Sep 17 00:00:00 2001 From: Alexander Filippov Date: Tue, 19 Dec 2023 12:05:32 +0300 Subject: [PATCH 05/17] Minor fixes --- contracts/src/zkbob/ZkBobDirectDepositQueue.sol | 3 +-- contracts/src/zkbob/ZkBobPool.sol | 3 +-- contracts/src/zkbob/manager/MPCGuard.sol | 2 +- migrations/3_pool.js | 1 + 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/contracts/src/zkbob/ZkBobDirectDepositQueue.sol b/contracts/src/zkbob/ZkBobDirectDepositQueue.sol index 9ea5b40..eaa53eb 100644 --- a/contracts/src/zkbob/ZkBobDirectDepositQueue.sol +++ b/contracts/src/zkbob/ZkBobDirectDepositQueue.sol @@ -52,8 +52,7 @@ contract ZkBobDirectDepositQueue is IZkBobDirectDeposits, IZkBobDirectDepositQue event CompleteDirectDepositBatch(uint256[] indices); constructor(address _pool, address _token, uint256 _denominator) { - // TODO: uncomment this check - //require(Address.isContract(_token), "ZkBobDirectDepositQueue: not a contract"); + require(Address.isContract(_token), "ZkBobDirectDepositQueue: not a contract"); pool = _pool; token = _token; TOKEN_DENOMINATOR = _denominator; diff --git a/contracts/src/zkbob/ZkBobPool.sol b/contracts/src/zkbob/ZkBobPool.sol index ea7ca05..7734e8d 100644 --- a/contracts/src/zkbob/ZkBobPool.sol +++ b/contracts/src/zkbob/ZkBobPool.sol @@ -68,8 +68,7 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk ZkBobAccounting(_precision) { require(__pool_id <= MAX_POOL_ID, "ZkBobPool: exceeds max pool id"); - // TODO: uncomment this check - //require(Address.isContract(_token), "ZkBobPool: not a contract"); + require(Address.isContract(_token), "ZkBobPool: not a contract"); // require(Address.isContract(address(_transfer_verifier)), "ZkBobPool: not a contract"); // require(Address.isContract(address(_tree_verifier)), "ZkBobPool: not a contract"); diff --git a/contracts/src/zkbob/manager/MPCGuard.sol b/contracts/src/zkbob/manager/MPCGuard.sol index 491bbf4..5258bf9 100644 --- a/contracts/src/zkbob/manager/MPCGuard.sol +++ b/contracts/src/zkbob/manager/MPCGuard.sol @@ -11,7 +11,7 @@ import "../../interfaces/IZkBobPool.sol"; contract MPCGuard is Ownable, CustomABIDecoder { address[] public guards; - address operator; + address public operator; address public immutable pool; diff --git a/migrations/3_pool.js b/migrations/3_pool.js index 3fe3207..5e6f738 100644 --- a/migrations/3_pool.js +++ b/migrations/3_pool.js @@ -112,6 +112,7 @@ async function assertSuccess(tronWeb, result, message) { throw new Error(message || "Assertion failed"); } + console.log("Waiting for transaction to be confirmed..."); var info = await tronWeb.trx.getTransactionInfo(result.txid); while (!info.receipt) { await new Promise(r => setTimeout(r, 1000)); From b5b0272a119e433d6cd678ff1ea013ff0ab5cf84 Mon Sep 17 00:00:00 2001 From: r0wdy1 <103738251+r0wdy1@users.noreply.github.com> Date: Tue, 26 Dec 2023 17:40:42 +0300 Subject: [PATCH 06/17] fix zero address Co-authored-by: Kirill Fedoseev --- migrations/2_pool_impl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/2_pool_impl.js b/migrations/2_pool_impl.js index 68d5ff0..372647b 100644 --- a/migrations/2_pool_impl.js +++ b/migrations/2_pool_impl.js @@ -12,7 +12,7 @@ var ZkBobPoolERC20 = artifacts.require("ZkBobPoolERC20"); module.exports = async function(deployer) { const usdt = TronWeb.address.fromHex(process.env.TOKEN); // 410000000000000000000000000000000000000000 does not work for some reason - const zeroAddress = TronWeb.address.fromHex('410000000000000000000000000000000000000001'); + const zeroAddress = '410000000000000000000000000000000000000000'; // 1. Deploy verifiers var transferVerifier; From 32241858b5c48f8d6a98339e498bc43b5660f87f Mon Sep 17 00:00:00 2001 From: vladimir Date: Wed, 27 Dec 2023 16:04:54 +0300 Subject: [PATCH 07/17] remove mixins and permits --- ...ial_migration.js => 1_initial_migration.js | 0 contracts/src/zkbob/ZkBobPermit2Mixin.sol | 45 ------------------- contracts/src/zkbob/ZkBobPool.sol | 15 +------ contracts/src/zkbob/ZkBobPoolBOB.sol | 33 -------------- contracts/src/zkbob/ZkBobPoolERC20.sol | 4 +- contracts/src/zkbob/ZkBobPoolETH.sol | 35 --------------- contracts/src/zkbob/ZkBobPoolUSDC.sol | 33 -------------- .../src/zkbob/ZkBobSaltedPermitMixin.sol | 18 -------- contracts/src/zkbob/ZkBobUSDCPermitMixin.sol | 27 ----------- migrations/3_pool.js | 34 -------------- truffle-config.js | 4 +- 11 files changed, 4 insertions(+), 244 deletions(-) rename migrations/1_initial_migration.js => 1_initial_migration.js (100%) delete mode 100644 contracts/src/zkbob/ZkBobPermit2Mixin.sol delete mode 100644 contracts/src/zkbob/ZkBobPoolBOB.sol delete mode 100644 contracts/src/zkbob/ZkBobPoolETH.sol delete mode 100644 contracts/src/zkbob/ZkBobPoolUSDC.sol delete mode 100644 contracts/src/zkbob/ZkBobSaltedPermitMixin.sol delete mode 100644 contracts/src/zkbob/ZkBobUSDCPermitMixin.sol diff --git a/migrations/1_initial_migration.js b/1_initial_migration.js similarity index 100% rename from migrations/1_initial_migration.js rename to 1_initial_migration.js diff --git a/contracts/src/zkbob/ZkBobPermit2Mixin.sol b/contracts/src/zkbob/ZkBobPermit2Mixin.sol deleted file mode 100644 index 952247b..0000000 --- a/contracts/src/zkbob/ZkBobPermit2Mixin.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../interfaces/IPermit2.sol"; -import "./ZkBobPool.sol"; - -/** - * @title ZkBobPermit2Mixin - */ -abstract contract ZkBobPermit2Mixin is ZkBobPool { - IPermit2 public immutable permit2; - - constructor(address _permit2) { - // require(Address.isContract(_permit2), "ZkBobPool: not a contract"); - permit2 = IPermit2(_permit2); - } - - // @inheritdoc ZkBobPool - function _transferFromByPermit(address _user, uint256 _nullifier, int256 _tokenAmount) internal override { - (uint8 v, bytes32 r, bytes32 s) = _permittable_deposit_signature(); - - bytes memory depositSignature = new bytes(65); - - assembly { - mstore(add(depositSignature, 0x20), r) - mstore(add(depositSignature, 0x40), s) - mstore8(add(depositSignature, 0x60), v) - } - - permit2.permitTransferFrom( - IPermit2.PermitTransferFrom({ - permitted: IPermit2.TokenPermissions({token: token, amount: uint256(_tokenAmount) * TOKEN_DENOMINATOR}), - nonce: _nullifier, - deadline: uint256(_memo_permit_deadline()) - }), - IPermit2.SignatureTransferDetails({ - to: address(this), - requestedAmount: uint256(_tokenAmount) * TOKEN_DENOMINATOR - }), - _user, - depositSignature - ); - } -} diff --git a/contracts/src/zkbob/ZkBobPool.sol b/contracts/src/zkbob/ZkBobPool.sol index 7734e8d..ef9892b 100644 --- a/contracts/src/zkbob/ZkBobPool.sol +++ b/contracts/src/zkbob/ZkBobPool.sol @@ -73,7 +73,6 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk // require(Address.isContract(address(_transfer_verifier)), "ZkBobPool: not a contract"); // require(Address.isContract(address(_tree_verifier)), "ZkBobPool: not a contract"); // require(Address.isContract(address(_batch_deposit_verifier)), "ZkBobPool: not a contract"); - require(Address.isContract(_direct_deposit_queue), "ZkBobPool: not a contract"); pool_id = __pool_id; token = _token; transfer_verifier = _transfer_verifier; @@ -176,14 +175,6 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk */ function _withdrawNative(address _user, uint256 _tokenAmount) internal virtual returns (uint256); - /** - * @dev Performs token transfer using a signed permit signature. - * @param _user token depositor address, should correspond to the signature author. - * @param _nullifier nullifier and permit signature salt to avoid transaction data manipulation. - * @param _tokenAmount amount to tokens to deposit. - */ - function _transferFromByPermit(address _user, uint256 _nullifier, int256 _tokenAmount) internal virtual; - /** * @dev Perform a zkBob pool transaction. * Callable only by the current operator. @@ -255,7 +246,7 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk } if (withdraw_amount > 0) { - IERC20(token).transfer(user, withdraw_amount); + IERC20(token).safeTransfer(user, withdraw_amount); } // energy withdrawals are not yet implemented, any transaction with non-zero energy_amount will revert @@ -263,10 +254,6 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk if (energy_amount < 0) { revert("ZkBobPool: XP claiming is not yet enabled"); } - } else if (txType == 3) { - // Permittable token deposit - require(transfer_token_delta > 0 && energy_amount == 0, "ZkBobPool: incorrect deposit amounts"); - _transferFromByPermit(user, nullifier, token_amount); } else { revert("ZkBobPool: Incorrect transaction type"); } diff --git a/contracts/src/zkbob/ZkBobPoolBOB.sol b/contracts/src/zkbob/ZkBobPoolBOB.sol deleted file mode 100644 index 8c2322e..0000000 --- a/contracts/src/zkbob/ZkBobPoolBOB.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./ZkBobPool.sol"; -import "./ZkBobTokenSellerMixin.sol"; -import "./ZkBobSaltedPermitMixin.sol"; - -/** - * @title ZkBobPoolBOB - * Shielded transactions pool for BOB tokens. - */ -contract ZkBobPoolBOB is ZkBobPool, ZkBobTokenSellerMixin, ZkBobSaltedPermitMixin { - constructor( - uint256 __pool_id, - address _token, - ITransferVerifier _transfer_verifier, - ITreeVerifier _tree_verifier, - IBatchDepositVerifier _batch_deposit_verifier, - address _direct_deposit_queue - ) - ZkBobPool( - __pool_id, - _token, - _transfer_verifier, - _tree_verifier, - _batch_deposit_verifier, - _direct_deposit_queue, - 1_000_000_000, - 1_000_000_000 - ) - {} -} diff --git a/contracts/src/zkbob/ZkBobPoolERC20.sol b/contracts/src/zkbob/ZkBobPoolERC20.sol index c95ecd9..c7e09b3 100644 --- a/contracts/src/zkbob/ZkBobPoolERC20.sol +++ b/contracts/src/zkbob/ZkBobPoolERC20.sol @@ -4,13 +4,12 @@ pragma solidity ^0.8.0; import "./ZkBobPool.sol"; import "./ZkBobTokenSellerMixin.sol"; -import "./ZkBobPermit2Mixin.sol"; /** * @title ZkBobPoolERC20 * Shielded transactions pool for ERC20 tokens */ -contract ZkBobPoolERC20 is ZkBobPool, ZkBobTokenSellerMixin, ZkBobPermit2Mixin { +contract ZkBobPoolERC20 is ZkBobPool, ZkBobTokenSellerMixin { constructor( uint256 __pool_id, address _token, @@ -32,6 +31,5 @@ contract ZkBobPoolERC20 is ZkBobPool, ZkBobTokenSellerMixin, ZkBobPermit2Mixin { _denominator, _precision ) - ZkBobPermit2Mixin(_permit2) {} } diff --git a/contracts/src/zkbob/ZkBobPoolETH.sol b/contracts/src/zkbob/ZkBobPoolETH.sol deleted file mode 100644 index bb1ef79..0000000 --- a/contracts/src/zkbob/ZkBobPoolETH.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./ZkBobPool.sol"; -import "./ZkBobWETHMixin.sol"; -import "./ZkBobPermit2Mixin.sol"; - -/** - * @title ZkBobPoolETH - * Shielded transactions pool for native and wrapped native tokens. - */ -contract ZkBobPoolETH is ZkBobPool, ZkBobWETHMixin, ZkBobPermit2Mixin { - constructor( - uint256 __pool_id, - address _token, - ITransferVerifier _transfer_verifier, - ITreeVerifier _tree_verifier, - IBatchDepositVerifier _batch_deposit_verifier, - address _direct_deposit_queue, - address _permit2 - ) - ZkBobPool( - __pool_id, - _token, - _transfer_verifier, - _tree_verifier, - _batch_deposit_verifier, - _direct_deposit_queue, - 1_000_000_000, - 1_000_000_000 - ) - ZkBobPermit2Mixin(_permit2) - {} -} diff --git a/contracts/src/zkbob/ZkBobPoolUSDC.sol b/contracts/src/zkbob/ZkBobPoolUSDC.sol deleted file mode 100644 index 0ba57be..0000000 --- a/contracts/src/zkbob/ZkBobPoolUSDC.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./ZkBobPool.sol"; -import "./ZkBobTokenSellerMixin.sol"; -import "./ZkBobUSDCPermitMixin.sol"; - -/** - * @title ZkBobPoolUSDC - * Shielded transactions pool for USDC tokens supporting USDC transfer authorizations - */ -contract ZkBobPoolUSDC is ZkBobPool, ZkBobTokenSellerMixin, ZkBobUSDCPermitMixin { - constructor( - uint256 __pool_id, - address _token, - ITransferVerifier _transfer_verifier, - ITreeVerifier _tree_verifier, - IBatchDepositVerifier _batch_deposit_verifier, - address _direct_deposit_queue - ) - ZkBobPool( - __pool_id, - _token, - _transfer_verifier, - _tree_verifier, - _batch_deposit_verifier, - _direct_deposit_queue, - 1, - 1_000_000 - ) - {} -} diff --git a/contracts/src/zkbob/ZkBobSaltedPermitMixin.sol b/contracts/src/zkbob/ZkBobSaltedPermitMixin.sol deleted file mode 100644 index 4e12e29..0000000 --- a/contracts/src/zkbob/ZkBobSaltedPermitMixin.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "./ZkBobPool.sol"; - -/** - * @title ZkBobSaltedPermitMixin - */ -abstract contract ZkBobSaltedPermitMixin is ZkBobPool { - // @inheritdoc ZkBobPool - function _transferFromByPermit(address _user, uint256 _nullifier, int256 _tokenAmount) internal override { - (uint8 v, bytes32 r, bytes32 s) = _permittable_deposit_signature(); - IERC20Permit(token).receiveWithSaltedPermit( - _user, uint256(_tokenAmount) * TOKEN_DENOMINATOR, _memo_permit_deadline(), bytes32(_nullifier), v, r, s - ); - } -} diff --git a/contracts/src/zkbob/ZkBobUSDCPermitMixin.sol b/contracts/src/zkbob/ZkBobUSDCPermitMixin.sol deleted file mode 100644 index ffec2ff..0000000 --- a/contracts/src/zkbob/ZkBobUSDCPermitMixin.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import "../interfaces/IUSDCPermit.sol"; -import "./ZkBobPool.sol"; - -/** - * @title ZkBobUSDCPermitMixin - */ -abstract contract ZkBobUSDCPermitMixin is ZkBobPool { - // @inheritdoc ZkBobPool - function _transferFromByPermit(address _user, uint256 _nullifier, int256 _tokenAmount) internal override { - (uint8 v, bytes32 r, bytes32 s) = _permittable_deposit_signature(); - IUSDCPermit(token).transferWithAuthorization( - _user, - address(this), - uint256(_tokenAmount) * TOKEN_DENOMINATOR, - 0, - _memo_permit_deadline(), - bytes32(_nullifier), - v, - r, - s - ); - } -} diff --git a/migrations/3_pool.js b/migrations/3_pool.js index 5e6f738..e4c6066 100644 --- a/migrations/3_pool.js +++ b/migrations/3_pool.js @@ -167,38 +167,6 @@ module.exports = async function(deployer) { await assertSuccess(tronWeb, result, 'Could not initialize pool'); console.log("Initialized pool"); - // 3. Deploy direct deposit queue implementation - await deployer.deploy(Base58); - await deployer.link(Base58, ZkAddress); - await deployer.deploy(ZkAddress); - await deployer.link(ZkAddress, ZkBobDirectDepositQueue); - await deployer.deploy( - ZkBobDirectDepositQueue, - TronWeb.address.fromHex(poolProxy.address), - usdt, - 1, - ); - const queueImpl = await ZkBobDirectDepositQueue.deployed(); - - // 4. Upgrade direct deposit queue proxy - transaction = await tronWeb.transactionBuilder.triggerSmartContract( - process.env.QUEUE_PROXY, - 'upgradeTo(address)', - {}, - [ - {'type': 'address', 'value': queueImpl.address}, - ] - ); - signed = await tronWeb.trx.sign( - transaction.transaction, - deployer.options.options.privateKey, - ); - result = await tronWeb.trx.sendRawTransaction( - signed - ); - await assertSuccess(tronWeb, result, 'Could not upgrade direct deposit queue proxy'); - console.log("Upgraded direct deposit queue proxy"); - // 5. Deploy MPCGuard with process.env.RELAYER as operator await deployer.deploy( MPCGuard, @@ -262,6 +230,4 @@ module.exports = async function(deployer) { console.log('MPCGuard: ', tronWeb.address.fromHex(mpcGuard.address)); console.log('Operator: ', tronWeb.address.fromHex(operatorManager.address)); - console.log('Pool: ', tronWeb.address.fromHex(poolProxy.address)); - console.log('Direct deposit queue: ', tronWeb.address.fromHex(process.env.QUEUE_PROXY)); }; diff --git a/truffle-config.js b/truffle-config.js index 86da8fd..5bcfba8 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -30,13 +30,13 @@ Then, run the migration with: nile: { privateKey: process.env.PRIVATE_KEY_NILE, userFeePercentage: 100, - feeLimit: 1000 * 1e6, + feeLimit: 20000 * 1e6, fullHost: 'https://nile.trongrid.io', network_id: '3' }, development: { // For tronbox/tre docker image - privateKey: '0000000000000000000000000000000000000000000000000000000000000001', + privateKey: '55fffd557ffbce4a378f0976776b84ca65e87e0f47b0ca4f3f58611165c64d3c', userFeePercentage: 0, feeLimit: 6000 * 1e6, fullHost: 'http://127.0.0.1:' + port, From e6a3a8f8a03bdff79f36d175a542d4b29ba7157b Mon Sep 17 00:00:00 2001 From: vladimir Date: Wed, 27 Dec 2023 18:26:45 +0300 Subject: [PATCH 08/17] zero address workaround --- migrations/2_pool_impl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrations/2_pool_impl.js b/migrations/2_pool_impl.js index 372647b..68d5ff0 100644 --- a/migrations/2_pool_impl.js +++ b/migrations/2_pool_impl.js @@ -12,7 +12,7 @@ var ZkBobPoolERC20 = artifacts.require("ZkBobPoolERC20"); module.exports = async function(deployer) { const usdt = TronWeb.address.fromHex(process.env.TOKEN); // 410000000000000000000000000000000000000000 does not work for some reason - const zeroAddress = '410000000000000000000000000000000000000000'; + const zeroAddress = TronWeb.address.fromHex('410000000000000000000000000000000000000001'); // 1. Deploy verifiers var transferVerifier; From f9c5b8f310c743e0c55921a7fcae3c4b3573d4f6 Mon Sep 17 00:00:00 2001 From: vladimir Date: Thu, 28 Dec 2023 00:55:32 +0300 Subject: [PATCH 09/17] return dd, update from contracts repo --- contracts/src/zkbob/ZkBobPool.sol | 1 + contracts/src/zkbob/manager/MPCGuard.sol | 17 +++++++----- migrations/3_pool.js | 33 +++++++++++++++++++++++- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/contracts/src/zkbob/ZkBobPool.sol b/contracts/src/zkbob/ZkBobPool.sol index ef9892b..4cd988a 100644 --- a/contracts/src/zkbob/ZkBobPool.sol +++ b/contracts/src/zkbob/ZkBobPool.sol @@ -73,6 +73,7 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk // require(Address.isContract(address(_transfer_verifier)), "ZkBobPool: not a contract"); // require(Address.isContract(address(_tree_verifier)), "ZkBobPool: not a contract"); // require(Address.isContract(address(_batch_deposit_verifier)), "ZkBobPool: not a contract"); + require(Address.isContract(_direct_deposit_queue), "ZkBobPool: not a contract"); pool_id = __pool_id; token = _token; transfer_verifier = _transfer_verifier; diff --git a/contracts/src/zkbob/manager/MPCGuard.sol b/contracts/src/zkbob/manager/MPCGuard.sol index 5258bf9..4c8f9ea 100644 --- a/contracts/src/zkbob/manager/MPCGuard.sol +++ b/contracts/src/zkbob/manager/MPCGuard.sol @@ -9,9 +9,9 @@ import "../utils/CustomABIDecoder.sol"; import "../../interfaces/IZkBobPool.sol"; contract MPCGuard is Ownable, CustomABIDecoder { - address[] public guards; + address[] private guards; - address public operator; + address operator; address public immutable pool; @@ -39,6 +39,9 @@ contract MPCGuard is Ownable, CustomABIDecoder { } function setGuards(address[] calldata _guards) external onlyOwner { + for (uint256 i = 0; i < _guards.length - 1; i++) { + require(_guards[i] > _guards[i + 1], "must be sorted in descending order"); + } guards = _guards; } @@ -105,18 +108,18 @@ contract MPCGuard is Ownable, CustomABIDecoder { ZkBobPool poolContract = ZkBobPool(pool); - bytes memory mpc_message = abi.encodePacked( + bytes memory mpc_message = abi.encodeWithSelector( ZkBobPool(pool).appendDirectDeposits.selector, _root_after, _indices, _out_commit, _batch_deposit_proof, - _tree_proof, - poolContract.roots(poolContract.pool_index()), - poolContract.pool_id() + _tree_proof ); + uint256 currentRoot = poolContract.roots(poolContract.pool_index()); + bytes32 challenge = digest(abi.encodePacked(mpc_message, currentRoot, poolContract.pool_id())); - require(checkQuorum(signatures, digest(mpc_message))); + require(checkQuorum(signatures, challenge)); IZkBobPool(pool).appendDirectDeposits(_root_after, _indices, _out_commit, _batch_deposit_proof, _tree_proof); } diff --git a/migrations/3_pool.js b/migrations/3_pool.js index e4c6066..398a12b 100644 --- a/migrations/3_pool.js +++ b/migrations/3_pool.js @@ -166,7 +166,38 @@ module.exports = async function(deployer) { var result = await tronWeb.trx.sendRawTransaction(signed); await assertSuccess(tronWeb, result, 'Could not initialize pool'); console.log("Initialized pool"); - + // 3. Deploy direct deposit queue implementation + await deployer.deploy(Base58); + await deployer.link(Base58, ZkAddress); + await deployer.deploy(ZkAddress); + await deployer.link(ZkAddress, ZkBobDirectDepositQueue); + await deployer.deploy( + ZkBobDirectDepositQueue, + TronWeb.address.fromHex(poolProxy.address), + usdt, + 1, + ); + const queueImpl = await ZkBobDirectDepositQueue.deployed(); + + // 4. Upgrade direct deposit queue proxy + transaction = await tronWeb.transactionBuilder.triggerSmartContract( + process.env.QUEUE_PROXY, + 'upgradeTo(address)', + {}, + [ + {'type': 'address', 'value': queueImpl.address}, + ] + ); + signed = await tronWeb.trx.sign( + transaction.transaction, + deployer.options.options.privateKey, + ); + result = await tronWeb.trx.sendRawTransaction( + signed + ); + await assertSuccess(tronWeb, result, 'Could not upgrade direct deposit queue proxy'); + console.log("Upgraded direct deposit queue proxy"); + // 5. Deploy MPCGuard with process.env.RELAYER as operator await deployer.deploy( MPCGuard, From 3014c57b94fe80e5411be8383a3735caa809363c Mon Sep 17 00:00:00 2001 From: vladimir Date: Thu, 28 Dec 2023 01:04:45 +0300 Subject: [PATCH 10/17] remove legacy migrations --- 1_initial_migration.js | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 1_initial_migration.js diff --git a/1_initial_migration.js b/1_initial_migration.js deleted file mode 100644 index 94323b1..0000000 --- a/1_initial_migration.js +++ /dev/null @@ -1,7 +0,0 @@ -var Migrations = artifacts.require("Migrations"); - -module.exports = async function(deployer) { - await deployer.deploy(Migrations); - const result = await Migrations.deployed(); - console.log('Migrations: ', result.address); -}; From 8c82987ec3ecb06735aaca281de8e0672b587698 Mon Sep 17 00:00:00 2001 From: vladimir Date: Thu, 28 Dec 2023 01:47:08 +0300 Subject: [PATCH 11/17] ditch safeTransfer --- contracts/src/zkbob/ZkBobPool.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/src/zkbob/ZkBobPool.sol b/contracts/src/zkbob/ZkBobPool.sol index 4cd988a..073f623 100644 --- a/contracts/src/zkbob/ZkBobPool.sol +++ b/contracts/src/zkbob/ZkBobPool.sol @@ -231,7 +231,7 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk if (txType == 0) { // Deposit require(transfer_token_delta > 0 && energy_amount == 0, "ZkBobPool: incorrect deposit amounts"); - IERC20(token).safeTransferFrom(user, address(this), uint256(token_amount) * TOKEN_DENOMINATOR); + IERC20(token).transferFrom(user, address(this), uint256(token_amount) * TOKEN_DENOMINATOR); } else if (txType == 1) { // Transfer require(token_amount == 0 && energy_amount == 0, "ZkBobPool: incorrect transfer amounts"); @@ -247,7 +247,7 @@ abstract contract ZkBobPool is IZkBobPool, EIP1967Admin, Ownable, Parameters, Zk } if (withdraw_amount > 0) { - IERC20(token).safeTransfer(user, withdraw_amount); + IERC20(token).transfer(user, withdraw_amount); } // energy withdrawals are not yet implemented, any transaction with non-zero energy_amount will revert From 692cf51e710ce9a4a73c0700069a413f3b4e1aba Mon Sep 17 00:00:00 2001 From: vladimir Date: Thu, 28 Dec 2023 13:43:21 +0300 Subject: [PATCH 12/17] move limits to envs --- .env.example | 10 ++++++++++ migrations/3_pool.js | 16 ++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index bf8d961..f0ab030 100644 --- a/.env.example +++ b/.env.example @@ -25,6 +25,16 @@ export PRIVATE_KEY= export PRIVATE_KEY_NILE= export PRIVATE_KEY_MAINNET= +#limits +export POOL_CAP=144115188075855871 +export DAILY_DEPOSIT_CAP=8589934591 +export DAILY_WITHDRAWAL_CAP=8589934591 +export DAILY_USER_DEPOSIT_CAP=8589934591 +export DEPOSIT_CAP=8589934591 +export DAILY_USER_DIRECT_DEPOSIT_CAP=0 +export DIRECT_DEPOSIT_CAP=0 + + # this values should be filled after 2_pool_impl.js migration export POOL_IMPL= export QUEUE_PROXY= diff --git a/migrations/3_pool.js b/migrations/3_pool.js index 398a12b..25e0448 100644 --- a/migrations/3_pool.js +++ b/migrations/3_pool.js @@ -138,13 +138,13 @@ module.exports = async function(deployer) { const params = encodeParams([ {type: 'uint256', value: process.env.INITIAL_ROOT}, // zkBobInitialRoot - {type: 'uint256', value: '144115188075855871'}, // zkBobPoolCap - {type: 'uint256', value: '8589934591'}, // zkBobDailyDepositCap - {type: 'uint256', value: '8589934591'}, // zkBobDailyWithdrawalCap - {type: 'uint256', value: '8589934591'}, // zkBobDailyUserDepositCap - {type: 'uint256', value: '8589934591'}, // zkBobDepositCap - {type: 'uint256', value: '0'}, // zkBobDailyUserDirectDepositCap - {type: 'uint256', value: '0'}, // zkBobDirectDepositCap + {type: 'uint256', value: process.env.POOL_CAP}, // zkBobPoolCap + {type: 'uint256', value: process.env.DAILY_DEPOSIT_CAP}, // zkBobDailyDepositCap + {type: 'uint256', value: process.env.DAILY_WITHDRAWAL_CAP}, // zkBobDailyWithdrawalCap + {type: 'uint256', value: process.env.DAILY_USER_DEPOSIT_CAP}, // zkBobDailyUserDepositCap + {type: 'uint256', value: process.env.DEPOSIT_CAP}, // zkBobDepositCap + {type: 'uint256', value: process.env.DAILY_USER_DIRECT_DEPOSIT_CAP}, // zkBobDailyUserDirectDepositCap + {type: 'uint256', value: process.env.DIRECT_DEPOSIT_CAP}, // zkBobDirectDepositCap ]); // 2. Initialize pool @@ -197,7 +197,7 @@ module.exports = async function(deployer) { ); await assertSuccess(tronWeb, result, 'Could not upgrade direct deposit queue proxy'); console.log("Upgraded direct deposit queue proxy"); - + // 5. Deploy MPCGuard with process.env.RELAYER as operator await deployer.deploy( MPCGuard, From c5ffa59c710bee53a2baa0ac38cb4afe3e6e3bb1 Mon Sep 17 00:00:00 2001 From: vladimir Date: Thu, 28 Dec 2023 16:54:13 +0300 Subject: [PATCH 13/17] optional token seller --- migrations/3_pool.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/migrations/3_pool.js b/migrations/3_pool.js index 25e0448..00d21b2 100644 --- a/migrations/3_pool.js +++ b/migrations/3_pool.js @@ -57,6 +57,22 @@ async function setGuards(deployer, tronWeb, mpcGuard) { ); return await tronWeb.trx.sendRawTransaction(signed); } +async function setTokenSeller(deployer, tronWeb, pool,seller) { + + var transaction = await tronWeb.transactionBuilder.triggerSmartContract( + pool, + 'setTokenSeller(address)', + {}, + [ + {type: 'address', value: seller}, + ], + ); + var signed = await tronWeb.trx.sign( + transaction.transaction, + deployer.options.options.privateKey, + ); + return await tronWeb.trx.sendRawTransaction(signed); +} async function setOperatorManager(deployer, tronWeb, contractAddress, operatorManager) { var transaction = await tronWeb.transactionBuilder.triggerSmartContract( @@ -229,6 +245,16 @@ module.exports = async function(deployer) { await assertSuccess(tronWeb, result, 'Could not set operator manager for pool proxy'); console.log("Set operator manager for pool proxy to " + TronWeb.address.fromHex(operatorManager.address)); + // config token seller + if (process.env.ROUTER && process.env.QUOTER) { + const fee = process.env.POOL_FEE; + const swapRouter = TronWeb.address.toHex(process.env.ROUTER); + const quoter = TronWeb.address.toHex(process.env.QUOTER); + + await deployer.deploy(UniswapV3Seller, swapRouter, quoter, usdt, fee, '410000000000000000000000000000000000000000', 0); + const seller = await UniswapV3Seller.deployed(); + await setTokenSeller(deployer, tronWeb, poolProxy.address, seller.address); + } // 9. Transfer ownership if (process.env.OWNER && tronWeb.address.toHex(process.env.OWNER) != deployerAddress) { result = await transferOwnership(deployer, tronWeb, poolProxy.address, process.env.OWNER); From 521794515c09e80388e18552c0954046836eb2ac Mon Sep 17 00:00:00 2001 From: vladimir Date: Fri, 29 Dec 2023 20:22:14 +0300 Subject: [PATCH 14/17] use transfer in UniswapSeller --- contracts/src/utils/UniswapV3Seller.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/utils/UniswapV3Seller.sol b/contracts/src/utils/UniswapV3Seller.sol index 7077750..b02200d 100644 --- a/contracts/src/utils/UniswapV3Seller.sol +++ b/contracts/src/utils/UniswapV3Seller.sol @@ -92,7 +92,7 @@ contract UniswapV3Seller is ITokenSeller { uint256 remainingBalance = IERC20(token0).balanceOf(address(this)); if (remainingBalance + _amount > balance) { uint256 refund = remainingBalance + _amount - balance; - IERC20(token0).safeTransfer(msg.sender, refund); + IERC20(token0).transfer(msg.sender, refund); return (amountOut, refund); } return (amountOut, 0); From e668bb0ade4828d440a0361089221a48248d609c Mon Sep 17 00:00:00 2001 From: vladimir Date: Wed, 3 Jan 2024 13:27:16 +0300 Subject: [PATCH 15/17] fix TokenSellerMixin --- contracts/src/zkbob/ZkBobTokenSellerMixin.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/zkbob/ZkBobTokenSellerMixin.sol b/contracts/src/zkbob/ZkBobTokenSellerMixin.sol index 144e4ad..440a802 100644 --- a/contracts/src/zkbob/ZkBobTokenSellerMixin.sol +++ b/contracts/src/zkbob/ZkBobTokenSellerMixin.sol @@ -28,7 +28,7 @@ abstract contract ZkBobTokenSellerMixin is ZkBobPool { function _withdrawNative(address _user, uint256 _tokenAmount) internal override returns (uint256) { ITokenSeller seller = tokenSeller; if (address(seller) != address(0)) { - IERC20(token).safeTransfer(address(seller), _tokenAmount); + IERC20(token).transfer(address(seller), _tokenAmount); (, uint256 refunded) = seller.sellForETH(_user, _tokenAmount); return _tokenAmount - refunded; } From e675e046d34758992c79ece42ba5f3080b419288 Mon Sep 17 00:00:00 2001 From: vladimir Date: Wed, 3 Jan 2024 13:31:25 +0300 Subject: [PATCH 16/17] update impl script --- migrations/5_update_impl.js | 89 +++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 migrations/5_update_impl.js diff --git a/migrations/5_update_impl.js b/migrations/5_update_impl.js new file mode 100644 index 0000000..3f7d8ca --- /dev/null +++ b/migrations/5_update_impl.js @@ -0,0 +1,89 @@ +const TronWeb = require("tronweb"); + +var EIP1967Proxy = artifacts.require("EIP1967Proxy"); +var TransferVerifierProd = artifacts.require("TransferVerifier.sol"); +var TreeUpdateVerifierProd = artifacts.require("TreeUpdateVerifier.sol"); +var DelegatedDepositVerifierProd = artifacts.require( + "DelegatedDepositVerifier.sol" +); +var TransferVerifierStage = artifacts.require("TransferVerifierStage.sol"); +var TreeUpdateVerifierStage = artifacts.require("TreeUpdateVerifierStage.sol"); +var DelegatedDepositVerifierStage = artifacts.require( + "DelegatedDepositVerifierStage.sol" +); +var ZkBobPoolERC20 = artifacts.require("ZkBobPoolERC20"); + +const poolAbi = require("./abi.json"); + +module.exports = async function (deployer) { + const usdt = TronWeb.address.fromHex(process.env.TOKEN); + // 410000000000000000000000000000000000000000 does not work for some reason + const zeroAddress = TronWeb.address.fromHex( + "410000000000000000000000000000000000000001" + ); + const tronWeb = new TronWeb( + { + fullNode: deployer.options.options.fullHost, + solidityNode: deployer.options.options.fullHost, + }, + deployer.options.options.privateKey + ); + const poolProxy = tronWeb.contract(poolAbi, process.env.POOL_PROXY); + + // // 1. Deploy verifiers + var transferVerifier; + var treeUpdateVerifier; + var delegatedDepositVerifier; + if (process.env.VERIFIERS.toLowerCase() == "prod") { + console.log("Deploying prod verifiers"); + await deployer.deploy(TransferVerifierProd); + transferVerifier = await TransferVerifierProd.deployed().address; + await deployer.deploy(TreeUpdateVerifierProd); + treeUpdateVerifier = await TreeUpdateVerifierProd.deployed().address; + await deployer.deploy(DelegatedDepositVerifierProd); + delegatedDepositVerifier = await DelegatedDepositVerifierProd.deployed() + .address; + } else if (process.env.VERIFIERS.toLowerCase() == "offchain") { + console.log("Using offchain verifiers"); + // We don't deploy verifiers in this case + // We assume that they are not used in the pool contract + transferVerifier = zeroAddress; + treeUpdateVerifier = zeroAddress; + delegatedDepositVerifier = zeroAddress; + } else { + console.log("Deploying stage verifiers"); + await deployer.deploy(TransferVerifierStage); + transferVerifier = await TransferVerifierStage.deployed().address; + await deployer.deploy(TreeUpdateVerifierStage); + treeUpdateVerifier = await TreeUpdateVerifierStage.deployed().address; + await deployer.deploy(DelegatedDepositVerifierStage); + delegatedDepositVerifier = await DelegatedDepositVerifierStage.deployed() + .address; + } + + // 2. Get direct deposit queue proxy + + const queueProxy = await poolProxy["direct_deposit_queue"]().call(); + console.log("queueProxy:", queueProxy); + +// 3. Deploy new pool implementation + await deployer.deploy( + ZkBobPoolERC20, + process.env.POOL_ID, + usdt, + transferVerifier, + treeUpdateVerifier, + delegatedDepositVerifier, + queueProxy, + zeroAddress, + 1, + 1000000 + ); + const newPoolImpl = await ZkBobPoolERC20.deployed(); + + console.log( + "New pool implementation: ", + TronWeb.address.fromHex(newPoolImpl.address) + ); + +}; From 36efa399e0ce0262794a8a3335724298b7d32fce Mon Sep 17 00:00:00 2001 From: vladimir Date: Wed, 3 Jan 2024 13:31:41 +0300 Subject: [PATCH 17/17] update impl script --- migrations/abi.json | 617 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 617 insertions(+) create mode 100644 migrations/abi.json diff --git a/migrations/abi.json b/migrations/abi.json new file mode 100644 index 0000000..cdc2c98 --- /dev/null +++ b/migrations/abi.json @@ -0,0 +1,617 @@ +[ + { + "inputs": [ + { "internalType": "uint256", "name": "__pool_id", "type": "uint256" }, + { "internalType": "address", "name": "_token", "type": "address" }, + { + "internalType": "contract ITransferVerifier", + "name": "_transfer_verifier", + "type": "address" + }, + { + "internalType": "contract ITreeVerifier", + "name": "_tree_verifier", + "type": "address" + }, + { + "internalType": "contract IBatchDepositVerifier", + "name": "_batch_deposit_verifier", + "type": "address" + }, + { + "internalType": "address", + "name": "_direct_deposit_queue", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "hash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "Message", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "manager", + "type": "address" + } + ], + "name": "UpdateKYCProvidersManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint8", + "name": "tier", + "type": "uint8" + }, + { + "components": [ + { "internalType": "uint56", "name": "tvlCap", "type": "uint56" }, + { + "internalType": "uint32", + "name": "dailyDepositCap", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "dailyWithdrawalCap", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "dailyUserDepositCap", + "type": "uint32" + }, + { "internalType": "uint32", "name": "depositCap", "type": "uint32" }, + { + "internalType": "uint32", + "name": "directDepositCap", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "dailyUserDirectDepositCap", + "type": "uint32" + } + ], + "indexed": false, + "internalType": "struct ZkBobAccounting.TierLimits", + "name": "limits", + "type": "tuple" + } + ], + "name": "UpdateLimits", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "manager", + "type": "address" + } + ], + "name": "UpdateOperatorManager", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "tier", + "type": "uint8" + } + ], + "name": "UpdateTier", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "seller", + "type": "address" + } + ], + "name": "UpdateTokenSeller", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "WithdrawFee", + "type": "event" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "accumulatedFee", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "all_messages_hash", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_root_after", "type": "uint256" }, + { "internalType": "uint256[]", "name": "_indices", "type": "uint256[]" }, + { "internalType": "uint256", "name": "_out_commit", "type": "uint256" }, + { + "internalType": "uint256[8]", + "name": "_batch_deposit_proof", + "type": "uint256[8]" + }, + { + "internalType": "uint256[8]", + "name": "_tree_proof", + "type": "uint256[8]" + } + ], + "name": "appendDirectDeposits", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "batch_deposit_verifier", + "outputs": [ + { + "internalType": "contract IBatchDepositVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "denominator", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "direct_deposit_queue", + "outputs": [ + { + "internalType": "contract IZkBobDirectDepositQueue", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "fillMigrationOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_user", "type": "address" } + ], + "name": "getLimitsFor", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "tvlCap", "type": "uint256" }, + { "internalType": "uint256", "name": "tvl", "type": "uint256" }, + { + "internalType": "uint256", + "name": "dailyDepositCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dailyDepositCapUsage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dailyWithdrawalCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dailyWithdrawalCapUsage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dailyUserDepositCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dailyUserDepositCapUsage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "depositCap", + "type": "uint256" + }, + { "internalType": "uint8", "name": "tier", "type": "uint8" }, + { + "internalType": "uint256", + "name": "dailyUserDirectDepositCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dailyUserDirectDepositCapUsage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "directDepositCap", + "type": "uint256" + } + ], + "internalType": "struct ZkBobAccounting.Limits", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_root", "type": "uint256" }, + { "internalType": "uint256", "name": "_tvlCap", "type": "uint256" }, + { + "internalType": "uint256", + "name": "_dailyDepositCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_dailyWithdrawalCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_dailyUserDepositCap", + "type": "uint256" + }, + { "internalType": "uint256", "name": "_depositCap", "type": "uint256" }, + { + "internalType": "uint256", + "name": "_dailyUserDirectDepositCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_directDepositCap", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "kycProvidersManager", + "outputs": [ + { + "internalType": "contract IKycProvidersManager", + "name": "res", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "nullifiers", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "operatorManager", + "outputs": [ + { + "internalType": "contract IOperatorManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pool_id", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pool_index", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_sender", "type": "address" }, + { "internalType": "uint256", "name": "_amount", "type": "uint256" } + ], + "name": "recordDirectDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint8", "name": "_tier", "type": "uint8" }], + "name": "resetDailyLimits", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "roots", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IKycProvidersManager", + "name": "_kycProvidersManager", + "type": "address" + } + ], + "name": "setKycProvidersManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint8", "name": "_tier", "type": "uint8" }, + { "internalType": "uint256", "name": "_tvlCap", "type": "uint256" }, + { + "internalType": "uint256", + "name": "_dailyDepositCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_dailyWithdrawalCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_dailyUserDepositCap", + "type": "uint256" + }, + { "internalType": "uint256", "name": "_depositCap", "type": "uint256" }, + { + "internalType": "uint256", + "name": "_dailyUserDirectDepositCap", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_directDepositCap", + "type": "uint256" + } + ], + "name": "setLimits", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IOperatorManager", + "name": "_operatorManager", + "type": "address" + } + ], + "name": "setOperatorManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_seller", "type": "address" } + ], + "name": "setTokenSeller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint8", "name": "_tier", "type": "uint8" }, + { "internalType": "address[]", "name": "_users", "type": "address[]" } + ], + "name": "setUsersTier", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokenSeller", + "outputs": [ + { "internalType": "contract ITokenSeller", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "transact", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "transfer_verifier", + "outputs": [ + { + "internalType": "contract ITransferVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tree_verifier", + "outputs": [ + { + "internalType": "contract ITreeVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_operator", "type": "address" }, + { "internalType": "address", "name": "_to", "type": "address" } + ], + "name": "withdrawFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] + \ No newline at end of file