From ef764da1e2f2dcc3460b6710c599211605340b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Volpe?= Date: Wed, 3 Jan 2024 18:23:22 +0100 Subject: [PATCH] Remove all MetaTransaction files (#10859) Remove all MetaTransaction files, it may not build --- .../common/MetaTransactionWallet.sol | 324 ---------- .../common/MetaTransactionWalletDeployer.sol | 35 - .../interfaces/IMetaTransactionWallet.sol | 34 - .../IMetaTransactionWalletDeployer.sol | 6 - .../MetaTransactionWalletDeployerProxy.sol | 6 - .../proxies/MetaTransactionWalletProxy.sol | 6 - packages/protocol/lib/meta-tx-utils.ts | 70 -- packages/protocol/migrationsConfig.js | 1 - packages/protocol/scripts/consts.ts | 5 - .../test/common/metatransactionwallet.ts | 607 ------------------ .../common/metatransactionwalletdeployer.ts | 86 --- 11 files changed, 1180 deletions(-) delete mode 100644 packages/protocol/contracts/common/MetaTransactionWallet.sol delete mode 100644 packages/protocol/contracts/common/MetaTransactionWalletDeployer.sol delete mode 100644 packages/protocol/contracts/common/interfaces/IMetaTransactionWallet.sol delete mode 100644 packages/protocol/contracts/common/interfaces/IMetaTransactionWalletDeployer.sol delete mode 100644 packages/protocol/contracts/common/proxies/MetaTransactionWalletDeployerProxy.sol delete mode 100644 packages/protocol/contracts/common/proxies/MetaTransactionWalletProxy.sol delete mode 100644 packages/protocol/lib/meta-tx-utils.ts delete mode 100644 packages/protocol/test/common/metatransactionwallet.ts delete mode 100644 packages/protocol/test/common/metatransactionwalletdeployer.ts diff --git a/packages/protocol/contracts/common/MetaTransactionWallet.sol b/packages/protocol/contracts/common/MetaTransactionWallet.sol deleted file mode 100644 index bb092447567..00000000000 --- a/packages/protocol/contracts/common/MetaTransactionWallet.sol +++ /dev/null @@ -1,324 +0,0 @@ -pragma solidity ^0.5.13; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; -import "solidity-bytes-utils/contracts/BytesLib.sol"; - -import "./interfaces/ICeloVersionedContract.sol"; -import "./interfaces/IMetaTransactionWallet.sol"; -import "./ExternalCall.sol"; -import "./Initializable.sol"; -import "./Signatures.sol"; - -contract MetaTransactionWallet is - IMetaTransactionWallet, - ICeloVersionedContract, - Initializable, - Ownable -{ - using SafeMath for uint256; - using BytesLib for bytes; - - bytes32 public eip712DomainSeparator; - // The EIP712 typehash for ExecuteMetaTransaction, i.e. keccak256( - // "ExecuteMetaTransaction(address destination,uint256 value,bytes data,uint256 nonce)"); - bytes32 public constant EIP712_EXECUTE_META_TRANSACTION_TYPEHASH = ( - 0x509c6e92324b7214543573524d0bb493d654d3410fa4f4937b3d2f4a903edd33 - ); - uint256 public nonce; - address public signer; - address public guardian; - - event SignerSet(address indexed signer); - event GuardianSet(address indexed guardian); - event WalletRecovered(address indexed newSigner); - event EIP712DomainSeparatorSet(bytes32 eip712DomainSeparator); - event Deposit(address indexed sender, uint256 value); - event TransactionExecution( - address indexed destination, - uint256 value, - bytes data, - bytes returnData - ); - event MetaTransactionExecution( - address indexed destination, - uint256 value, - bytes data, - uint256 indexed nonce, - bytes returnData - ); - - // onlyGuardian functions can only be called when the guardian is not the zero address and - // the caller is the guardian. - modifier onlyGuardian() { - // Note that if the guardian is not set (e.g. its address 0), this require statement will fail. - require(guardian == msg.sender, "Caller is not the guardian"); - _; - } - - /** - * @notice Sets initialized == true on implementation contracts - * @param test Set to true to skip implementation initialization - */ - constructor(bool test) public Initializable(test) {} - - /** - * @dev Fallback function allows to deposit ether. - */ - function() external payable { - if (msg.value > 0) emit Deposit(msg.sender, msg.value); - } - - /** - * @notice Returns the storage, major, minor, and patch version of the contract. - * @return Storage version of the contract. - * @return Major version of the contract. - * @return Minor version of the contract. - * @return Patch version of the contract. - */ - function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 1, 1); - } - - /** - * @notice Used in place of the constructor to allow the contract to be upgradable via proxy. - * @param _signer The address authorized to execute transactions via this wallet. - */ - function initialize(address _signer) external initializer { - _setSigner(_signer); - setEip712DomainSeparator(); - // MetaTransactionWallet owns itself, which necessitates that all onlyOwner functions - // be called via executeTransaction or executeMetaTransaction. - // If the signer was the owner, onlyOwner functions would not be callable via - // meta-transactions. - _transferOwnership(address(this)); - } - - /** - * @notice Transfers control of the wallet to a new signer. - * @param _signer The address authorized to execute transactions via this wallet. - */ - function setSigner(address _signer) external onlyOwner { - _setSigner(_signer); - } - - /** - * @notice Sets the wallet's guardian address. - * @param _guardian The address authorized to change the wallet's signer - */ - function setGuardian(address _guardian) external onlyOwner { - guardian = _guardian; - emit GuardianSet(guardian); - } - - /** - * @notice Changes the wallet's signer - * @param newSigner The new signer address - */ - function recoverWallet(address newSigner) external onlyGuardian { - _setSigner(newSigner); - emit WalletRecovered(newSigner); - } - - /** - * @notice Sets the EIP-712 domain separator. - * @dev Should be called every time the wallet is upgraded to a new version. - */ - function setEip712DomainSeparator() public { - uint256 id; - assembly { - id := chainid - } - // Note: `version` is the storage.major part of this contract's version (an - // increase to either of these could mean backwards incompatibilities). - eip712DomainSeparator = keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256(bytes("MetaTransactionWallet")), - keccak256("1.1"), - id, - address(this) - ) - ); - emit EIP712DomainSeparatorSet(eip712DomainSeparator); - } - - /** - * @notice Returns the struct hash of the MetaTransaction - * @param destination The address to which the meta-transaction is to be sent. - * @param value The CELO value to be sent with the meta-transaction. - * @param data The data to be sent with the meta-transaction. - * @param _nonce The nonce for this meta-transaction local to this wallet. - * @return The digest of the provided meta-transaction. - */ - function _getMetaTransactionStructHash( - address destination, - uint256 value, - bytes memory data, - uint256 _nonce - ) internal view returns (bytes32) { - return - keccak256( - abi.encode( - EIP712_EXECUTE_META_TRANSACTION_TYPEHASH, - destination, - value, - keccak256(data), - _nonce - ) - ); - } - - /** - * @notice Returns the digest of the provided meta-transaction, to be signed by `sender`. - * @param destination The address to which the meta-transaction is to be sent. - * @param value The CELO value to be sent with the meta-transaction. - * @param data The data to be sent with the meta-transaction. - * @param _nonce The nonce for this meta-transaction local to this wallet. - * @return The digest of the provided meta-transaction. - */ - function getMetaTransactionDigest( - address destination, - uint256 value, - bytes calldata data, - uint256 _nonce - ) external view returns (bytes32) { - bytes32 structHash = _getMetaTransactionStructHash(destination, value, data, _nonce); - return Signatures.toEthSignedTypedDataHash(eip712DomainSeparator, structHash); - } - - /** - * @notice Returns the address that signed the provided meta-transaction. - * @param destination The address to which the meta-transaction is to be sent. - * @param value The CELO value to be sent with the meta-transaction. - * @param data The data to be sent with the meta-transaction. - * @param _nonce The nonce for this meta-transaction local to this wallet. - * @param v The recovery id of the ECDSA signature of the meta-transaction. - * @param r Output value r of the ECDSA signature. - * @param s Output value s of the ECDSA signature. - * @return The address that signed the provided meta-transaction. - */ - function getMetaTransactionSigner( - address destination, - uint256 value, - bytes memory data, - uint256 _nonce, - uint8 v, - bytes32 r, - bytes32 s - ) public view returns (address) { - bytes32 structHash = _getMetaTransactionStructHash(destination, value, data, _nonce); - return Signatures.getSignerOfTypedDataHash(eip712DomainSeparator, structHash, v, r, s); - } - - /** - * @notice Executes a meta-transaction on behalf of the signer. - * @param destination The address to which the meta-transaction is to be sent. - * @param value The CELO value to be sent with the meta-transaction. - * @param data The data to be sent with the meta-transaction. - * @param v The recovery id of the ECDSA signature of the meta-transaction. - * @param r Output value r of the ECDSA signature. - * @param s Output value s of the ECDSA signature. - * @return The return value of the meta-transaction execution. - */ - function executeMetaTransaction( - address destination, - uint256 value, - bytes calldata data, - uint8 v, - bytes32 r, - bytes32 s - ) external returns (bytes memory) { - address _signer = getMetaTransactionSigner(destination, value, data, nonce, v, r, s); - require(_signer == signer, "Invalid meta-transaction signer"); - nonce = nonce.add(1); - bytes memory returnData = ExternalCall.execute(destination, value, data); - emit MetaTransactionExecution(destination, value, data, nonce.sub(1), returnData); - return returnData; - } - - /** - * @notice Executes a transaction on behalf of the signer.` - * @param destination The address to which the transaction is to be sent. - * @param value The CELO value to be sent with the transaction. - * @param data The data to be sent with the transaction. - * @return The return value of the transaction execution. - */ - function executeTransaction(address destination, uint256 value, bytes memory data) - public - returns (bytes memory) - { - // Allowing the owner to call execute transaction allows, when the contract is self-owned, - // for the signer to sign and execute a batch of transactions via a meta-transaction. - require(msg.sender == signer || msg.sender == owner(), "Invalid transaction sender"); - bytes memory returnData = ExternalCall.execute(destination, value, data); - emit TransactionExecution(destination, value, data, returnData); - return returnData; - } - - /** - * @notice Executes multiple transactions on behalf of the signer.` - * @param destinations The address to which each transaction is to be sent. - * @param values The CELO value to be sent with each transaction. - * @param data The concatenated data to be sent in each transaction. - * @param dataLengths The length of each transaction's data. - * @return All transactions appended as bytes - * @return An array of the length - * of each transaction output which will be 0 if a transaction had no output - */ - function executeTransactions( - address[] calldata destinations, - uint256[] calldata values, - bytes calldata data, - uint256[] calldata dataLengths - ) external returns (bytes memory, uint256[] memory) { - require( - destinations.length == values.length && values.length == dataLengths.length, - "Input arrays must be same length" - ); - - bytes memory returnValues; - uint256[] memory returnLengths = new uint256[](destinations.length); - uint256 dataPosition = 0; - for (uint256 i = 0; i < destinations.length; i = i.add(1)) { - bytes memory returnVal = executeTransaction( - destinations[i], - values[i], - sliceData(data, dataPosition, dataLengths[i]) - ); - returnValues = abi.encodePacked(returnValues, returnVal); - returnLengths[i] = returnVal.length; - dataPosition = dataPosition.add(dataLengths[i]); - } - - require(dataPosition == data.length, "data cannot have extra bytes appended"); - return (returnValues, returnLengths); - } - - /** - * @notice Returns a slice from a byte array. - * @param data The byte array. - * @param start The start index of the slice to take. - * @param length The length of the slice to take. - * @return A slice from a byte array. - */ - function sliceData(bytes memory data, uint256 start, uint256 length) - internal - returns (bytes memory) - { - // When length == 0 bytes.slice does not seem to always return an empty byte array. - bytes memory sliced; - if (length > 0) { - sliced = data.slice(start, length); - } - return sliced; - } - - function _setSigner(address _signer) internal { - require(_signer != address(0), "cannot assign zero address as signer"); - signer = _signer; - emit SignerSet(signer); - } - -} diff --git a/packages/protocol/contracts/common/MetaTransactionWalletDeployer.sol b/packages/protocol/contracts/common/MetaTransactionWalletDeployer.sol deleted file mode 100644 index 8db335e68ce..00000000000 --- a/packages/protocol/contracts/common/MetaTransactionWalletDeployer.sol +++ /dev/null @@ -1,35 +0,0 @@ -pragma solidity ^0.5.13; - -import "./interfaces/ICeloVersionedContract.sol"; -import "./interfaces/IMetaTransactionWalletDeployer.sol"; -import "./proxies/MetaTransactionWalletProxy.sol"; - -contract MetaTransactionWalletDeployer is IMetaTransactionWalletDeployer, ICeloVersionedContract { - event WalletDeployed(address indexed owner, address indexed wallet, address implementation); - - /** - * @notice Returns the storage, major, minor, and patch version of the contract. - * @return Storage version of the contract. - * @return Major version of the contract. - * @return Minor version of the contract. - * @return Patch version of the contract. - */ - function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 0, 3); - } - - /** - * @notice Used to deploy a MetaTransactionWalletProxy, set the implementation, - * initialize, transfer ownership and emit an event. - * @param owner The external account which will act as signer and owner of the proxy - * @param implementation The address of the implementation which the proxy will point to - * @param initCallData calldata pointing to a method on implementation used to initialize - */ - function deploy(address owner, address implementation, bytes calldata initCallData) external { - MetaTransactionWalletProxy proxy = new MetaTransactionWalletProxy(); - proxy._setAndInitializeImplementation(implementation, initCallData); - proxy._transferOwnership(owner); - - emit WalletDeployed(owner, address(proxy), implementation); - } -} diff --git a/packages/protocol/contracts/common/interfaces/IMetaTransactionWallet.sol b/packages/protocol/contracts/common/interfaces/IMetaTransactionWallet.sol deleted file mode 100644 index 2b47c321062..00000000000 --- a/packages/protocol/contracts/common/interfaces/IMetaTransactionWallet.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity >=0.5.13 <0.9.0; - -interface IMetaTransactionWallet { - function setEip712DomainSeparator() external; - function executeMetaTransaction(address, uint256, bytes calldata, uint8, bytes32, bytes32) - external - returns (bytes memory); - function executeTransaction(address, uint256, bytes calldata) external returns (bytes memory); - function executeTransactions( - address[] calldata, - uint256[] calldata, - bytes calldata, - uint256[] calldata - ) external returns (bytes memory, uint256[] memory); - - // view functions - function getMetaTransactionDigest(address, uint256, bytes calldata, uint256) - external - view - returns (bytes32); - function getMetaTransactionSigner( - address, - uint256, - bytes calldata, - uint256, - uint8, - bytes32, - bytes32 - ) external view returns (address); - - //only owner - function setSigner(address) external; -} diff --git a/packages/protocol/contracts/common/interfaces/IMetaTransactionWalletDeployer.sol b/packages/protocol/contracts/common/interfaces/IMetaTransactionWalletDeployer.sol deleted file mode 100644 index f7c111741fe..00000000000 --- a/packages/protocol/contracts/common/interfaces/IMetaTransactionWalletDeployer.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity >=0.5.13 <0.9.0; - -interface IMetaTransactionWalletDeployer { - function deploy(address, address, bytes calldata) external; -} diff --git a/packages/protocol/contracts/common/proxies/MetaTransactionWalletDeployerProxy.sol b/packages/protocol/contracts/common/proxies/MetaTransactionWalletDeployerProxy.sol deleted file mode 100644 index 6cbd504b1f9..00000000000 --- a/packages/protocol/contracts/common/proxies/MetaTransactionWalletDeployerProxy.sol +++ /dev/null @@ -1,6 +0,0 @@ -pragma solidity ^0.5.3; - -import "../Proxy.sol"; - -/* solhint-disable-next-line no-empty-blocks */ -contract MetaTransactionWalletDeployerProxy is Proxy {} diff --git a/packages/protocol/contracts/common/proxies/MetaTransactionWalletProxy.sol b/packages/protocol/contracts/common/proxies/MetaTransactionWalletProxy.sol deleted file mode 100644 index 919b344011c..00000000000 --- a/packages/protocol/contracts/common/proxies/MetaTransactionWalletProxy.sol +++ /dev/null @@ -1,6 +0,0 @@ -pragma solidity ^0.5.3; - -import "../Proxy.sol"; - -/* solhint-disable-next-line no-empty-blocks */ -contract MetaTransactionWalletProxy is Proxy {} diff --git a/packages/protocol/lib/meta-tx-utils.ts b/packages/protocol/lib/meta-tx-utils.ts deleted file mode 100644 index 867e723397e..00000000000 --- a/packages/protocol/lib/meta-tx-utils.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Address } from '@celo/utils/lib/address'; -import { generateTypedDataHash, structHash } from '@celo/utils/lib/sign-typed-data-utils'; -import { parseSignatureWithoutPrefix } from '@celo/utils/lib/signatureUtils'; -import { - bufferToHex -} from '@ethereumjs/util'; - -export interface MetaTransaction { - destination: Address - value: number - data: string - nonce: number -} -// The value currently returned by the chainId assembly code in ganache. -const chainId = 1 -const getTypedData = (walletAddress: Address, tx?: MetaTransaction) => { - const typedData = { - types: { - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'chainId', type: 'uint256' }, - { name: 'verifyingContract', type: 'address' }, - ], - ExecuteMetaTransaction: [ - { name: 'destination', type: 'address' }, - { name: 'value', type: 'uint256' }, - { name: 'data', type: 'bytes' }, - { name: 'nonce', type: 'uint256' }, - ], - }, - primaryType: 'ExecuteMetaTransaction', - domain: { - name: 'MetaTransactionWallet', - version: '1.1', - chainId, - verifyingContract: walletAddress, - }, - message: tx ? tx : {}, - } - return typedData -} - -export const getDomainDigest = (walletAddress: Address) => { - const typedData = getTypedData(walletAddress) - return bufferToHex( - structHash('EIP712Domain', typedData.domain, typedData.types) - ) -} - -export const constructMetaTransactionExecutionDigest = (walletAddress: Address, tx: MetaTransaction) => { - const typedData = getTypedData(walletAddress, tx) - return bufferToHex(generateTypedDataHash(typedData) ) -} - -export const getSignatureForMetaTransaction = async ( - signer: Address, - walletAddress: Address, - tx: MetaTransaction -) => { - const typedData = getTypedData(walletAddress, tx) - - const signature = await web3.currentProvider.request({ - method: 'eth_signTypedData',params: [signer, typedData], - }) - - const messageHash = constructMetaTransactionExecutionDigest(walletAddress, tx) - const parsedSignature = parseSignatureWithoutPrefix(messageHash, signature, signer) - return parsedSignature -} diff --git a/packages/protocol/migrationsConfig.js b/packages/protocol/migrationsConfig.js index 86f5cacb47d..92db3f9d4b1 100644 --- a/packages/protocol/migrationsConfig.js +++ b/packages/protocol/migrationsConfig.js @@ -590,7 +590,6 @@ const linkedLibraries = { 'AttestationsTest', 'LockedGold', 'Escrow', - 'MetaTransactionWallet', 'FederatedAttestations', ], } diff --git a/packages/protocol/scripts/consts.ts b/packages/protocol/scripts/consts.ts index 45fb8aa4597..f763775e928 100644 --- a/packages/protocol/scripts/consts.ts +++ b/packages/protocol/scripts/consts.ts @@ -30,8 +30,6 @@ export const ProxyContracts = [ 'GovernanceApproverMultiSigProxy', 'GovernanceProxy', 'LockedGoldProxy', - 'MetaTransactionWalletProxy', - 'MetaTransactionWalletDeployerProxy', 'OdisPaymentsProxy', 'RegistryProxy', 'SortedOraclesProxy', @@ -47,12 +45,9 @@ export const CoreContracts = [ 'UniswapFeeHandlerSeller', 'FeeCurrencyWhitelist', 'GoldToken', - 'MetaTransactionWallet', - 'MetaTransactionWalletDeployer', 'MultiSig', 'Registry', 'Freezer', - 'MetaTransactionWallet', // governance 'Election', diff --git a/packages/protocol/test/common/metatransactionwallet.ts b/packages/protocol/test/common/metatransactionwallet.ts deleted file mode 100644 index 7a073ca7007..00000000000 --- a/packages/protocol/test/common/metatransactionwallet.ts +++ /dev/null @@ -1,607 +0,0 @@ -import { - constructMetaTransactionExecutionDigest, - getDomainDigest, - getSignatureForMetaTransaction, -} from '@celo/protocol/lib/meta-tx-utils' -import { - assertEqualBN, - assertLogMatches2, - // tslint:disable-next-line: ordered-imports - assertTransactionRevertWithReason, - assertTransactionRevertWithoutReason, -} from '@celo/protocol/lib/test-utils' -import { ensureLeading0x, trimLeading0x } from '@celo/utils/lib/address' -import { MetaTransactionWalletContract, MetaTransactionWalletInstance } from 'types' - -const MetaTransactionWallet: MetaTransactionWalletContract = - artifacts.require('MetaTransactionWallet') - -contract('MetaTransactionWallet', (accounts: string[]) => { - let wallet: MetaTransactionWalletInstance - let initializeRes - const signer = accounts[1] - const nonSigner = accounts[2] - - const executeOnSelf = (data: string, value = 0) => - // @ts-ignore - wallet.executeTransaction(wallet.address, value, data, { from: signer }) - - beforeEach(async () => { - wallet = await MetaTransactionWallet.new(true) - initializeRes = await wallet.initialize(signer) - }) - - describe('#EIP712_EXECUTE_META_TRANSACTION_TYPEHASH()', () => { - it('should have set the right typehash', async () => { - const expectedTypehash = web3.utils.soliditySha3( - 'ExecuteMetaTransaction(address destination,uint256 value,bytes data,uint256 nonce)' - ) - assert.equal(await wallet.EIP712_EXECUTE_META_TRANSACTION_TYPEHASH(), expectedTypehash) - }) - }) - - describe('#initialize()', () => { - it('should have set the owner to itself', async () => { - assert.equal(await wallet.owner(), wallet.address) - }) - - it('should have set the signer', async () => { - assert.equal(await wallet.signer(), signer) - }) - - it('should have set the EIP-712 domain separator', async () => { - assert.equal(await wallet.eip712DomainSeparator(), getDomainDigest(wallet.address)) - }) - - it('should emit the SignerSet event', () => { - assertLogMatches2(initializeRes.logs[0], { - event: 'SignerSet', - args: { - signer, - }, - }) - }) - - it('should emit the EIP712DomainSeparatorSet event', () => { - assertLogMatches2(initializeRes.logs[1], { - event: 'EIP712DomainSeparatorSet', - args: { - eip712DomainSeparator: getDomainDigest(wallet.address), - }, - }) - }) - - it('should emit the OwnershipTransferred event', () => { - assertLogMatches2(initializeRes.logs[2], { - event: 'OwnershipTransferred', - args: { - previousOwner: accounts[0], - newOwner: wallet.address, - }, - }) - }) - - it('should not be callable again', async () => { - await assertTransactionRevertWithReason( - wallet.initialize(signer), - 'contract already initialized' - ) - }) - }) - - describe('fallback function', () => { - describe('when receiving celo', () => { - it('emits Deposit event with correct parameters', async () => { - const value = 100 - // @ts-ignore - const res = await wallet.send(value) - assertLogMatches2(res.logs[0], { - event: 'Deposit', - args: { - sender: accounts[0], - value, - }, - }) - }) - }) - - describe('when receiving 0 value', () => { - it('does not emit an event', async () => { - // @ts-ignore - const res = await wallet.send(0) - assert.equal(res.logs, 0) - }) - }) - }) - - describe('#setSigner()', () => { - const newSigner = accounts[3] - - describe('when called by the wallet contract', () => { - let res - beforeEach(async () => { - // @ts-ignore - const data = wallet.contract.methods.setSigner(newSigner).encodeABI() - res = await executeOnSelf(data) - }) - it('should set a new signer', async () => { - assert.equal(await wallet.signer(), newSigner) - }) - it('should emit the SignerSet event', async () => { - assertLogMatches2(res.logs[0], { - event: 'SignerSet', - args: { - signer: newSigner, - }, - }) - }) - }) - - describe('when called by the signer', () => { - it('should revert', async () => { - await assertTransactionRevertWithReason( - wallet.setSigner(newSigner, { from: signer }), - 'Ownable: caller is not the owner' - ) - }) - }) - }) - - describe('#setGuardian()', () => { - const guardian = accounts[4] - - describe('when called by the wallet contract', () => { - let res - beforeEach(async () => { - // @ts-ignore - const data = wallet.contract.methods.setGuardian(guardian).encodeABI() - res = await executeOnSelf(data) - }) - it('should set a new guardian', async () => { - assert.equal(await wallet.guardian(), guardian) - }) - it('should emit the GuardianSet event', async () => { - assertLogMatches2(res.logs[0], { - event: 'GuardianSet', - args: { - guardian, - }, - }) - }) - }) - - describe('when not called by the wallet contract', () => { - it('should revert', async () => { - await assertTransactionRevertWithReason( - wallet.setGuardian(guardian, { from: nonSigner }), - 'Ownable: caller is not the owner' - ) - }) - }) - }) - - describe('#recoverWallet()', () => { - const newSigner = accounts[3] - const guardian = accounts[4] - const nonGuardian = accounts[5] - - describe('When the guardian is set', async () => { - let res - beforeEach(async () => { - // @ts-ignore - const data = wallet.contract.methods.setGuardian(guardian).encodeABI() - await executeOnSelf(data) - }) - - it('guardian should be able to recover wallet and update signer', async () => { - assert.notEqual(await wallet.signer(), newSigner) - res = await wallet.recoverWallet(newSigner, { from: guardian }) - assert.equal(await wallet.signer(), newSigner) - }) - - it('should emit the SignerSet event', async () => { - assertLogMatches2(res.logs[1], { - event: 'WalletRecovered', - args: { - newSigner, - }, - }) - }) - - it('non guardian should not be able to recover wallet', async () => { - await assertTransactionRevertWithReason( - wallet.recoverWallet(newSigner, { from: nonGuardian }), - 'Caller is not the guardian' - ) - }) - }) - - describe('when the guardian is not set', () => { - it('should not be able to recover wallet', async () => { - await assertTransactionRevertWithReason( - wallet.recoverWallet(newSigner, { from: guardian }), - 'Caller is not the guardian' - ) - }) - }) - }) - - describe('#executeTransaction()', () => { - describe('when the destination is a contract', () => { - let res: any - let data: string - let destination: string - const value = 0 - beforeEach(async () => { - destination = wallet.address - // @ts-ignore - data = wallet.contract.methods.setSigner(nonSigner).encodeABI() - }) - - describe('when the caller is the signer', () => { - beforeEach(async () => { - res = await wallet.executeTransaction(destination, value, data, { from: signer }) - }) - - it('should execute the transaction', async () => { - assert.equal(await wallet.signer(), nonSigner) - }) - - it('should not increment the nonce', async () => { - assertEqualBN(await wallet.nonce(), 0) - }) - - it('should emit the TransactionExecution event', () => { - assertLogMatches2(res.logs[1], { - event: 'TransactionExecution', - args: { - destination, - value, - data, - returnData: null, - }, - }) - }) - }) - - describe('when the caller is not the signer', () => { - it('should revert', async () => { - await assertTransactionRevertWithReason( - wallet.executeTransaction(destination, value, data, { from: nonSigner }), - 'Invalid transaction sender' - ) - }) - }) - }) - - describe('when the destination is not a contract', () => { - const value = 100 - const destination = web3.utils.toChecksumAddress(web3.utils.randomHex(20)) - describe('when the caller is the signer', () => { - describe('when data is empty', () => { - let res: any - const data = '0x' - beforeEach(async () => { - await web3.eth.sendTransaction({ from: accounts[0], to: wallet.address, value }) - // @ts-ignore - res = await wallet.executeTransaction(destination, value, data, { from: signer }) - }) - - it('should execute the transaction', async () => { - assert.equal(await web3.eth.getBalance(destination), value) - }) - - it('should not increment the nonce', async () => { - assertEqualBN(await wallet.nonce(), 0) - }) - - it('should emit the TransactionExecution event', () => { - assertLogMatches2(res.logs[0], { - event: 'TransactionExecution', - args: { - destination, - value, - data: null, - returnData: null, - }, - }) - }) - }) - - describe('when data is not empty', () => { - it('should revert', async () => { - await assertTransactionRevertWithReason( - wallet.executeTransaction(destination, value, '0x1234', { from: signer }), - 'Invalid contract address' - ) - }) - }) - }) - }) - }) - - describe('#executeTransactions()', () => { - describe('when the transactions are a mix of contract and non-contract calls', () => { - const value = 100 - let transactions: any[] - beforeEach(async () => { - transactions = [ - // CELO transfer - { - destination: web3.utils.toChecksumAddress(web3.utils.randomHex(20)), - value, - data: '0x', - }, - - // No-op transfer ownership - { - destination: wallet.address, - value: 0, - // @ts-ignore - data: wallet.contract.methods.transferOwnership(wallet.address).encodeABI(), - }, - - // CELO transfer - { - destination: web3.utils.toChecksumAddress(web3.utils.randomHex(20)), - value, - data: '0x', - }, - - // view signer (to test return values) - { - destination: wallet.address, - value: 0, - // @ts-ignore - data: wallet.contract.methods.signer().encodeABI(), - }, - - // view isOwner (to test return values) - { - destination: wallet.address, - value: 0, - // @ts-ignore - data: wallet.contract.methods.isOwner().encodeABI(), - }, - - // Change signer - { - destination: wallet.address, - value: 0, - // @ts-ignore - data: wallet.contract.methods.setSigner(nonSigner).encodeABI(), - }, - ] - - await web3.eth.sendTransaction({ - from: accounts[0], - to: wallet.address, - value: value * 2, - }) - }) - - describe('when the caller is the signer', () => { - describe('when the transactions are executed directly', () => { - describe('', () => { - beforeEach(async () => { - await wallet.executeTransactions( - transactions.map((t) => t.destination), - transactions.map((t) => t.value), - ensureLeading0x(transactions.map((t) => trimLeading0x(t.data)).join('')), - transactions.map((t) => trimLeading0x(t.data).length / 2), - { from: signer } - ) - }) - - it('should execute the transactions', async () => { - assert.equal(await web3.eth.getBalance(transactions[0].destination), value) - assert.equal(await web3.eth.getBalance(transactions[2].destination), value) - assert.equal(await wallet.signer(), nonSigner) - }) - - it('should not increment the nonce', async () => { - assertEqualBN(await wallet.nonce(), 0) - }) - }) - - it('returns the proper values', async () => { - const boolTrueReturned: string = '01' - const signerAddrReturned: string = trimLeading0x(signer).toLowerCase() - const viewSignerTxIndex: number = 3 - const isOwnerTxIndex: number = 4 - - const returnValues = await wallet.executeTransactions.call( - transactions.map((t) => t.destination), - transactions.map((t) => t.value), - ensureLeading0x(transactions.map((t) => trimLeading0x(t.data)).join('')), - transactions.map((t) => trimLeading0x(t.data).length / 2), - { from: signer } - ) - - assert.equal( - returnValues[0], - // return values are padded to have a length of 32 bytes - `0x${signerAddrReturned.padStart(64, '0')}${boolTrueReturned.padStart(64, '0')}` - ) - for (const i of returnValues[1].keys()) { - if (i === viewSignerTxIndex || i === isOwnerTxIndex) { - assert.equal(web3.utils.hexToNumber(returnValues[1][i]), 32) - } else { - assert.equal(web3.utils.hexToNumber(returnValues[1][i]), 0) - } - } - }) - }) - - describe('when the data parameter has extra bytes appended', () => { - it('reverts', async () => { - await assertTransactionRevertWithReason( - wallet.executeTransactions( - transactions.map((t) => t.destination), - transactions.map((t) => t.value), - ensureLeading0x(transactions.map((t) => trimLeading0x(t.data)).join('deadbeef')), - transactions.map((t) => trimLeading0x(t.data).length / 2), - { from: signer } - ), - 'data cannot have extra bytes appended' - ) - }) - }) - - describe('when dataLengths has erroneous lengths', () => { - it('reverts', async () => { - await assertTransactionRevertWithoutReason( - wallet.executeTransactions( - transactions.map((t) => t.destination), - transactions.map((t) => t.value), - ensureLeading0x(transactions.map((t) => trimLeading0x(t.data)).join('deadbeef')), - transactions.map((t) => trimLeading0x(t.data).length), // invalid lengths without /2 - { from: signer } - ) - ) - }) - }) - - describe('when the transactions are executed as a meta-transaction', () => { - beforeEach(async () => { - // @ts-ignore - const data = wallet.contract.methods - .executeTransactions( - transactions.map((t) => t.destination), - transactions.map((t) => t.value), - ensureLeading0x(transactions.map((t) => trimLeading0x(t.data)).join('')), - transactions.map((t) => trimLeading0x(t.data).length / 2) - ) - .encodeABI() - const { v, r, s } = await getSignatureForMetaTransaction(signer, wallet.address, { - destination: wallet.address, - value: 0, - data, - nonce: 0, - }) - await wallet.executeMetaTransaction(wallet.address, 0, data, v, r, s, { - from: signer, - }) - }) - - it('should execute the transactions', async () => { - assert.equal(await web3.eth.getBalance(transactions[0].destination), value) - assert.equal(await web3.eth.getBalance(transactions[2].destination), value) - assert.equal(await wallet.signer(), nonSigner) - }) - - it('should increment the nonce', async () => { - assertEqualBN(await wallet.nonce(), 1) - }) - }) - }) - }) - }) - - describe('#getMetaTransactionDigest', () => { - it('creates the digest as expected', async () => { - const value = 100 - const destination = web3.utils.toChecksumAddress(web3.utils.randomHex(20)) - const data = '0x' - const nonce = 0 - - const digest = constructMetaTransactionExecutionDigest(wallet.address, { - value, - destination, - data, - nonce, - }) - - assert.equal(await wallet.getMetaTransactionDigest(destination, value, data, nonce), digest) - }) - }) - - describe('#executeMetaTransaction()', () => { - const value = 100 - const destination = web3.utils.toChecksumAddress(web3.utils.randomHex(20)) - const data = '0x' - let submitter - let nonce - let transferSigner - - const doTransfer = async () => { - const { v, r, s } = await getSignatureForMetaTransaction(transferSigner, wallet.address, { - value, - destination, - data, - nonce, - }) - - return wallet.executeMetaTransaction(destination, value, data, v, r, s, { - from: submitter, - }) - } - - beforeEach(async () => { - // Transfer some funds to the wallet - await web3.eth.sendTransaction({ from: accounts[0], to: wallet.address, value }) - }) - - describe('when submitted by a non-signer', () => { - beforeEach(() => { - submitter = nonSigner - }) - - describe('when the nonce is valid', () => { - beforeEach(() => { - nonce = 0 - }) - - let res: any - describe('when signed by the signer', () => { - beforeEach(async () => { - transferSigner = signer - res = await doTransfer() - }) - - it('should execute the transaction', async () => { - assert.equal(await web3.eth.getBalance(destination), value) - }) - - it('should increment the nonce', async () => { - assertEqualBN(await wallet.nonce(), 1) - }) - - it('should emit the MetaTransactionExecution event', () => { - assertLogMatches2(res.logs[0], { - event: 'MetaTransactionExecution', - args: { - destination, - value, - data: null, - nonce: 0, - returnData: null, - }, - }) - }) - }) - - describe('when signed by a non-signer', () => { - it('should revert', async () => { - transferSigner = nonSigner - await assertTransactionRevertWithReason(doTransfer(), 'Invalid meta-transaction signer') - }) - }) - }) - - describe('when the nonce is invalid', () => { - beforeEach(() => { - nonce = 1 - }) - describe('when signed by the signer', () => { - beforeEach(() => { - transferSigner = signer - }) - it('should revert', async () => { - await assertTransactionRevertWithReason(doTransfer(), 'Invalid meta-transaction signer') - }) - }) - }) - }) - }) -}) diff --git a/packages/protocol/test/common/metatransactionwalletdeployer.ts b/packages/protocol/test/common/metatransactionwalletdeployer.ts deleted file mode 100644 index 7866974f51c..00000000000 --- a/packages/protocol/test/common/metatransactionwalletdeployer.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - MetaTransactionWalletContract, - MetaTransactionWalletDeployerContract, - MetaTransactionWalletDeployerInstance, - MetaTransactionWalletInstance, - ProxyContract, -} from 'types' - -const MetaTransactionWalletDeployer: MetaTransactionWalletDeployerContract = artifacts.require( - 'MetaTransactionWalletDeployer' -) - -const MetaTransactionWallet: MetaTransactionWalletContract = - artifacts.require('MetaTransactionWallet') - -const Proxy: ProxyContract = artifacts.require('Proxy') - -contract('MetaTransactionWalletDeployer', (accounts: string[]) => { - let deployer: MetaTransactionWalletDeployerInstance - const valoraAccount = accounts[1] - - beforeEach(async () => { - deployer = await MetaTransactionWalletDeployer.new() - }) - - describe('#deploy', async () => { - let implementation: MetaTransactionWalletInstance - let deployRes - let walletDeployedEvent - - before(async () => { - implementation = await MetaTransactionWallet.new(true) - }) - - beforeEach(async () => { - // @ts-ignore - deployRes = await deployer.deploy( - valoraAccount, - implementation.address, - // @ts-ignore - implementation.contract.methods.initialize(valoraAccount).encodeABI() - ) - walletDeployedEvent = deployRes.logs.find((log) => log.event === 'WalletDeployed') - }) - - it('deploys a wallet', async () => { - assert.exists(walletDeployedEvent) - assert.equal(walletDeployedEvent.args.owner, valoraAccount) - assert.equal(walletDeployedEvent.args.implementation, implementation.address) - }) - - it('initializes the wallet with the correct signer', async () => { - const wallet = await MetaTransactionWallet.at(walletDeployedEvent.args.wallet) - assert.equal(await wallet.signer(), valoraAccount) - }) - - it('sets the the right proxy implementation', async () => { - const proxy = await Proxy.at(walletDeployedEvent.args.wallet) - assert.equal(await proxy._getImplementation(), implementation.address) - }) - - describe('when the external account already owns a wallet', async () => { - beforeEach(async () => { - await deployer.deploy( - valoraAccount, - implementation.address, - // @ts-ignore - implementation.contract.methods.initialize(valoraAccount).encodeABI() - ) - deployRes = await deployer.deploy( - valoraAccount, - implementation.address, - // @ts-ignore - implementation.contract.methods.initialize(valoraAccount).encodeABI() - ) - walletDeployedEvent = deployRes.logs.find((log) => log.event === 'WalletDeployed') - }) - - it('does redeploy', async () => { - assert.exists(walletDeployedEvent) - assert.equal(walletDeployedEvent.args.owner, valoraAccount) - assert.equal(walletDeployedEvent.args.implementation, implementation.address) - }) - }) - }) -})