diff --git a/package.json b/package.json index 9c6527dd5..c73e9888d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "build": "yarn compile && yarn build:packages", "setup": "yarn compile && yarn build:circuits && yarn build:ptau", "test": "yarn workspace @webb-tools/contracts run test", - "fast": "yarn workspace @webb-tools/contracts run fast", + "fast": "yarn workspace @webb-tools/contracts run test:parallel", "build:circuits": "./scripts/bash/build_circuits.sh", "build:packages": "lerna run build", "build:ptau": "./scripts/bash/generate_phase1_ptau.sh", @@ -20,7 +20,6 @@ "clean:packages": "lerna run clean", "fetch:fixtures": "cd packages/contracts/solidity-fixtures && dvc pull && cd ../../../", "prettier": "prettier -c .", - "prettier:write": "prettier -w .", "publish:packages": "lerna run compile && lerna publish", "setup:groth16:vanchor2": "./scripts/bash/groth16/vanchor/phase2_poseidon_vanchor2.sh", "setup:groth16:vanchor8": "./scripts/bash/groth16/vanchor/phase2_poseidon_vanchor8.sh", @@ -34,20 +33,20 @@ "setup:plonk:identity-vanchor8": "./scripts/bash/plonk/identity_vanchor/phase2_identity_vanchor8.sh", "setup:plonk": "./scripts/bash/setup_verifiers_plonk.sh", "ts-check": "tsc --noEmit -p ./tsconfig.json", - "format": "prettier --write \"packages/**/*.{ts,js,jsx,tsx}\"" + "format": "prettier -w ." }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-truffle5": "^2.0.0", "@nomiclabs/hardhat-web3": "^2.0.0", - "@webb-tools/anchors": "^0.5.3", - "@webb-tools/bridges": "^0.5.3", - "@webb-tools/contracts": "^0.5.3", - "@webb-tools/evm-test-utils": "^0.5.3", - "@webb-tools/interfaces": "^0.5.3", - "@webb-tools/tokens": "^0.5.3", - "@webb-tools/utils": "^0.5.3", - "@webb-tools/vbridge": "^0.5.3", + "@webb-tools/anchors": "^0.5", + "@webb-tools/bridges": "^0.5", + "@webb-tools/contracts": "^0.5", + "@webb-tools/evm-test-utils": "^0.5", + "@webb-tools/interfaces": "^0.5", + "@webb-tools/tokens": "^0.5", + "@webb-tools/utils": "^0.5", + "@webb-tools/vbridge": "^0.5", "copyfiles": "^2.4.1", "eth-proof": "~2.1.6", "ethers": "5.7.0", diff --git a/packages/contracts/contracts/DeterministicDeployFactory.sol b/packages/contracts/contracts/DeterministicDeployFactory.sol index baa1ba3ba..de8344714 100644 --- a/packages/contracts/contracts/DeterministicDeployFactory.sol +++ b/packages/contracts/contracts/DeterministicDeployFactory.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; contract DeterministicDeployFactory { event Deploy(address addr); - function deploy(bytes memory bytecode, uint _salt) external { + function deploy(bytes memory bytecode, uint _salt) external returns (address) { address addr; assembly { addr := create2(0, add(bytecode, 0x20), mload(bytecode), _salt) @@ -14,5 +14,65 @@ contract DeterministicDeployFactory { } emit Deploy(addr); + return addr; + } + + /** + @notice Deploy a fungible token + @param bytecode The bytecode of the contract + @param _salt The salt for the contract + @param _feePercentage The fee percentage for wrapping + @param _feeRecipient The recipient for fees from wrapping. + @param _handler The address of the handler + @param _limit The maximum amount of tokens that can be wrapped + @param _isNativeAllowed Whether or not native tokens are allowed to be wrapped + */ + function deployFungibleToken( + bytes memory bytecode, + uint _salt, + uint16 _feePercentage, + address _feeRecipient, + address _handler, + uint256 _limit, + bool _isNativeAllowed + ) external { + address c = this.deploy(bytecode, _salt); + // delegate call initialize the contract created with the msg.sender + (bool success, bytes memory data) = c.call( + abi.encodeWithSignature( + "initialize(uint16,address,address,uint256,bool)", + _feePercentage, + _feeRecipient, + _handler, + _limit, + _isNativeAllowed + ) + ); + require(success, string(data)); + } + + /** + @notice Deploy a VAnchor + @param bytecode The bytecode of the contract + @param _salt The salt for the contract + @param _minimalWithdrawalAmount The minimal withdrawal amount + @param _maximumDepositAmount The maximum deposit amount + */ + function deployVAnchor( + bytes memory bytecode, + uint _salt, + uint256 _minimalWithdrawalAmount, + uint256 _maximumDepositAmount + ) external { + address c = this.deploy(bytecode, _salt); + // delegate call initialize the contract created with the msg.sender + (bool success, bytes memory data) = c.call( + abi.encodeWithSignature( + "initialize(uint256,uint256)", + _minimalWithdrawalAmount, + _maximumDepositAmount + ) + ); + require(success, string(data)); } } diff --git a/packages/contracts/contracts/anchors/LinkableAnchor.sol b/packages/contracts/contracts/anchors/LinkableAnchor.sol index 20b1a764b..70dc8ae62 100644 --- a/packages/contracts/contracts/anchors/LinkableAnchor.sol +++ b/packages/contracts/contracts/anchors/LinkableAnchor.sol @@ -66,6 +66,14 @@ abstract contract LinkableAnchor is event EdgeAddition(uint256 chainID, uint256 latestLeafIndex, uint256 merkleRoot); event EdgeUpdate(uint256 chainID, uint256 latestLeafIndex, uint256 merkleRoot); + /** + @notice Checks the sender is the AnchorHandler configured on this contract + */ + modifier onlyHandler() { + require(msg.sender == handler, "sender is not the handler"); + _; + } + /** @notice The LinkableAnchor constructor @param _handler The address of the `AnchorHandler` contract @@ -227,14 +235,6 @@ abstract contract LinkableAnchor is return true; } - /** - @notice Checks the sender is the AnchorHandler configured on this contract - */ - modifier onlyHandler() { - require(msg.sender == handler, "sender is not the handler"); - _; - } - /** @notice Checks the `_chainID` has an edge on this contract */ diff --git a/packages/contracts/contracts/interfaces/tokens/IMultiTokenManager.sol b/packages/contracts/contracts/interfaces/tokens/IMultiTokenManager.sol index 0267843ba..703294ec2 100644 --- a/packages/contracts/contracts/interfaces/tokens/IMultiTokenManager.sol +++ b/packages/contracts/contracts/interfaces/tokens/IMultiTokenManager.sol @@ -26,6 +26,7 @@ interface IMultiTokenManager { @param _limit The maximum amount of tokens that can be wrapped @param _feePercentage The fee percentage for wrapping @param _isNativeAllowed Whether or not native tokens are allowed to be wrapped + @param _admin The address of the admin who will receive minting rights and admin role */ function registerToken( address _handler, @@ -34,7 +35,8 @@ interface IMultiTokenManager { bytes32 _salt, uint256 _limit, uint16 _feePercentage, - bool _isNativeAllowed + bool _isNativeAllowed, + address _admin ) external returns (address); /** diff --git a/packages/contracts/contracts/tokens/AaveTokenWrapper.sol b/packages/contracts/contracts/tokens/AaveTokenWrapper.sol index e686a200b..c2aab0072 100644 --- a/packages/contracts/contracts/tokens/AaveTokenWrapper.sol +++ b/packages/contracts/contracts/tokens/AaveTokenWrapper.sol @@ -26,29 +26,28 @@ contract AaveTokenWrapper is FungibleTokenWrapper, IAaveTokenWrapper { string memory _name, string memory _symbol, address _aaveLendingPool - ) FungibleTokenWrapper(_name, _symbol) { - aaveLendingPool = IAaveLendingPool(_aaveLendingPool); - } + ) FungibleTokenWrapper(_name, _symbol) {} /** @notice AaveTokenWrapper initializer - @param _feeRecipient The recipient for fees from wrapping - @param _governor The address of the governor + @param _feePercentage The fee percentage for wrapping + @param _feeRecipient The recipient for fees from wrapping. + @param _handler The address of the handler @param _limit The maximum amount of tokens that can be wrapped @param _isNativeAllowed Whether or not native tokens are allowed to be wrapped + @param _admin The address of the admin who will receive minting rights and admin role + @param _aaveLendingPool The address of the Aave lending pool */ function initialize( + uint16 _feePercentage, address _feeRecipient, - address _governor, + address _handler, uint256 _limit, bool _isNativeAllowed, + address _admin, address _aaveLendingPool - ) public { - require(!initialized, "Contract already initialized"); - feeRecipient = payable(_feeRecipient); - wrappingLimit = _limit; - isNativeAllowed = _isNativeAllowed; - initialized = true; + ) public onlyUninitialized { + super.initialize(_feePercentage, _feeRecipient, _handler, _limit, _isNativeAllowed, _admin); aaveLendingPool = IAaveLendingPool(_aaveLendingPool); } diff --git a/packages/contracts/contracts/tokens/FungibleTokenWrapper.sol b/packages/contracts/contracts/tokens/FungibleTokenWrapper.sol index 65ff37a50..047e1cfad 100644 --- a/packages/contracts/contracts/tokens/FungibleTokenWrapper.sol +++ b/packages/contracts/contracts/tokens/FungibleTokenWrapper.sol @@ -18,8 +18,8 @@ import "../utils/ProposalNonceTracker.sol"; TokenHandler contract. */ contract FungibleTokenWrapper is - TokenWrapper, Initialized, + TokenWrapper, IFungibleTokenWrapper, ProposalNonceTracker { @@ -48,14 +48,17 @@ contract FungibleTokenWrapper is @param _handler The address of the handler @param _limit The maximum amount of tokens that can be wrapped @param _isNativeAllowed Whether or not native tokens are allowed to be wrapped + @param _admin The address of the admin who will receive minting rights and admin role */ function initialize( uint16 _feePercentage, address _feeRecipient, address _handler, uint256 _limit, - bool _isNativeAllowed + bool _isNativeAllowed, + address _admin ) public onlyUninitialized { + super._initialize(_admin); initialized = true; feePercentage = _feePercentage; feeRecipient = payable(_feeRecipient); diff --git a/packages/contracts/contracts/tokens/MultiFungibleTokenManager.sol b/packages/contracts/contracts/tokens/MultiFungibleTokenManager.sol index c55f670b7..114298974 100644 --- a/packages/contracts/contracts/tokens/MultiFungibleTokenManager.sol +++ b/packages/contracts/contracts/tokens/MultiFungibleTokenManager.sol @@ -25,6 +25,7 @@ contract MultiFungibleTokenManager is MultiTokenManagerBase { @param _limit The maximum amount of tokens that can be wrapped @param _feePercentage The fee percentage for wrapping @param _isNativeAllowed Whether or not native tokens are allowed to be wrapped + @param _admin The address of the admin who will receive minting rights and admin role */ function registerToken( address _handler, @@ -33,7 +34,8 @@ contract MultiFungibleTokenManager is MultiTokenManagerBase { bytes32 _salt, uint256 _limit, uint16 _feePercentage, - bool _isNativeAllowed + bool _isNativeAllowed, + address _admin ) external override onlyRegistry onlyInitialized returns (address) { FungibleTokenWrapper token = new FungibleTokenWrapper{ salt: _salt }(_name, _symbol); @@ -42,7 +44,8 @@ contract MultiFungibleTokenManager is MultiTokenManagerBase { payable(masterFeeRecipient), _handler, _limit, - _isNativeAllowed + _isNativeAllowed, + _admin ); wrappedTokens.push(address(token)); diff --git a/packages/contracts/contracts/tokens/MultiNftTokenManager.sol b/packages/contracts/contracts/tokens/MultiNftTokenManager.sol index a9fa76ecb..e943e43f0 100644 --- a/packages/contracts/contracts/tokens/MultiNftTokenManager.sol +++ b/packages/contracts/contracts/tokens/MultiNftTokenManager.sol @@ -23,7 +23,8 @@ contract MultiNftTokenManager is MultiTokenManagerBase { bytes32, uint256, uint16, - bool + bool, + address ) public view override onlyRegistry onlyInitialized returns (address) { revert(); } diff --git a/packages/contracts/contracts/tokens/Registry.sol b/packages/contracts/contracts/tokens/Registry.sol index 0024473b0..ef335a0bc 100644 --- a/packages/contracts/contracts/tokens/Registry.sol +++ b/packages/contracts/contracts/tokens/Registry.sol @@ -90,7 +90,8 @@ contract Registry is Initialized, IRegistry, ProposalNonceTracker { _salt, _limit, _feePercentage, - _isNativeAllowed + _isNativeAllowed, + maspVAnchor ); emit TokenRegistered(token, _tokenHandler, _assetIdentifier); idToWrappedAsset[_assetIdentifier] = token; diff --git a/packages/contracts/contracts/tokens/TokenWrapper.sol b/packages/contracts/contracts/tokens/TokenWrapper.sol index 68b9fa992..70d9b73a4 100644 --- a/packages/contracts/contracts/tokens/TokenWrapper.sol +++ b/packages/contracts/contracts/tokens/TokenWrapper.sol @@ -41,6 +41,16 @@ abstract contract TokenWrapper is ERC20PresetMinterPauser, ITokenWrapper { return _amountToWrap.mul(feePercentage).div(10000); } + /** + @notice Get the fee for a target amount to wrap + @param _admin the address for granting minting, pausing and admin roles at initialization + */ + function _initialize(address _admin) internal returns (uint256) { + _setupRole(MINTER_ROLE, _admin); + _setupRole(DEFAULT_ADMIN_ROLE, _admin); + _setupRole(PAUSER_ROLE, _admin); + } + /** @notice Get the amount to wrap for a target `_deposit` amount @param _deposit The deposit amount diff --git a/packages/contracts/contracts/vanchors/base/VAnchorBase.sol b/packages/contracts/contracts/vanchors/base/VAnchorBase.sol index 25a76833b..9edb5de71 100644 --- a/packages/contracts/contracts/vanchors/base/VAnchorBase.sol +++ b/packages/contracts/contracts/vanchors/base/VAnchorBase.sol @@ -75,14 +75,14 @@ abstract contract VAnchorBase is LinkableAnchor { function configureMinimalWithdrawalLimit( uint256 _minimalWithdrawalAmount, uint32 _nonce - ) public override onlyHandler onlyIncrementingByOne(_nonce) { + ) public override onlyHandler onlyIncrementingByOne(_nonce) onlyInitialized { _configureMinimalWithdrawalLimit(_minimalWithdrawalAmount); } function configureMaximumDepositLimit( uint256 _maximumDepositAmount, uint32 _nonce - ) public override onlyHandler onlyIncrementingByOne(_nonce) { + ) public override onlyHandler onlyIncrementingByOne(_nonce) onlyInitialized { _configureMaximumDepositLimit(_maximumDepositAmount); } diff --git a/packages/contracts/test/identityVAnchor/identityVAnchor.test.ts b/packages/contracts/test/identityVAnchor/identityVAnchor.test.ts index 4d164221a..43b5b8f1d 100644 --- a/packages/contracts/test/identityVAnchor/identityVAnchor.test.ts +++ b/packages/contracts/test/identityVAnchor/identityVAnchor.test.ts @@ -1398,7 +1398,8 @@ describe('IdentityVAnchor for 2 max edges', () => { dummyFeeRecipient, alice.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const groupId = BigNumber.from(99); // arbitrary diff --git a/packages/contracts/test/token/MultiTokenManager.test.ts b/packages/contracts/test/token/MultiTokenManager.test.ts index 3e984c849..4d9c1ca08 100644 --- a/packages/contracts/test/token/MultiTokenManager.test.ts +++ b/packages/contracts/test/token/MultiTokenManager.test.ts @@ -48,7 +48,8 @@ describe('MultiFungibleTokenManager', () => { salt, limit, feePercentage, - true + true, + sender.address ); await tx.wait(); const wrappedTokenAddress = await multiTokenMgr.contract.wrappedTokens(0); diff --git a/packages/contracts/test/token/Registry.test.ts b/packages/contracts/test/token/Registry.test.ts index f33f933ca..912786d78 100644 --- a/packages/contracts/test/token/Registry.test.ts +++ b/packages/contracts/test/token/Registry.test.ts @@ -72,7 +72,8 @@ describe('Registry', () => { salt, limit, feePercentage, - true + true, + sender.address ) ).to.be.revertedWith('MultiTokenManagerBase: Only registry can call this function'); }); diff --git a/packages/contracts/test/vanchor/vanchor.test.ts b/packages/contracts/test/vanchor/vanchor.test.ts index ab5e8a0d3..0cede519a 100644 --- a/packages/contracts/test/vanchor/vanchor.test.ts +++ b/packages/contracts/test/vanchor/vanchor.test.ts @@ -47,7 +47,7 @@ const path = require('path'); const snarkjs = require('snarkjs'); const { toBN } = require('web3-utils'); -describe('VAnchor for 1 max edge', () => { +describe.only('VAnchor for 1 max edge', () => { let anchor: VAnchor; const levels = 30; @@ -141,7 +141,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); @@ -1195,7 +1196,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); @@ -1270,7 +1272,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); @@ -1346,7 +1349,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); @@ -1452,7 +1456,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = 5; @@ -1609,7 +1614,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = 5; @@ -1638,7 +1644,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = 10001; @@ -1665,7 +1672,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = -1; @@ -1691,7 +1699,8 @@ describe('VAnchor for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = 2.5; diff --git a/packages/contracts/test/vanchor/vanchorForest.test.ts b/packages/contracts/test/vanchor/vanchorForest.test.ts index 9726a2fd5..df630bccc 100644 --- a/packages/contracts/test/vanchor/vanchorForest.test.ts +++ b/packages/contracts/test/vanchor/vanchorForest.test.ts @@ -145,7 +145,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); @@ -1265,7 +1266,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); @@ -1341,7 +1343,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); @@ -1418,7 +1421,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); @@ -1525,7 +1529,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = 5; @@ -1683,7 +1688,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = 5; @@ -1712,7 +1718,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = 10001; @@ -1739,7 +1746,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = -1; @@ -1765,7 +1773,8 @@ describe('VAnchorForest for 1 max edge', () => { dummyFeeRecipient, sender.address, '10000000000000000000000000', - true + true, + wallet.address ); await wrappedToken.add(token.address, (await wrappedToken.proposalNonce()).add(1)); const wrapFee = 2.5; diff --git a/packages/tokens/src/FungibleTokenWrapper.ts b/packages/tokens/src/FungibleTokenWrapper.ts index b71bd7ebd..51bc4d7c8 100644 --- a/packages/tokens/src/FungibleTokenWrapper.ts +++ b/packages/tokens/src/FungibleTokenWrapper.ts @@ -1,4 +1,4 @@ -import { ethers } from 'ethers'; +import { BigNumberish, ethers } from 'ethers'; import { getChainIdType } from '@webb-tools/utils'; import { toHex, generateFunctionSigHash } from '@webb-tools/sdk-core'; import { @@ -28,16 +28,23 @@ export class FungibleTokenWrapper { feePercentage: number, feeRecipient: string, handler: string, - limit: string, + limit: BigNumberish, isNativeAllowed: boolean, deployer: ethers.Signer ) { assert(feePercentage <= 10_000, 'feePercentage should be less than 10_000'); const factory = new FungibleTokenWrapper__factory(deployer); - const contract = await factory.deploy(name, symbol); + const contract: FungibleTokenWrapperContract = await factory.deploy(name, symbol); await contract.deployed(); // Initialize immediately after deployment as we use an intializer now - await contract.initialize(feePercentage, feeRecipient, handler, limit, isNativeAllowed); + await contract.initialize( + feePercentage, + feeRecipient, + handler, + limit, + isNativeAllowed, + await deployer.getAddress() + ); const tokenWrapper = new FungibleTokenWrapper(contract, deployer); return tokenWrapper; @@ -87,6 +94,32 @@ export class FungibleTokenWrapper { return; } + public async approve(address: string, amount: BigNumberish) { + const tx = await this.contract.approve(address, amount); + await tx.wait(); + return; + } + + public async wrap(tokenAddress: string, amount: BigNumberish) { + const tx = await this.contract.wrap(tokenAddress, amount); + await tx.wait(); + return; + } + + public async unwrap(tokenAddress: string, amount: BigNumberish) { + const tx = await this.contract.unwrap(tokenAddress, amount); + await tx.wait(); + return; + } + + public async isNativeAllowed(): Promise { + return await this.contract.isNativeAllowed(); + } + + public async canWrap(address: string): Promise { + return await this.contract.isValidToken(address); + } + public async getFeeRecipientAddress(): Promise { return await this.contract.feeRecipient(); } diff --git a/packages/tokens/src/MultiFungibleTokenManager.ts b/packages/tokens/src/MultiFungibleTokenManager.ts index 1e18f97d0..8e11ef72e 100644 --- a/packages/tokens/src/MultiFungibleTokenManager.ts +++ b/packages/tokens/src/MultiFungibleTokenManager.ts @@ -6,23 +6,26 @@ import { export class MultiFungibleTokenManager { contract: MultiFungibleTokenManagerContract; + signer: ethers.Signer; - constructor(contract: MultiFungibleTokenManagerContract) { + constructor(contract: MultiFungibleTokenManagerContract, signer: ethers.Signer) { this.contract = contract; + this.signer; } public static async createMultiFungibleTokenManager(deployer: ethers.Signer) { const factory = new MultiFungibleTokenManager__factory(deployer); const contract = await factory.deploy(); + await contract.deployed(); - const manager = new MultiFungibleTokenManager(contract); + const manager = new MultiFungibleTokenManager(contract, deployer); return manager; } public static async connect(managerAddress: string, signer: ethers.Signer) { const managerContract = MultiFungibleTokenManager__factory.connect(managerAddress, signer); - const manager = new MultiFungibleTokenManager(managerContract); + const manager = new MultiFungibleTokenManager(managerContract, signer); return manager; } @@ -48,7 +51,8 @@ export class MultiFungibleTokenManager { salt, limit, feePercentage, - isNativeAllowed + isNativeAllowed, + await this.signer.getAddress() ); await tx.wait(); } diff --git a/scripts/evm/deployments/VBridge8Side.ts b/scripts/evm/deployments/VBridge8Side.ts index 8f1f675cf..a650c73d4 100644 --- a/scripts/evm/deployments/VBridge8Side.ts +++ b/scripts/evm/deployments/VBridge8Side.ts @@ -23,13 +23,19 @@ import { chainIdTypeSepolia, walletSepolia, chainIdTypeAurora, -} from "../ethersGovernorWallets"; -import { EvmLinkedAnchor, ProposalSigningBackend } from "@webb-tools/test-utils"; -import { ContractConfig, getEvmChainConfig, writeEvmChainConfig } from "./utils"; +} from '../ethersGovernorWallets'; +import { EvmLinkedAnchor, ProposalSigningBackend } from '@webb-tools/test-utils'; +import { ContractConfig, getEvmChainConfig, writeEvmChainConfig } from './utils'; import { zip } from 'itertools'; import fs from 'fs'; -import { EndPointConfig, goerliEndPoints, moonbaseEndPoints, optimismEndPoints, polygonEndPoints, sepoliaEndPoints } from "./endPoints"; - +import { + EndPointConfig, + goerliEndPoints, + moonbaseEndPoints, + optimismEndPoints, + polygonEndPoints, + sepoliaEndPoints, +} from './endPoints'; async function deploySignatureVBridge( tokens: Record, @@ -131,41 +137,49 @@ async function run() { // [chainIdTypeMoonbase]: moonbaseEndPoints, } + const chainEndPoints: Record = { + // [chainIdTypeGoerli]: goerliEndPoints, + // [chainIdTypeSepolia]: sepoliaEndPoints, + // [chainIdTypeOptimism]: optimismEndPoints, + [chainIdTypePolygon]: polygonEndPoints, + // [chainIdTypeMoonbase]: moonbaseEndPoints, + }; + const vbridge = await deploySignatureVBridge(tokens, deployers); // print out all the info for the addresses const bridgeConfig = await vbridge.exportConfig(); - + const anchorIterable = bridgeConfig.vAnchors.values(); const bridgeSideIterable = bridgeConfig.vBridgeSides.values(); - - for (const [anchor, bridgeSide] of zip(anchorIterable, bridgeSideIterable)){ + + for (const [anchor, bridgeSide] of zip(anchorIterable, bridgeSideIterable)) { const chainId = await anchor.signer.getChainId(); const anchorContractConfig: ContractConfig = { address: anchor.contract.address.toLowerCase(), - deployedAt: anchor.contract.deployTransaction.blockNumber + deployedAt: anchor.contract.deployTransaction.blockNumber, }; const bridgeContractConfig: ContractConfig = { address: bridgeSide.contract.address.toLowerCase(), - deployedAt: bridgeSide.contract.deployTransaction.blockNumber - } - const proposalSigningBackend : ProposalSigningBackend = { type: "DKGNode", node: "tangle" } + deployedAt: bridgeSide.contract.deployTransaction.blockNumber, + }; + const proposalSigningBackend: ProposalSigningBackend = { type: 'DKGNode', node: 'tangle' }; const linkedAnchors: EvmLinkedAnchor[] = []; const typedChainId = getChainIdType(chainId); - const beneficiary = "0xf1fd5607b90f6c421db7313158baac170d4f053b"; // relayer wallet address - + const beneficiary = '0xf1fd5607b90f6c421db7313158baac170d4f053b'; // relayer wallet address + // Add all other anchors to the list of linked anchors for (const otherAnchor of Array.from(bridgeConfig.vAnchors.values())) { if (otherAnchor !== anchor) { - linkedAnchors.push({ + linkedAnchors.push({ chainId: (await otherAnchor.contract.getChainId()).toString(), address: otherAnchor.contract.address.toLowerCase(), - type: "Evm" + type: 'Evm', }); } } - const endPointConfig = endPoints[typedChainId]; + const endPointConfig = chainEndPoints[typedChainId]; // construct chain configuration const chainConfig = getEvmChainConfig( chainId, @@ -177,18 +191,17 @@ async function run() { endPointConfig, beneficiary ); - + const configString = JSON.stringify(chainConfig, null, 2); console.log(configString); // convert config to kebab case and write to json file const dirPath = `${__dirname}/relayer-config`; - writeEvmChainConfig(`${dirPath}/${endPointConfig.name}.toml`, chainConfig - ) + writeEvmChainConfig(`${dirPath}/${endPointConfig.name}.toml`, chainConfig); console.log( `Anchor ${anchorContractConfig.address} deployed at: ${anchorContractConfig.deployedAt} for chainId ${chainId}` ); - + console.log( `BridgeSide ${bridgeContractConfig.address} deployed at: ${bridgeContractConfig.deployedAt} for chain ${chainId}` ); diff --git a/scripts/evm/deployments/create2/create2Bridge.ts b/scripts/evm/deployments/create2/create2Bridge.ts index 57eab20b6..ec4eaf332 100644 --- a/scripts/evm/deployments/create2/create2Bridge.ts +++ b/scripts/evm/deployments/create2/create2Bridge.ts @@ -135,7 +135,11 @@ export class Create2VBridge { let deployer1Contract = await Deployer1.deploy(); await deployer1Contract.deployed(); deployer = new Deployer(deployer1Contract); - let vBridgeInstance = await SignatureBridgeSide.create2BridgeSide(deployer, saltHex, deployers[chainID] ); + let vBridgeInstance = await SignatureBridgeSide.create2BridgeSide( + deployer, + saltHex, + deployers[chainID] + ); const handler = await AnchorHandler.create2AnchorHandler( vBridgeInstance.contract.address, [], @@ -163,7 +167,11 @@ export class Create2VBridge { await vBridgeInstance.setTreasuryHandler(treasuryHandler); await vBridgeInstance.setTreasuryResourceWithSignature(treasury); // Create the Hasher and Verifier for the chain - const hasherInstance = await PoseidonHasher.create2PoseidonHasher(deployer, saltHex, deployers[chainID]); + const hasherInstance = await PoseidonHasher.create2PoseidonHasher( + deployer, + saltHex, + deployers[chainID] + ); const verifier = await Verifier.create2Verifier(deployer, saltHex, deployers[chainID]); let verifierInstance = verifier.contract; // Check the addresses of the asset. If it is zero, deploy a native token wrapper @@ -334,8 +342,6 @@ export class Create2VBridge { vBridgeSides: this.vBridgeSides, }; } - - } export default Create2VBridge; diff --git a/scripts/evm/deployments/create2/deploymentScript.ts b/scripts/evm/deployments/create2/deploymentScript.ts index ef7f0fd82..01268043d 100644 --- a/scripts/evm/deployments/create2/deploymentScript.ts +++ b/scripts/evm/deployments/create2/deploymentScript.ts @@ -1,8 +1,8 @@ -import { FungibleTokenWrapper } from "@webb-tools/tokens"; -import { fetchComponentsFromFilePaths, getChainIdType } from "@webb-tools/utils"; -import { DeployerConfig } from "@webb-tools/interfaces"; -import path from "path"; -import { ethers } from "ethers"; +import { FungibleTokenWrapper } from '@webb-tools/tokens'; +import { fetchComponentsFromFilePaths, getChainIdType } from '@webb-tools/utils'; +import { DeployerConfig } from '@webb-tools/interfaces'; +import path from 'path'; +import { ethers } from 'ethers'; import { chainIdTypeGoerli, chainIdTypeOptimism, @@ -23,13 +23,17 @@ import { chainIdTypeSepolia, walletSepolia, chainIdTypeAurora, -} from "../../ethersGovernorWallets"; -import { EvmLinkedAnchor, ProposalSigningBackend } from "@webb-tools/test-utils"; -import { ContractConfig, getEvmChainConfig, writeEvmChainConfig } from "../utils"; +} from '../../ethersGovernorWallets'; +import { EvmLinkedAnchor, ProposalSigningBackend } from '@webb-tools/test-utils'; +import { ContractConfig, getEvmChainConfig, writeEvmChainConfig } from '../utils'; import { zip } from 'itertools'; -import { EndPointConfig, moonbaseEndPoints, polygonEndPoints, sepoliaEndPoints } from "../endPoints"; -import Create2VBridge from "./create2Bridge"; - +import { + EndPointConfig, + moonbaseEndPoints, + polygonEndPoints, + sepoliaEndPoints, +} from '../endPoints'; +import Create2VBridge from './create2Bridge'; async function deploySignatureVBridge( tokens: Record, @@ -43,9 +47,7 @@ async function deploySignatureVBridge( for (const chainIdType of Object.keys(deployers)) { assetRecord[chainIdType] = tokens[chainIdType]; chainIdsArray.push(Number(chainIdType)); - governorConfig[Number(chainIdType)] = await deployers[ - chainIdType - ].getAddress(); + governorConfig[Number(chainIdType)] = await deployers[chainIdType].getAddress(); existingWebbTokens[chainIdType] = null; console.log(tokens[chainIdType]); } @@ -111,19 +113,19 @@ async function run() { const tokens: Record = { // [chainIdTypeGoerli]: ["0", ""], - [chainIdTypeSepolia]: ["0", "0xeD43f81C17976372Fcb5786Dd214572e7dbB92c7"], + [chainIdTypeSepolia]: ['0', '0xeD43f81C17976372Fcb5786Dd214572e7dbB92c7'], // [chainIdTypeOptimism]: ["0", "0x4200000000000000000000000000000000000006"], // [chainIdTypePolygon]: ["0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889"], // [chainIdTypeMoonbase]: ["0xD909178CC99d318e4D46e7E66a972955859670E1"], }; - + const endPoints: Record = { // [chainIdTypeGoerli]: goerliEndPoints, [chainIdTypeSepolia]: sepoliaEndPoints, // [chainIdTypeOptimism]: optimismEndPoints, // [chainIdTypePolygon]: polygonEndPoints, // [chainIdTypeMoonbase]: moonbaseEndPoints, - } + }; const vbridge = await deploySignatureVBridge(tokens, deployers); @@ -132,30 +134,29 @@ async function run() { const anchorIterable = bridgeConfig.vAnchors.values(); const bridgeSideIterable = bridgeConfig.vBridgeSides.values(); - - - for (const [anchor, bridgeSide] of zip(anchorIterable, bridgeSideIterable)){ + + for (const [anchor, bridgeSide] of zip(anchorIterable, bridgeSideIterable)) { const chainId = await anchor.signer.getChainId(); const anchorContractConfig: ContractConfig = { address: anchor.contract.address.toLowerCase(), - deployedAt: anchor.contract.deployTransaction.blockNumber + deployedAt: anchor.contract.deployTransaction.blockNumber, }; const bridgeContractConfig: ContractConfig = { address: bridgeSide.contract.address.toLowerCase(), - deployedAt: bridgeSide.contract.deployTransaction.blockNumber - } - const proposalSigningBackend : ProposalSigningBackend = { type: "DKGNode", node: "tangle" } + deployedAt: bridgeSide.contract.deployTransaction.blockNumber, + }; + const proposalSigningBackend: ProposalSigningBackend = { type: 'DKGNode', node: 'tangle' }; const linkedAnchors: EvmLinkedAnchor[] = []; const typedChainId = getChainIdType(chainId); - const beneficiary = "0xf1fd5607b90f6c421db7313158baac170d4f053b"; // relayer wallet address - + const beneficiary = '0xf1fd5607b90f6c421db7313158baac170d4f053b'; // relayer wallet address + // Add all other anchors to the list of linked anchors for (const otherAnchor of Array.from(bridgeConfig.vAnchors.values())) { if (otherAnchor !== anchor) { - linkedAnchors.push({ + linkedAnchors.push({ chainId: (await otherAnchor.contract.getChainId()).toString(), address: otherAnchor.contract.address.toLowerCase(), - type: "Evm" + type: 'Evm', }); } } @@ -172,27 +173,22 @@ async function run() { endPointConfig, beneficiary ); - + // convert config to kebab case and write to json file const dirPath = `${__dirname}/relayer-config`; - writeEvmChainConfig(`${dirPath}/${endPointConfig.name}.toml`, chainConfig - ) + writeEvmChainConfig(`${dirPath}/${endPointConfig.name}.toml`, chainConfig); console.log( `Anchor ${anchorContractConfig.address} deployed at: ${anchorContractConfig.deployedAt} for chainId ${chainId}` ); - + console.log( `BridgeSide ${bridgeContractConfig.address} deployed at: ${bridgeContractConfig.deployedAt} for chain ${chainId}` ); } - for (const webbToken of Array.from( - bridgeConfig.webbTokenAddresses.entries() - )) { - console.log( - `webbToken entry: ${webbToken[0]} + ${webbToken[1].toLowerCase()}` - ); + for (const webbToken of Array.from(bridgeConfig.webbTokenAddresses.entries())) { + console.log(`webbToken entry: ${webbToken[0]} + ${webbToken[1].toLowerCase()}`); } } diff --git a/scripts/evm/deployments/endPoints.ts b/scripts/evm/deployments/endPoints.ts index ac676b805..62a264e4f 100644 --- a/scripts/evm/deployments/endPoints.ts +++ b/scripts/evm/deployments/endPoints.ts @@ -1,37 +1,35 @@ export type EndPointConfig = { - httpEndpoint: string, - wsEndpoint: string, - name: string - } - -export const polygonEndPoints: EndPointConfig = { - httpEndpoint: process.env.MUMBAI_TESTNET_HTTPS_URL!, - wsEndpoint: process.env.MUMBAI_TESTNET_WSS_URL!, - name: "mumbai" + httpEndpoint: string; + wsEndpoint: string; + name: string; }; -export const sepoliaEndPoints: EndPointConfig = { - httpEndpoint: process.env.SEPOLIA_HTTPS_URL!, - wsEndpoint: process.env.SEPOLIA_WSS_URL!, - name: "sepolia" +export const polygonEndPoints: EndPointConfig = { + httpEndpoint: process.env.MUMBAI_TESTNET_HTTPS_URL!, + wsEndpoint: process.env.MUMBAI_TESTNET_WSS_URL!, + name: 'mumbai', }; -export const optimismEndPoints: EndPointConfig = { - httpEndpoint: process.env.OPTIMISM_TESTNET_HTTPS_URL!, - wsEndpoint: process.env.OPTIMISM_TESTNET_WSS_URL!, - name: "optimism" +export const sepoliaEndPoints: EndPointConfig = { + httpEndpoint: process.env.SEPOLIA_HTTPS_URL!, + wsEndpoint: process.env.SEPOLIA_WSS_URL!, + name: 'sepolia', }; -export const moonbaseEndPoints: EndPointConfig = { - httpEndpoint: process.env.MOONBASE_HTTPS_URL!, - wsEndpoint: process.env.MOONBASE_WSS_URL!, - name: "moonbase" +export const optimismEndPoints: EndPointConfig = { + httpEndpoint: process.env.OPTIMISM_TESTNET_HTTPS_URL!, + wsEndpoint: process.env.OPTIMISM_TESTNET_WSS_URL!, + name: 'optimism', }; -export const goerliEndPoints: EndPointConfig = { - httpEndpoint: process.env.GOERLI_HTTPS_URL!, - wsEndpoint: process.env.GOERLI_WSS_URL!, - name: "goerli" +export const moonbaseEndPoints: EndPointConfig = { + httpEndpoint: process.env.MOONBASE_HTTPS_URL!, + wsEndpoint: process.env.MOONBASE_WSS_URL!, + name: 'moonbase', }; - +export const goerliEndPoints: EndPointConfig = { + httpEndpoint: process.env.GOERLI_HTTPS_URL!, + wsEndpoint: process.env.GOERLI_WSS_URL!, + name: 'goerli', +}; diff --git a/scripts/evm/deployments/utils.ts b/scripts/evm/deployments/utils.ts index a4cbe7b54..bd904295b 100644 --- a/scripts/evm/deployments/utils.ts +++ b/scripts/evm/deployments/utils.ts @@ -1,6 +1,14 @@ import fs from 'fs'; -import { Contract, ConvertToKebabCase, EventsWatcher, FullChainInfo, LinkedAnchor, ProposalSigningBackend, WithdrawConfig } from "@webb-tools/test-utils"; -import { EndPointConfig } from "./endPoints"; +import { + Contract, + ConvertToKebabCase, + EventsWatcher, + FullChainInfo, + LinkedAnchor, + ProposalSigningBackend, + WithdrawConfig, +} from '@webb-tools/test-utils'; +import { EndPointConfig } from './endPoints'; import { Wallet } from 'ethers'; import { toToml } from 'tomlify-j0.4'; @@ -18,77 +26,69 @@ const defaultEventWatcherConfigValue: EventsWatcher = { }; export type ContractConfig = { - address: string, - deployedAt: number, + address: string; + deployedAt: number; }; export function getEvmChainConfig( - chainId: number, - anchor: ContractConfig, - bridge: ContractConfig, - deployerWallet: Wallet, - linkedAnchors: LinkedAnchor[], - proposalSigningBackend: ProposalSigningBackend, - endpoint: EndPointConfig, - beneficiary?: string - ): FullChainInfo{ - - const contracts: Contract[] = [ - // first the local Anchor - { - contract: 'VAnchor', - address: anchor.address, - deployedAt: anchor.deployedAt, - size: 1, // Ethers - proposalSigningBackend: proposalSigningBackend, - withdrawConfig: defaultWithdrawConfigValue, - eventsWatcher: defaultEventWatcherConfigValue, - linkedAnchors: linkedAnchors, - }, - { - contract: 'SignatureBridge', - address: bridge.address, - deployedAt: bridge.deployedAt, - eventsWatcher: defaultEventWatcherConfigValue - }, - ]; - const chainInfo: FullChainInfo = { - name: endpoint.name, - enabled: true, - httpEndpoint: endpoint.httpEndpoint, - wsEndpoint: endpoint.wsEndpoint, - blockConfirmations: 0, - chainId: chainId, - beneficiary: beneficiary ?? '', - privateKey: deployerWallet.privateKey, - contracts: contracts, - }; - return chainInfo; + chainId: number, + anchor: ContractConfig, + bridge: ContractConfig, + deployerWallet: Wallet, + linkedAnchors: LinkedAnchor[], + proposalSigningBackend: ProposalSigningBackend, + endpoint: EndPointConfig, + beneficiary?: string +): FullChainInfo { + const contracts: Contract[] = [ + // first the local Anchor + { + contract: 'VAnchor', + address: anchor.address, + deployedAt: anchor.deployedAt, + size: 1, // Ethers + proposalSigningBackend: proposalSigningBackend, + withdrawConfig: defaultWithdrawConfigValue, + eventsWatcher: defaultEventWatcherConfigValue, + linkedAnchors: linkedAnchors, + }, + { + contract: 'SignatureBridge', + address: bridge.address, + deployedAt: bridge.deployedAt, + eventsWatcher: defaultEventWatcherConfigValue, + }, + ]; + const chainInfo: FullChainInfo = { + name: endpoint.name, + enabled: true, + httpEndpoint: endpoint.httpEndpoint, + wsEndpoint: endpoint.wsEndpoint, + blockConfirmations: 0, + chainId: chainId, + beneficiary: beneficiary ?? '', + privateKey: deployerWallet.privateKey, + contracts: contracts, + }; + return chainInfo; } - -export function writeEvmChainConfig ( - path: string, - config: FullChainInfo -) { +export function writeEvmChainConfig(path: string, config: FullChainInfo) { type ConvertedLinkedAnchor = ConvertToKebabCase; type ConvertedContract = Omit< - ConvertToKebabCase, - | 'events-watcher' - | 'proposal-signing-backend' - | 'withdraw-config' - | 'linked-anchors' - | 'deployed-at' + ConvertToKebabCase, + | 'events-watcher' + | 'proposal-signing-backend' + | 'withdraw-config' + | 'linked-anchors' + | 'deployed-at' > & { 'events-watcher': ConvertToKebabCase; 'proposal-signing-backend'?: ConvertToKebabCase; 'withdraw-config'?: ConvertToKebabCase; 'linked-anchors'?: ConvertedLinkedAnchor[]; }; - type ConvertedConfig = Omit< - ConvertToKebabCase, - 'contracts' - > & { + type ConvertedConfig = Omit, 'contracts'> & { contracts: ConvertedContract[]; }; type FullConfigFile = { @@ -109,66 +109,60 @@ export function writeEvmChainConfig ( 'events-watcher': { enabled: contract.eventsWatcher.enabled, 'polling-interval': contract.eventsWatcher.pollingInterval, - 'print-progress-interval': - contract.eventsWatcher.printProgressInterval + 'print-progress-interval': contract.eventsWatcher.printProgressInterval, }, 'linked-anchors': contract?.linkedAnchors?.map((anchor: LinkedAnchor) => anchor.type === 'Evm' ? { - address: anchor.address, - 'chain-id': anchor.chainId, - type: 'Evm' - } + address: anchor.address, + 'chain-id': anchor.chainId, + type: 'Evm', + } : anchor.type === 'Substrate' - ? { + ? { 'chain-id': anchor.chainId, pallet: anchor.pallet, 'tree-id': anchor.treeId, - type: 'Substrate' - + type: 'Substrate', } - : { + : { 'resource-id': anchor.resourceId, - type: 'Raw' + type: 'Raw', } ), 'proposal-signing-backend': contract.proposalSigningBackend?.type === 'Mocked' ? { - 'private-key': contract.proposalSigningBackend?.privateKey, - type: 'Mocked' - } + 'private-key': contract.proposalSigningBackend?.privateKey, + type: 'Mocked', + } : contract.proposalSigningBackend?.type === 'DKGNode' - ? { + ? { node: contract.proposalSigningBackend?.node, - type: 'DKGNode' + type: 'DKGNode', } - : undefined, + : undefined, 'withdraw-config': contract.withdrawConfig ? { - 'withdraw-fee-percentage': - contract.withdrawConfig?.withdrawFeePercentage, - 'withdraw-gaslimit': contract.withdrawConfig?.withdrawGaslimit - } - : undefined + 'withdraw-fee-percentage': contract.withdrawConfig?.withdrawFeePercentage, + 'withdraw-gaslimit': contract.withdrawConfig?.withdrawGaslimit, + } + : undefined, })), enabled: config.enabled, 'http-endpoint': config.httpEndpoint, name: config.name, 'private-key': config.privateKey, - 'ws-endpoint': config.wsEndpoint + 'ws-endpoint': config.wsEndpoint, }; const fullConfigFile: FullConfigFile = { evm: { - [config.name]: convertedConfig - } + [config.name]: convertedConfig, + }, }; const toml = toToml(fullConfigFile, { spaces: 4 }); // Write the TOML string to a file fs.writeFileSync(path, toml); - } - -