From f17640032afc129204a1474c7e3e1d6ad0011a45 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 7 Oct 2024 03:56:59 -0700 Subject: [PATCH 01/23] test: helper base for e2e tests (#215) ## Description Base test contract for forked tests in e2e tests ## Test Plan CI ## Related Issues --- .github/workflows/test.yml | 4 + foundry.toml | 2 +- test/e2e/ERC20Mintable.sol | 17 +++ test/e2e/Helper.sol | 221 +++++++++++++++++++++++++++++++++++++ test/e2e/WETH9.sol | 62 +++++++++++ test/src/EIP173Proxy.sol | 107 ++++++++++++++++++ test/src/ERC20Mintable.sol | 1 + 7 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 test/e2e/ERC20Mintable.sol create mode 100644 test/e2e/Helper.sol create mode 100644 test/e2e/WETH9.sol create mode 100644 test/src/EIP173Proxy.sol create mode 120000 test/src/ERC20Mintable.sol diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f8b25ba..fb71e41b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,6 +31,10 @@ jobs: name: Foundry project runs-on: ubuntu-latest + env: + # should support archive requests + FORK_URL: 'https://eth.llamarpc.com' + FORK_BLOCK_NUMBER: 20691292 steps: - uses: actions/checkout@v4 with: diff --git a/foundry.toml b/foundry.toml index ef318bbb..a0113718 100644 --- a/foundry.toml +++ b/foundry.toml @@ -14,7 +14,7 @@ deny_warnings = true fs_permissions = [ { access = "read", path = "./balancer"}, { access = "read", path = "./networks.json"}, - { access = "read", path = "./out"} + { access = "read", path = "./out"}, ] [fmt] diff --git a/test/e2e/ERC20Mintable.sol b/test/e2e/ERC20Mintable.sol new file mode 100644 index 00000000..30b53884 --- /dev/null +++ b/test/e2e/ERC20Mintable.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// solhint-disable-next-line compiler-version +pragma solidity ^0.7; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract ERC20Mintable is ERC20 { + constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {} + + function mint(address account, uint256 amount) external { + _mint(account, amount); + } + + function burn(uint256 amount) external { + _burn(msg.sender, amount); + } +} diff --git a/test/e2e/Helper.sol b/test/e2e/Helper.sol new file mode 100644 index 00000000..a0f7521d --- /dev/null +++ b/test/e2e/Helper.sol @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Test, Vm, stdJson} from "forge-std/Test.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; + +import {GPv2AllowListAuthentication} from "src/contracts/GPv2AllowListAuthentication.sol"; +import { + GPv2Authentication, + GPv2Interaction, + GPv2Settlement, + GPv2Trade, + IERC20, + IVault +} from "src/contracts/GPv2Settlement.sol"; + +import {WETH9} from "./WETH9.sol"; +import {SettlementEncoder} from "test/libraries/encoders/SettlementEncoder.sol"; +import {SwapEncoder} from "test/libraries/encoders/SwapEncoder.sol"; + +address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; +address constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; + +interface IAuthorizer { + function grantRole(bytes32, address) external; + function canPerform(bytes32 actionId, address account, address where) external view returns (bool); +} + +interface IERC20Mintable is IERC20 { + function mint(address, uint256) external; + function burn(uint256) external; +} + +interface IBalancerVault is IVault { + function getAuthorizer() external view returns (address); +} + +// solhint-disable func-name-mixedcase +// solhint-disable max-states-count +abstract contract Helper is Test { + using stdJson for string; + using SettlementEncoder for SettlementEncoder.State; + + address internal deployer; + address internal owner; + GPv2Settlement internal settlement; + bytes32 internal domainSeparator; + GPv2Authentication internal authenticator; + IVault internal vault; + GPv2AllowListAuthentication internal allowList; + GPv2AllowListAuthentication internal allowListImpl; + address vaultRelayer; + address balancerVaultAuthorizer; + + SettlementEncoder.State internal encoder; + SwapEncoder.State internal swapEncoder; + + address internal solver; + Vm.Wallet internal trader; + + bool immutable isForked; + uint256 forkId; + + WETH9 weth; + + bytes32 constant SALT = "Mattresses in Berlin!"; + + constructor(bool _isForked) { + isForked = _isForked; + } + + function setUp() public virtual { + if (isForked) { + uint256 blockNumber = vm.envUint("FORK_BLOCK_NUMBER"); + string memory forkUrl; + + try vm.envString("FORK_URL") returns (string memory url) { + forkUrl = url; + } catch { + forkUrl = "https://eth.llamarpc.com"; + } + + forkId = vm.createSelectFork(forkUrl, blockNumber); + weth = WETH9(payable(WETH)); + } else { + weth = new WETH9(); + } + + // Configure addresses + deployer = makeAddr("E2E.Helper: deployer"); + owner = makeAddr("E2E.Helper: owner"); + solver = makeAddr("E2E.Helper: solver"); + vm.startPrank(deployer); + + // Deploy the allowlist manager + allowListImpl = new GPv2AllowListAuthentication{salt: SALT}(); + allowList = GPv2AllowListAuthentication( + deployProxy(address(allowListImpl), owner, abi.encodeCall(allowListImpl.initializeManager, (owner)), SALT) + ); + authenticator = allowList; + + (balancerVaultAuthorizer, vault) = _deployBalancerVault(); + + // Deploy the settlement contract + settlement = new GPv2Settlement{salt: SALT}(authenticator, vault); + vaultRelayer = address(settlement.vaultRelayer()); + + // Reset the prank + vm.stopPrank(); + + _grantBalancerRolesToRelayer(balancerVaultAuthorizer, address(vault), vaultRelayer); + + // By default, allow `solver` to settle + vm.prank(owner); + allowList.addSolver(solver); + + // Configure default encoders + encoder = SettlementEncoder.makeSettlementEncoder(); + swapEncoder = SwapEncoder.makeSwapEncoder(); + + // Set the domain separator + domainSeparator = settlement.domainSeparator(); + + // Create wallets + trader = vm.createWallet("E2E.Helper: trader"); + } + + function settle(SettlementEncoder.EncodedSettlement memory _settlement) internal { + settlement.settle(_settlement.tokens, _settlement.clearingPrices, _settlement.trades, _settlement.interactions); + } + + function swap(SwapEncoder.EncodedSwap memory _swap) internal { + settlement.swap(_swap.swaps, _swap.tokens, _swap.trade); + } + + function emptySettlement() internal pure returns (SettlementEncoder.EncodedSettlement memory) { + return SettlementEncoder.EncodedSettlement({ + tokens: new IERC20[](0), + clearingPrices: new uint256[](0), + trades: new GPv2Trade.Data[](0), + interactions: [new GPv2Interaction.Data[](0), new GPv2Interaction.Data[](0), new GPv2Interaction.Data[](0)] + }); + } + + function _deployBalancerVault() internal returns (address, IBalancerVault) { + if (isForked) { + IBalancerVault balancerVault = IBalancerVault(BALANCER_VAULT); + address authorizer = balancerVault.getAuthorizer(); + return (authorizer, balancerVault); + } else { + bytes memory authorizerInitCode = abi.encodePacked(_getBalancerBytecode("Authorizer"), abi.encode(owner)); + address authorizer = _create(authorizerInitCode, 0); + + bytes memory vaultInitCode = + abi.encodePacked(_getBalancerBytecode("Vault"), abi.encode(authorizer, address(weth), 0, 0)); + address deployedVault = _create(vaultInitCode, 0); + + return (authorizer, IBalancerVault(deployedVault)); + } + } + + function _grantBalancerRolesToRelayer(address authorizer, address deployedVault, address relayer) internal { + _grantBalancerActionRole( + authorizer, deployedVault, relayer, "manageUserBalance((uint8,address,uint256,address,address)[])" + ); + _grantBalancerActionRole( + authorizer, + deployedVault, + relayer, + "batchSwap(uint8,(bytes32,uint256,uint256,uint256,bytes)[],address[],(address,bool,address,bool),int256[],uint256)" + ); + } + + function _grantBalancerActionRole(address authorizer, address balVault, address to, string memory action) + internal + { + bytes32 actionId = _getActionId(action, balVault); + vm.mockCall( + address(authorizer), abi.encodeCall(IAuthorizer.canPerform, (actionId, to, balVault)), abi.encode(true) + ); + } + + function _getActionId(string memory fnDef, address vaultAddr) internal pure returns (bytes32) { + bytes32 hash = keccak256(bytes(fnDef)); + bytes4 selector = bytes4(hash); + return keccak256(abi.encodePacked(uint256(uint160(vaultAddr)), selector)); + } + + function _getBalancerBytecode(string memory artifactName) internal view returns (bytes memory) { + string memory data = vm.readFile(string(abi.encodePacked("balancer/", artifactName, ".json"))); + return vm.parseJsonBytes(data, ".bytecode"); + } + + function _create(bytes memory initCode, uint256 value) internal returns (address deployed) { + assembly ("memory-safe") { + deployed := create(value, add(initCode, 0x20), mload(initCode)) + } + require(deployed != address(0), "deployment failed"); + } + + function _create2(bytes memory initCode, uint256 value, bytes32 salt) internal returns (address deployed) { + assembly ("memory-safe") { + deployed := create2(value, add(initCode, 0x20), mload(initCode), salt) + } + require(deployed != address(0), "deployment failed"); + } + + function deployMintableErc20(string memory name, string memory symbol) internal returns (IERC20Mintable token) { + // need to use like this because OZ requires ^0.7 and tests are on ^0.8 + bytes memory initCode = abi.encodePacked(vm.getCode("ERC20Mintable"), abi.encode(name, symbol)); + token = IERC20Mintable(_create(initCode, 0)); + } + + function deployProxy(address implAddress, address ownerAddress, bytes memory data, bytes32 salt) + internal + returns (address proxy) + { + proxy = + _create2(abi.encodePacked(vm.getCode("EIP173Proxy"), abi.encode(implAddress, ownerAddress, data)), 0, salt); + } +} diff --git a/test/e2e/WETH9.sol b/test/e2e/WETH9.sol new file mode 100644 index 00000000..71f4586a --- /dev/null +++ b/test/e2e/WETH9.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +contract WETH9 { + string public name = "Wrapped Ether"; + string public symbol = "WETH"; + uint8 public decimals = 18; + + event Approval(address indexed src, address indexed guy, uint256 wad); + event Transfer(address indexed src, address indexed dst, uint256 wad); + event Deposit(address indexed dst, uint256 wad); + event Withdrawal(address indexed src, uint256 wad); + + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + receive() external payable { + deposit(); + } + + function deposit() public payable { + balanceOf[msg.sender] += msg.value; + emit Deposit(msg.sender, msg.value); + } + + function withdraw(uint256 wad) public { + require(balanceOf[msg.sender] >= wad); + balanceOf[msg.sender] -= wad; + payable(msg.sender).transfer(wad); + emit Withdrawal(msg.sender, wad); + } + + function totalSupply() public view returns (uint256) { + return address(this).balance; + } + + function approve(address guy, uint256 wad) public returns (bool) { + allowance[msg.sender][guy] = wad; + emit Approval(msg.sender, guy, wad); + return true; + } + + function transfer(address dst, uint256 wad) public returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint256 wad) public returns (bool) { + require(balanceOf[src] >= wad); + + if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { + require(allowance[src][msg.sender] >= wad); + allowance[src][msg.sender] -= wad; + } + + balanceOf[src] -= wad; + balanceOf[dst] += wad; + + emit Transfer(src, dst, wad); + + return true; + } +} diff --git a/test/src/EIP173Proxy.sol b/test/src/EIP173Proxy.sol new file mode 100644 index 00000000..b4a71ee4 --- /dev/null +++ b/test/src/EIP173Proxy.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// solhint-disable-next-line compiler-version +pragma solidity ^0.7; + +import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; + +interface IERC165 { + function supportsInterface(bytes4 id) external view returns (bool); +} + +// ref: https://github.com/wighawag/hardhat-deploy/blob/e0ffcf9e7dc92b246e832c4d175f9dbd8b6df14d/solc_0.8/proxy/EIP173Proxy.sol +contract EIP173Proxy is Proxy { + bytes32 constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 constant OWNER_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + modifier onlyOwner() { + require(msg.sender == _owner(), "NOT_AUTHORIZED"); + _; + } + + constructor(address implAddress, address ownerAddress, bytes memory data) { + _setOwner(ownerAddress); + _setImplementation(implAddress, data); + } + + function owner() external view returns (address) { + return _owner(); + } + + function supportsInterface(bytes4 id) external view returns (bool) { + if (id == 0x01ffc9a7 || id == 0x7f5828d0) { + return true; + } + if (id == 0xFFFFFFFF) { + return false; + } + + IERC165 implementation; + assembly { + implementation := sload(IMPLEMENTATION_SLOT) + } + + // Technically this is not standard compliant as ERC-165 require 30,000 gas which that call cannot ensure + // because it is itself inside `supportsInterface` that might only get 30,000 gas. + // In practise this is unlikely to be an issue. + try implementation.supportsInterface(id) returns (bool support) { + return support; + } catch { + return false; + } + } + + function transferOwnership(address newOwner) external onlyOwner { + _setOwner(newOwner); + } + + function upgradeTo(address newImplementation) external onlyOwner { + _setImplementation(newImplementation, ""); + } + + function upgradeToAndCall(address newImplementation, bytes calldata data) external payable onlyOwner { + _setImplementation(newImplementation, data); + } + + function _owner() internal view returns (address adminAddress) { + assembly { + adminAddress := sload(OWNER_SLOT) + } + } + + function _setOwner(address newOwner) internal { + address previousOwner = _owner(); + assembly { + sstore(OWNER_SLOT, newOwner) + } + emit OwnershipTransferred(previousOwner, newOwner); + } + + function _implementation() internal view override returns (address) { + address impl; + assembly { + impl := sload(IMPLEMENTATION_SLOT) + } + return impl; + } + + function _setImplementation(address impl, bytes memory data) internal { + assembly { + sstore(IMPLEMENTATION_SLOT, impl) + } + + if (data.length > 0) { + // solhint-disable-next-line avoid-low-level-calls + (bool success,) = impl.delegatecall(data); + if (!success) { + assembly { + // This assembly ensure the revert contains the exact string data + let returnDataSize := returndatasize() + returndatacopy(0, 0, returnDataSize) + revert(0, returnDataSize) + } + } + } + } +} diff --git a/test/src/ERC20Mintable.sol b/test/src/ERC20Mintable.sol new file mode 120000 index 00000000..c31a1693 --- /dev/null +++ b/test/src/ERC20Mintable.sol @@ -0,0 +1 @@ +../e2e/ERC20Mintable.sol \ No newline at end of file From 884319e11c4ce282a3b11e1cbda9a1b0be8bbddc Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 7 Oct 2024 04:21:01 -0700 Subject: [PATCH 02/23] chore: migrate E2E nonStandardErc20 tests to Foundry (#213) ## Description Migrate nonStandardErc20 e2e test to foundry. Depends on `FoundryTest.sol` introduced in #215 ## Test Plan CI ## Related Issues Closes #138 --- test/e2e/NonStandardErc20.t.sol | 157 ++++++++++++++++++++++++++++++ test/e2e/nonStandardErc20.test.ts | 107 -------------------- test/src/NonStandardERC20.sol | 56 ----------- 3 files changed, 157 insertions(+), 163 deletions(-) create mode 100644 test/e2e/NonStandardErc20.t.sol delete mode 100644 test/e2e/nonStandardErc20.test.ts delete mode 100644 test/src/NonStandardERC20.sol diff --git a/test/e2e/NonStandardErc20.t.sol b/test/e2e/NonStandardErc20.t.sol new file mode 100644 index 00000000..1445d1d9 --- /dev/null +++ b/test/e2e/NonStandardErc20.t.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {Helper} from "./Helper.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; + +import {GPv2Order, GPv2Signing, SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; + +abstract contract NonStandardERC20 { + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + function mint(address to, uint256 amount) external { + balanceOf[to] += amount; + } + + function approve(address spender, uint256 amount) external { + allowance[msg.sender][spender] = amount; + } + + function transfer_(address to, uint256 amount) internal { + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount; + } + + function transferFrom_(address from, address to, uint256 amount) internal { + allowance[from][msg.sender] -= amount; + balanceOf[from] -= amount; + balanceOf[to] += amount; + } +} + +contract ERC20NoReturn is NonStandardERC20 { + function transfer(address to, uint256 amount) external { + transfer_(to, amount); + } + + function transferFrom(address from, address to, uint256 amount) external { + transferFrom_(from, to, amount); + } +} + +contract ERC20ReturningUint is NonStandardERC20 { + // Largest 256-bit prime :) + uint256 private constant OK = 115792089237316195423570985008687907853269984665640564039457584007913129639747; + + function transfer(address to, uint256 amount) external returns (uint256) { + transfer_(to, amount); + return OK; + } + + function transferFrom(address from, address to, uint256 amount) external returns (uint256) { + transferFrom_(from, to, amount); + return OK; + } +} + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +contract NonStandardErc20Test is Helper(false) { + ERC20NoReturn noReturnToken; + ERC20ReturningUint uintReturningToken; + + function setUp() public override { + super.setUp(); + + noReturnToken = new ERC20NoReturn(); + uintReturningToken = new ERC20ReturningUint(); + } + + function test_should_allow_trading_non_standard_erc20_tokens() external { + uint256 amount = 1 ether; + uint256 feeAmount = 0.01 ether; + + Vm.Wallet memory trader1 = vm.createWallet("trader1"); + Vm.Wallet memory trader2 = vm.createWallet("trader2"); + + // mint some noReturnToken tokens to trader1 + noReturnToken.mint(trader1.addr, amount + feeAmount); + vm.prank(trader1.addr); + noReturnToken.approve(vaultRelayer, type(uint256).max); + // place order to swap noReturnToken for uintReturningToken + encoder.signEncodeTrade( + vm, + trader1, + GPv2Order.Data({ + sellToken: IERC20(address(noReturnToken)), + buyToken: IERC20(address(uintReturningToken)), + receiver: trader1.addr, + sellAmount: amount, + buyAmount: amount, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: feeAmount, + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some uintReturningToken tokens to trader2 + uintReturningToken.mint(trader2.addr, amount + feeAmount); + vm.prank(trader2.addr); + uintReturningToken.approve(vaultRelayer, type(uint256).max); + // place order to swap uintReturningToken for noReturnToken + encoder.signEncodeTrade( + vm, + trader2, + GPv2Order.Data({ + sellToken: IERC20(address(uintReturningToken)), + buyToken: IERC20(address(noReturnToken)), + receiver: trader2.addr, + sellAmount: amount, + buyAmount: amount, + validTo: 0xffffffff, + appData: bytes32(uint256(2)), + feeAmount: feeAmount, + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // set token prices + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = IERC20(address(noReturnToken)); + tokens[1] = IERC20(address(uintReturningToken)); + uint256[] memory prices = new uint256[](2); + prices[0] = 1; + prices[1] = 1; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + // settle the orders + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + vm.prank(solver); + settle(encodedSettlement); + + assertEq(noReturnToken.balanceOf(address(settlement)), feeAmount, "order1 fee not charged as expected"); + assertEq(noReturnToken.balanceOf(trader2.addr), amount, "order1 swap output not as expected"); + + assertEq(uintReturningToken.balanceOf(address(settlement)), feeAmount, "order2 fee not charged as expected"); + assertEq(uintReturningToken.balanceOf(trader1.addr), amount, "order2 swap output not as expected"); + } +} diff --git a/test/e2e/nonStandardErc20.test.ts b/test/e2e/nonStandardErc20.test.ts deleted file mode 100644 index 3b638c19..00000000 --- a/test/e2e/nonStandardErc20.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { ethers } from "hardhat"; - -import { - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -describe("E2E: Non-Standard ERC20 Tokens", () => { - let solver: Wallet; - let traders: Wallet[]; - - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let tokens: [Contract, Contract]; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - settlement, - vaultRelayer, - wallets: [solver, ...traders], - } = deployment); - - const { authenticator, manager } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - const ERC20NoReturn = await ethers.getContractFactory("ERC20NoReturn"); - const ERC20ReturningUint = await ethers.getContractFactory( - "ERC20ReturningUint", - ); - tokens = [await ERC20NoReturn.deploy(), await ERC20ReturningUint.deploy()]; - }); - - it("should allow trading non-standard ERC20 tokens", async () => { - // Just trade 1:1 - - const encoder = new SettlementEncoder(domainSeparator); - const amount = ethers.utils.parseEther("1.0"); - const feeAmount = ethers.utils.parseEther("0.01"); - - await tokens[0].mint(traders[0].address, amount.add(feeAmount)); - await tokens[0] - .connect(traders[0]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: tokens[0].address, - buyToken: tokens[1].address, - sellAmount: amount, - buyAmount: amount, - feeAmount, - validTo: 0xffffffff, - appData: 1, - }, - traders[0], - SigningScheme.EIP712, - ); - - await tokens[1].mint(traders[1].address, amount.add(feeAmount)); - await tokens[1] - .connect(traders[1]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - sellToken: tokens[1].address, - buyToken: tokens[0].address, - buyAmount: amount, - sellAmount: amount, - feeAmount, - validTo: 0xffffffff, - appData: 2, - }, - traders[1], - SigningScheme.EIP712, - ); - - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [tokens[0].address]: 1, - [tokens[1].address]: 1, - }), - ); - - expect(await tokens[0].balanceOf(settlement.address)).to.equal(feeAmount); - expect(await tokens[0].balanceOf(traders[1].address)).to.equal(amount); - - expect(await tokens[1].balanceOf(settlement.address)).to.equal(feeAmount); - expect(await tokens[1].balanceOf(traders[0].address)).to.equal(amount); - }); -}); diff --git a/test/src/NonStandardERC20.sol b/test/src/NonStandardERC20.sol deleted file mode 100644 index c91f8843..00000000 --- a/test/src/NonStandardERC20.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// solhint-disable-next-line compiler-version -pragma solidity >=0.7.6 <0.9.0; - -import "src/contracts/libraries/SafeMath.sol"; - -abstract contract NonStandardERC20 { - using SafeMath for uint256; - - mapping(address => uint256) public balanceOf; - mapping(address => mapping(address => uint256)) public allowance; - - function mint(address to, uint256 amount) external { - balanceOf[to] = balanceOf[to].add(amount); - } - - function approve(address spender, uint256 amount) external { - allowance[msg.sender][spender] = amount; - } - - function transfer_(address to, uint256 amount) internal { - balanceOf[msg.sender] = balanceOf[msg.sender].sub(amount); - balanceOf[to] = balanceOf[to].add(amount); - } - - function transferFrom_(address from, address to, uint256 amount) internal { - allowance[from][msg.sender] = allowance[from][msg.sender].sub(amount); - balanceOf[from] = balanceOf[from].sub(amount); - balanceOf[to] = balanceOf[to].add(amount); - } -} - -contract ERC20NoReturn is NonStandardERC20 { - function transfer(address to, uint256 amount) external { - transfer_(to, amount); - } - - function transferFrom(address from, address to, uint256 amount) external { - transferFrom_(from, to, amount); - } -} - -contract ERC20ReturningUint is NonStandardERC20 { - // Largest 256-bit prime :) - uint256 private constant OK = 115792089237316195423570985008687907853269984665640564039457584007913129639747; - - function transfer(address to, uint256 amount) external returns (uint256) { - transfer_(to, amount); - return OK; - } - - function transferFrom(address from, address to, uint256 amount) external returns (uint256) { - transferFrom_(from, to, amount); - return OK; - } -} From efc8caa936664a263b684c52b28a329519bd1373 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 7 Oct 2024 04:23:37 -0700 Subject: [PATCH 03/23] chore: migrate E2E internalBalances test to Foundry (#216) ## Description Migrate the internalBalances e2e test to foundry. Depends on #215. ## Test Plan CI ## Related Issues Closes #137 --- test/e2e/InternalBalances.t.sol | 225 ++++++++++++++++++++++++++++++ test/e2e/internalBalances.test.ts | 213 ---------------------------- 2 files changed, 225 insertions(+), 213 deletions(-) create mode 100644 test/e2e/InternalBalances.t.sol delete mode 100644 test/e2e/internalBalances.test.ts diff --git a/test/e2e/InternalBalances.t.sol b/test/e2e/InternalBalances.t.sol new file mode 100644 index 00000000..77461e26 --- /dev/null +++ b/test/e2e/InternalBalances.t.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {IVault} from "src/contracts/interfaces/IVault.sol"; + +import {GPv2Order, GPv2Signing, SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +interface IBalancerVault is IVault { + function setRelayerApproval(address, address, bool) external; + function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory); + function hasApprovedRelayer(address, address) external view returns (bool); +} + +contract InternalBalancesTest is Helper(false) { + IERC20Mintable token1; + IERC20Mintable token2; + + function setUp() public override { + super.setUp(); + + token1 = deployMintableErc20("TK1", "TK1"); + token2 = deployMintableErc20("TK2", "TK2"); + + vm.startPrank(address(settlement)); + token1.approve(address(vault), type(uint256).max); + token2.approve(address(vault), type(uint256).max); + vm.stopPrank(); + } + + function test_should_settle_orders_buying_and_selling_with_internal_balances() external { + Vm.Wallet memory trader1 = vm.createWallet("trader1"); + Vm.Wallet memory trader2 = vm.createWallet("trader2"); + Vm.Wallet memory trader3 = vm.createWallet("trader3"); + Vm.Wallet memory trader4 = vm.createWallet("trader4"); + + // mint some tokens to trader1 + _mintTokens(token1, trader1.addr, 1.001 ether); + + // approve tokens to the balancer vault and approve the settlement contract to + // be able to spend the balancer internal/external balances + vm.startPrank(trader1.addr); + token1.approve(address(vault), type(uint256).max); + IBalancerVault(address(vault)).setRelayerApproval(trader1.addr, vaultRelayer, true); + vm.stopPrank(); + + // place order for selling 1 token1 for 500 token2 + encoder.signEncodeTrade( + vm, + trader1, + GPv2Order.Data({ + sellToken: token1, + buyToken: token2, + receiver: trader1.addr, + sellAmount: 1 ether, + buyAmount: 500 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 0.001 ether, + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_EXTERNAL, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some tokens to trader2 + _mintTokens(token2, trader2.addr, 300.3 ether); + + // approve tokens to the balancer vault and deposit some tokens to balancer internal + // balance + vm.startPrank(trader2.addr); + token2.approve(address(vault), type(uint256).max); + IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1); + ops[0] = IVault.UserBalanceOp({ + kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL, + asset: token2, + amount: 300.3 ether, + sender: trader2.addr, + recipient: payable(trader2.addr) + }); + vault.manageUserBalance(ops); + IBalancerVault(address(vault)).setRelayerApproval(trader2.addr, vaultRelayer, true); + vm.stopPrank(); + + // place order for buying 0.5 token1 with max 300 token2 + encoder.signEncodeTrade( + vm, + trader2, + GPv2Order.Data({ + sellToken: token2, + buyToken: token1, + receiver: trader2.addr, + sellAmount: 300 ether, + buyAmount: 0.5 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 0.3 ether, + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_INTERNAL, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some tokens to trader3 + _mintTokens(token1, trader3.addr, 2.002 ether); + + // approve the tokens to cow vault relayer + vm.prank(trader3.addr); + token1.approve(vaultRelayer, type(uint256).max); + + // place order for selling 2 token1 for min 1000 token2 + encoder.signEncodeTrade( + vm, + trader3, + GPv2Order.Data({ + sellToken: token1, + buyToken: token2, + receiver: trader3.addr, + sellAmount: 2 ether, + buyAmount: 1000 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 0.002 ether, + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_INTERNAL + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some tokens to trader4 + _mintTokens(token2, trader4.addr, 1501.5 ether); + + // approve tokens to the balancer vault and deposit some tokens to balancer internal + // balance + vm.startPrank(trader4.addr); + token2.approve(address(vault), type(uint256).max); + ops = new IVault.UserBalanceOp[](1); + ops[0] = IVault.UserBalanceOp({ + kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL, + asset: token2, + amount: 1501.5 ether, + sender: trader4.addr, + recipient: payable(trader4.addr) + }); + IBalancerVault(address(vault)).manageUserBalance(ops); + IBalancerVault(address(vault)).setRelayerApproval(trader4.addr, vaultRelayer, true); + vm.stopPrank(); + + // place order to buy 2.5 token1 with max 1500 token2 + encoder.signEncodeTrade( + vm, + trader4, + GPv2Order.Data({ + sellToken: token2, + buyToken: token1, + receiver: trader4.addr, + sellAmount: 1500 ether, + buyAmount: 2.5 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 1.5 ether, + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_INTERNAL, + buyTokenBalance: GPv2Order.BALANCE_INTERNAL + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // set token prices + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = token1; + tokens[1] = token2; + uint256[] memory prices = new uint256[](2); + prices[0] = 550; + prices[1] = 1; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + // settle the orders + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + vm.prank(solver); + settle(encodedSettlement); + + assertEq(token2.balanceOf(trader1.addr), 550 ether, "trader1 amountOut not as expected"); + assertEq(token1.balanceOf(trader2.addr), 0.5 ether, "trader2 amountOut not as expected"); + assertEq(_getInternalBalance(address(token2), trader3.addr), 1100 ether, "trader3 amountOut not as expected"); + assertEq(_getInternalBalance(address(token1), trader4.addr), 2.5 ether, "trader4 amountOut not as expected"); + + assertEq(token1.balanceOf(address(settlement)), 0.003 ether, "token1 settlement fee amount not as expected"); + assertEq(token2.balanceOf(address(settlement)), 1.8 ether, "token2 settlement fee amount not as expected"); + } + + function _mintTokens(IERC20Mintable token, address to, uint256 amt) internal { + token.mint(to, amt); + } + + function _getInternalBalance(address token, address who) internal view returns (uint256) { + IERC20[] memory tokens = new IERC20[](1); + tokens[0] = IERC20(token); + uint256[] memory bals = IBalancerVault(address(vault)).getInternalBalance(who, tokens); + return bals[0]; + } +} diff --git a/test/e2e/internalBalances.test.ts b/test/e2e/internalBalances.test.ts deleted file mode 100644 index 8cfdedb6..00000000 --- a/test/e2e/internalBalances.test.ts +++ /dev/null @@ -1,213 +0,0 @@ -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { - OrderBalance, - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, - grantRequiredRoles, -} from "../../src/ts"; -import { UserBalanceOpKind } from "../balancer"; - -import { deployTestContracts } from "./fixture"; - -describe("E2E: Should allow trading with Vault internal balances", () => { - let deployer: Wallet; - let solver: Wallet; - let traders: Wallet[]; - - let vault: Contract; - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let tokens: [Contract, Contract]; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - vault, - settlement, - vaultRelayer, - wallets: [solver, ...traders], - } = deployment); - - const { vaultAuthorizer, authenticator, manager } = deployment; - await grantRequiredRoles( - vaultAuthorizer.connect(manager), - vault.address, - vaultRelayer.address, - ); - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - tokens = [ - await waffle.deployContract(deployer, ERC20, ["T0", 18]), - await waffle.deployContract(deployer, ERC20, ["T1", 18]), - ]; - - await settlement.connect(solver).settle( - ...SettlementEncoder.encodedSetup( - ...tokens.map((token) => ({ - target: token.address, - callData: token.interface.encodeFunctionData("approve", [ - vault.address, - ethers.constants.MaxUint256, - ]), - })), - ), - ); - }); - - it("should settle orders buying and selling with internal balances", async () => { - const encoder = new SettlementEncoder(domainSeparator); - - await tokens[0].mint(traders[0].address, ethers.utils.parseEther("1.001")); - await tokens[0] - .connect(traders[0]) - .approve(vault.address, ethers.constants.MaxUint256); - await vault - .connect(traders[0]) - .setRelayerApproval(traders[0].address, vaultRelayer.address, true); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: tokens[0].address, - buyToken: tokens[1].address, - sellAmount: ethers.utils.parseEther("1.0"), - buyAmount: ethers.utils.parseEther("500.0"), - feeAmount: ethers.utils.parseEther("0.001"), - validTo: 0xffffffff, - appData: 1, - sellTokenBalance: OrderBalance.EXTERNAL, - }, - traders[0], - SigningScheme.EIP712, - ); - - await tokens[1].mint(traders[1].address, ethers.utils.parseEther("300.3")); - await tokens[1] - .connect(traders[1]) - .approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(traders[1]).manageUserBalance([ - { - kind: UserBalanceOpKind.DEPOSIT_INTERNAL, - asset: tokens[1].address, - amount: ethers.utils.parseEther("300.3"), - sender: traders[1].address, - recipient: traders[1].address, - }, - ]); - await vault - .connect(traders[1]) - .setRelayerApproval(traders[1].address, vaultRelayer.address, true); - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - buyToken: tokens[0].address, - sellToken: tokens[1].address, - buyAmount: ethers.utils.parseEther("0.5"), - sellAmount: ethers.utils.parseEther("300.0"), - feeAmount: ethers.utils.parseEther("0.3"), - validTo: 0xffffffff, - appData: 2, - sellTokenBalance: OrderBalance.INTERNAL, - }, - traders[1], - SigningScheme.EIP712, - ); - - await tokens[0].mint(traders[2].address, ethers.utils.parseEther("2.002")); - await tokens[0] - .connect(traders[2]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: tokens[0].address, - buyToken: tokens[1].address, - sellAmount: ethers.utils.parseEther("2.0"), - buyAmount: ethers.utils.parseEther("1000.0"), - feeAmount: ethers.utils.parseEther("0.002"), - validTo: 0xffffffff, - appData: 2, - buyTokenBalance: OrderBalance.INTERNAL, - }, - traders[2], - SigningScheme.EIP712, - ); - - await tokens[1].mint(traders[3].address, ethers.utils.parseEther("1501.5")); - await tokens[1] - .connect(traders[3]) - .approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(traders[3]).manageUserBalance([ - { - kind: UserBalanceOpKind.DEPOSIT_INTERNAL, - asset: tokens[1].address, - amount: ethers.utils.parseEther("1501.5"), - sender: traders[3].address, - recipient: traders[3].address, - }, - ]); - await vault - .connect(traders[3]) - .setRelayerApproval(traders[3].address, vaultRelayer.address, true); - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - buyToken: tokens[0].address, - sellToken: tokens[1].address, - buyAmount: ethers.utils.parseEther("2.5"), - sellAmount: ethers.utils.parseEther("1500.0"), - feeAmount: ethers.utils.parseEther("1.5"), - validTo: 0xffffffff, - appData: 2, - sellTokenBalance: OrderBalance.INTERNAL, - buyTokenBalance: OrderBalance.INTERNAL, - }, - traders[3], - SigningScheme.EIP712, - ); - - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [tokens[0].address]: 550, - [tokens[1].address]: 1, - }), - ); - - expect(await tokens[1].balanceOf(traders[0].address)).to.equal( - ethers.utils.parseEther("550.0"), - ); - expect(await tokens[0].balanceOf(traders[1].address)).to.equal( - ethers.utils.parseEther("0.5"), - ); - expect( - await vault.getInternalBalance(traders[2].address, [tokens[1].address]), - ).to.deep.equal([ethers.utils.parseEther("1100")]); - expect( - await vault.getInternalBalance(traders[3].address, [tokens[0].address]), - ).to.deep.equal([ethers.utils.parseEther("2.5")]); - - expect(await tokens[0].balanceOf(settlement.address)).to.equal( - ethers.utils.parseEther("0.003"), - ); - expect(await tokens[1].balanceOf(settlement.address)).to.equal( - ethers.utils.parseEther("1.8"), - ); - }); -}); From 59c3410d054d86befcd78b5274369c02bbd02260 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 7 Oct 2024 04:24:10 -0700 Subject: [PATCH 04/23] chore: migrate E2E 0xTrade test to Foundry (#219) ## Description Migrate the 0xTrade e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #131 #132 --- package.json | 1 - test/e2e/0xTrade.test.ts | 188 ----------------------------------- test/e2e/zero-ex/index.ts | 9 -- test/e2e/zero-ex/v2/index.ts | 153 ---------------------------- yarn.lock | 5 - 5 files changed, 356 deletions(-) delete mode 100644 test/e2e/0xTrade.test.ts delete mode 100644 test/e2e/zero-ex/index.ts delete mode 100644 test/e2e/zero-ex/v2/index.ts diff --git a/package.json b/package.json index 0043addc..cd7d1fe4 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "ethers": "^5.4.0" }, "devDependencies": { - "@0x/contract-artifacts-v2": "npm:@0x/contract-artifacts@^2.2.2", "@gnosis.pm/safe-contracts": "^1.3.0", "@nomicfoundation/hardhat-verify": "^2.0.1", "@nomiclabs/hardhat-ethers": "^2.2.3", diff --git a/test/e2e/0xTrade.test.ts b/test/e2e/0xTrade.test.ts deleted file mode 100644 index 75bcc30a..00000000 --- a/test/e2e/0xTrade.test.ts +++ /dev/null @@ -1,188 +0,0 @@ -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import { expect } from "chai"; -import Debug from "debug"; -import { BigNumber, Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { - Order, - OrderKind, - Prices, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; -import { SimpleOrder as ZeroExSimpleOrder } from "./zero-ex"; -import * as ZeroExV2 from "./zero-ex/v2"; - -const debug = Debug("test:e2e:0xTrade"); - -describe("E2E: Can settle a 0x trade", () => { - let deployer: Wallet; - let solver: Wallet; - let trader: Wallet; - let marketMaker: Wallet; - - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let owl: Contract; - let gno: Contract; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - settlement, - vaultRelayer, - wallets: [solver, trader, marketMaker], - } = deployment); - - const { authenticator, manager } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - owl = await waffle.deployContract(deployer, ERC20, ["OWL", 18]); - gno = await waffle.deployContract(deployer, ERC20, ["GNO", 18]); - }); - - function generateSettlementSolution(): { - gpv2Order: Order; - zeroExOrder: ZeroExSimpleOrder; - zeroExTakerAmount: BigNumber; - clearingPrices: Prices; - gpv2OwlSurplus: BigNumber; - zeroExOwlSurplus: BigNumber; - } { - const gpv2Order = { - kind: OrderKind.BUY, - partiallyFillable: false, - buyToken: gno.address, - sellToken: owl.address, - buyAmount: ethers.utils.parseEther("1.0"), - sellAmount: ethers.utils.parseEther("130.0"), - feeAmount: ethers.utils.parseEther("10.0"), - validTo: 0xffffffff, - appData: 1, - }; - - const zeroExGnoPrice = 110; - const zeroExOrder = { - takerAddress: settlement.address, - makerAssetAddress: gno.address, - makerAssetAmount: ethers.utils.parseEther("1000.0"), - takerAssetAddress: owl.address, - takerAssetAmount: ethers.utils.parseEther("1000.0").mul(zeroExGnoPrice), - }; - const zeroExTakerAmount = gpv2Order.buyAmount.mul(zeroExGnoPrice); - - const gpv2GnoPrice = 120; - const clearingPrices = { - [owl.address]: 1, - [gno.address]: gpv2GnoPrice, - }; - - const gpv2OwlSurplus = gpv2Order.sellAmount.sub( - gpv2Order.buyAmount.mul(gpv2GnoPrice), - ); - const zeroExOwlSurplus = gpv2Order.buyAmount.mul( - gpv2GnoPrice - zeroExGnoPrice, - ); - - return { - gpv2Order, - zeroExOrder, - zeroExTakerAmount, - clearingPrices, - gpv2OwlSurplus, - zeroExOwlSurplus, - }; - } - - describe("0x Protocol v2", () => { - it("should settle an EOA trade with a 0x trade", async () => { - // Settles a market order buying 1 GNO for 120 OWL and get matched with a - // market maker using 0x orders. - - const { - gpv2Order, - zeroExOrder, - zeroExTakerAmount, - clearingPrices, - gpv2OwlSurplus, - zeroExOwlSurplus, - } = generateSettlementSolution(); - - await owl.mint(trader.address, ethers.utils.parseEther("140")); - await owl - .connect(trader) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - - const zeroEx = await ZeroExV2.deployExchange(deployer); - - await gno.mint(marketMaker.address, ethers.utils.parseEther("1000.0")); - await gno - .connect(marketMaker) - .approve(zeroEx.erc20Proxy.address, ethers.constants.MaxUint256); - - const zeroExSignedOrder = await ZeroExV2.signSimpleOrder( - marketMaker, - zeroEx.domainSeparator, - zeroExOrder, - ); - expect( - await zeroEx.exchange.isValidSignature( - zeroExSignedOrder.hash, - marketMaker.address, - zeroExSignedOrder.signature, - ), - ).to.be.true; - - const encoder = new SettlementEncoder(domainSeparator); - await encoder.signEncodeTrade(gpv2Order, trader, SigningScheme.EIP712); - encoder.encodeInteraction({ - target: owl.address, - callData: owl.interface.encodeFunctionData("approve", [ - zeroEx.erc20Proxy.address, - zeroExTakerAmount, - ]), - }); - encoder.encodeInteraction({ - target: zeroEx.exchange.address, - callData: zeroEx.exchange.interface.encodeFunctionData("fillOrder", [ - zeroExSignedOrder.order, - zeroExTakerAmount, - zeroExSignedOrder.signature, - ]), - }); - - const tx = await settlement - .connect(solver) - .settle(...encoder.encodedSettlement(clearingPrices)); - - const { gasUsed } = await tx.wait(); - debug(`gas used: ${gasUsed}`); - - expect(await gno.balanceOf(trader.address)).to.deep.equal( - ethers.utils.parseEther("1.0"), - ); - expect(await gno.balanceOf(marketMaker.address)).to.deep.equal( - ethers.utils.parseEther("999.0"), - ); - - // NOTE: The user keeps the surplus from their trade. - expect(await owl.balanceOf(trader.address)).to.deep.equal(gpv2OwlSurplus); - // NOTE: The exchange keeps the surplus from the 0x order. - expect(await owl.balanceOf(settlement.address)).to.deep.equal( - zeroExOwlSurplus.add(gpv2Order.feeAmount), - ); - }); - }); -}); diff --git a/test/e2e/zero-ex/index.ts b/test/e2e/zero-ex/index.ts deleted file mode 100644 index 5cae98f5..00000000 --- a/test/e2e/zero-ex/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { BigNumberish, BytesLike } from "ethers"; - -export interface SimpleOrder { - takerAddress: string; - makerAssetAmount: BigNumberish; - takerAssetAmount: BigNumberish; - makerAssetAddress: BytesLike; - takerAssetAddress: BytesLike; -} diff --git a/test/e2e/zero-ex/v2/index.ts b/test/e2e/zero-ex/v2/index.ts deleted file mode 100644 index e25924d3..00000000 --- a/test/e2e/zero-ex/v2/index.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { ERC20Proxy, Exchange, ZRXToken } from "@0x/contract-artifacts-v2"; -import { BigNumberish, BytesLike, Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { SimpleOrder } from ".."; -import { TypedDataDomain } from "../../../../src/ts"; - -// NOTE: Order type from: -// -export interface Order { - makerAddress: string; - takerAddress: string; - feeRecipientAddress: string; - senderAddress: string; - makerAssetAmount: BigNumberish; - takerAssetAmount: BigNumberish; - makerFee: BigNumberish; - takerFee: BigNumberish; - expirationTimeSeconds: BigNumberish; - salt: BigNumberish; - makerAssetData: BytesLike; - takerAssetData: BytesLike; -} - -const ORDER_TYPE_DESCRIPTOR = { - Order: [ - { name: "makerAddress", type: "address" }, - { name: "takerAddress", type: "address" }, - { name: "feeRecipientAddress", type: "address" }, - { name: "senderAddress", type: "address" }, - { name: "makerAssetAmount", type: "uint256" }, - { name: "takerAssetAmount", type: "uint256" }, - { name: "makerFee", type: "uint256" }, - { name: "takerFee", type: "uint256" }, - { name: "expirationTimeSeconds", type: "uint256" }, - { name: "salt", type: "uint256" }, - { name: "makerAssetData", type: "bytes" }, - { name: "takerAssetData", type: "bytes" }, - ], -}; - -export interface SignedOrder { - order: Order; - hash: BytesLike; - signature: BytesLike; -} - -function encodeErc20AssetData(tokenAddress: BytesLike): string { - // NOTE: ERC20 proxy asset data defined in: - // - - const { id, hexDataSlice } = ethers.utils; - const PROXY_ID = hexDataSlice(id("ERC20Token(address)"), 0, 4); - - return ethers.utils.hexConcat([ - PROXY_ID, - ethers.utils.defaultAbiCoder.encode(["address"], [tokenAddress]), - ]); -} - -export async function signSimpleOrder( - maker: Wallet, - domain: TypedDataDomain, - simpleOrder: SimpleOrder, -): Promise { - const order = { - ...simpleOrder, - makerAddress: maker.address, - makerAssetData: encodeErc20AssetData(simpleOrder.makerAssetAddress), - takerAssetData: encodeErc20AssetData(simpleOrder.takerAssetAddress), - - // NOTE: Unused. - expirationTimeSeconds: 0xffffffff, - salt: ethers.constants.Zero, - - // NOTE: Setting taker and sender address to `address(0)` means that the - // order can be executed (sender) against any counterparty (taker). For the - // purposes of GPv2, these need to be either `address(0)` or the settlement - // contract. - takerAddress: ethers.constants.AddressZero, - senderAddress: ethers.constants.AddressZero, - - // NOTE: Include no additional fees. I am not sure how this is used by - // market makers, but in theory this can be used to assign an additional - // fee, on top of the 0x protocol fee, to the GPv2 settlement contract. - feeRecipientAddress: ethers.constants.AddressZero, - makerFee: 0, - takerFee: 0, - }; - - const hash = ethers.utils._TypedDataEncoder.hash( - domain, - ORDER_TYPE_DESCRIPTOR, - order, - ); - - // NOTE: Use EIP-712 signing scheme for the order. The signature is just the - // ECDSA signature post-fixed with the signature scheme ID (0x02): - // - - const EIP712_SIGNATURE_ID = 0x02; - const { v, r, s } = ethers.utils.splitSignature( - await maker._signTypedData(domain, ORDER_TYPE_DESCRIPTOR, order), - ); - const signature = ethers.utils.solidityPack( - ["uint8", "bytes32", "bytes32", "uint8"], - [v, r, s, EIP712_SIGNATURE_ID], - ); - - return { order, hash, signature }; -} - -export interface Deployment { - zrxToken: Contract; - exchange: Contract; - erc20Proxy: Contract; - domainSeparator: TypedDataDomain; -} - -export async function deployExchange(deployer: Wallet): Promise { - const zrxToken = await waffle.deployContract( - deployer, - ZRXToken.compilerOutput, - ); - - const zrxAssetData = encodeErc20AssetData(zrxToken.address); - const exchange = await waffle.deployContract( - deployer, - Exchange.compilerOutput, - [zrxAssetData], - ); - - const erc20Proxy = await waffle.deployContract( - deployer, - ERC20Proxy.compilerOutput, - ); - - await erc20Proxy.addAuthorizedAddress(exchange.address); - await exchange.registerAssetProxy(erc20Proxy.address); - - return { - zrxToken, - exchange, - erc20Proxy, - // NOTE: Domain separator parameters taken from: - // - domainSeparator: { - name: "0x Protocol", - version: "2", - verifyingContract: exchange.address, - }, - }; -} diff --git a/yarn.lock b/yarn.lock index 43e1761b..affcaeda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@0x/contract-artifacts-v2@npm:@0x/contract-artifacts@^2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@0x/contract-artifacts/-/contract-artifacts-2.2.2.tgz#e6d771afb58d0b59c19c5364af5a42a3dfd17219" - integrity sha512-sbFnSXE6PlmYsbPXpKtEOR3YdVlSn63HhbPgQB3J5jm27wwQtnZ2Lf21I7BdiRBsHwwbf75C/s2pjNqafaRrgQ== - "@babel/code-frame@^7.0.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" From 8d8f77dc9c7de9f956dc946c310015665395e21a Mon Sep 17 00:00:00 2001 From: mfw78 <53399572+mfw78@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:44:28 +0000 Subject: [PATCH 05/23] chore: merkle.io for ETH RPC (#227) ## Description This PR replaces llamarpc with merkle.io for the RPC as there are higher RPS. ## Test Plan 1. Ensure CI/CD remains green for fork tests. ## Related Issues N/A --- test/e2e/Helper.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/Helper.sol b/test/e2e/Helper.sol index a0f7521d..9543cc7f 100644 --- a/test/e2e/Helper.sol +++ b/test/e2e/Helper.sol @@ -77,7 +77,7 @@ abstract contract Helper is Test { try vm.envString("FORK_URL") returns (string memory url) { forkUrl = url; } catch { - forkUrl = "https://eth.llamarpc.com"; + forkUrl = "https://eth.merkle.io"; } forkId = vm.createSelectFork(forkUrl, blockNumber); From 9225dffc636faeb95aad13ae63b9d3c6cc981025 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 7 Oct 2024 05:46:11 -0700 Subject: [PATCH 06/23] chore: migrate E2E wineOilMarket tests to Foundry (#214) ## Description Migrate wineOilMarket e2e test to foundry. Depends on #215 for the `ForkedTest.t.sol`. ## Test Plan CI ## Related Issues Closes #143 --- test/e2e/WineOilMarket.t.sol | 200 +++++++++++++++++++++++++++++++++ test/e2e/wineOilMarket.test.ts | 198 -------------------------------- 2 files changed, 200 insertions(+), 198 deletions(-) create mode 100644 test/e2e/WineOilMarket.t.sol delete mode 100644 test/e2e/wineOilMarket.test.ts diff --git a/test/e2e/WineOilMarket.t.sol b/test/e2e/WineOilMarket.t.sol new file mode 100644 index 00000000..7a73423f --- /dev/null +++ b/test/e2e/WineOilMarket.t.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; + +import {GPv2Order, GPv2Signing, SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +contract WineOilTest is Helper(false) { + IERC20Mintable eur; + IERC20Mintable oil; + IERC20Mintable wine; + + uint256 constant STARTING_BALANCE = 1000 ether; + + function setUp() public override { + super.setUp(); + + eur = deployMintableErc20("eur", "eur"); + oil = deployMintableErc20("oil", "oil"); + wine = deployMintableErc20("wine", "wine"); + } + + // Settlement for the RetrETH wine and olive oil market: + // + // /---(6. BUY 10 🍷 with 💶 if p(🍷) <= 13)--> [🍷] + // | | + // | | + // [💶] (1. SELL 12 🍷 for 🫒 if p(🍷) >= p(🫒)) + // |^ | + // || | + // |\--(4. SELL 15 🫒 for 💶 if p(🫒) >= 12)--\ v + // \---(5. BUY 4 🫒 with 💶 if p(🫒) <= 13)---> [🫒] + function test_should_settle_red_wine_and_olive_oil_market() external { + Vm.Wallet memory trader1 = vm.createWallet("trader1"); + Vm.Wallet memory trader2 = vm.createWallet("trader2"); + Vm.Wallet memory trader3 = vm.createWallet("trader3"); + Vm.Wallet memory trader4 = vm.createWallet("trader4"); + uint256 feeAmount = 1 ether; + + // sell 12 wine for min 12 oil + _createOrder( + trader1, + _orderData({ + sellToken: wine, + buyToken: oil, + sellAmount: 12 ether, + buyAmount: 12 ether, + feeAmount: feeAmount, + orderKind: GPv2Order.KIND_SELL, + partiallyFillable: false + }), + 0 + ); + // sell 15 oil for min 180 eur + _createOrder( + trader2, + _orderData({ + sellToken: oil, + buyToken: eur, + sellAmount: 15 ether, + buyAmount: 180 ether, + feeAmount: feeAmount, + orderKind: GPv2Order.KIND_SELL, + partiallyFillable: false + }), + 0 + ); + // buy 4 oil with max 52 eur + uint256 order3ExecutedAmount = uint256(27 ether) / 13; + _createOrder( + trader3, + _orderData({ + sellToken: eur, + buyToken: oil, + sellAmount: 52 ether, + buyAmount: 4 ether, + feeAmount: feeAmount, + orderKind: GPv2Order.KIND_BUY, + partiallyFillable: true + }), + order3ExecutedAmount + ); + // buy 20 wine with max 280 eur + uint256 order4ExecutedAmount = 12 ether; + _createOrder( + trader4, + _orderData({ + sellToken: eur, + buyToken: wine, + sellAmount: 280 ether, + buyAmount: 20 ether, + feeAmount: feeAmount, + orderKind: GPv2Order.KIND_BUY, + partiallyFillable: true + }), + order4ExecutedAmount + ); + + uint256 oilPrice = 13 ether; + uint256 winePrice = 14 ether; + { + // set token prices + IERC20[] memory tokens = new IERC20[](3); + tokens[0] = eur; + tokens[1] = oil; + tokens[2] = wine; + uint256[] memory prices = new uint256[](3); + prices[0] = 1 ether; + prices[1] = oilPrice; + prices[2] = winePrice; + + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + } + + // settle the orders + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + vm.prank(solver); + settle(encodedSettlement); + + assertEq( + wine.balanceOf(trader1.addr), + STARTING_BALANCE - 12 ether - feeAmount, + "trader1 sold token amounts not as expected" + ); + uint256 trader1AmountOut = ceilDiv(uint256(12 ether * 14 ether), 13 ether); + assertEq(oil.balanceOf(trader1.addr), trader1AmountOut, "trader1 amountOut not as expected"); + + assertEq( + oil.balanceOf(trader2.addr), + STARTING_BALANCE - 15 ether - feeAmount, + "trader2 sold token amounts not as expected" + ); + assertEq(eur.balanceOf(trader2.addr), 15 ether * 13, "trader2 amountOut not as expected"); + + // order: buy 4 oil with max 52 eur, partial execution + uint256 order3SellAmount = order3ExecutedAmount * oilPrice / 1 ether; + uint256 order3FeeAmount = feeAmount * order3ExecutedAmount / 4 ether; + assertEq( + eur.balanceOf(trader3.addr), + STARTING_BALANCE - order3SellAmount - order3FeeAmount, + "trader3 sold token amount not as expected" + ); + assertEq(oil.balanceOf(trader3.addr), order3ExecutedAmount, "trader3 amountOut not as expected"); + + // order: buy 20 wine with max 280 eur, partial execution + uint256 order4SellAmount = order4ExecutedAmount * winePrice / 1 ether; + uint256 order4FeeAmount = feeAmount * order4ExecutedAmount / 20 ether; + assertEq( + eur.balanceOf(trader4.addr), + STARTING_BALANCE - order4SellAmount - order4FeeAmount, + "trader4 sold token amount not as expected" + ); + assertEq(wine.balanceOf(trader4.addr), order4ExecutedAmount, "trader4 amountOut not as expected"); + } + + function _createOrder(Vm.Wallet memory wallet, GPv2Order.Data memory order, uint256 executedAmount) internal { + IERC20Mintable(address(order.sellToken)).mint(wallet.addr, STARTING_BALANCE); + vm.prank(wallet.addr); + order.sellToken.approve(vaultRelayer, type(uint256).max); + + encoder.signEncodeTrade(vm, wallet, order, domainSeparator, GPv2Signing.Scheme.Eip712, executedAmount); + } + + function _orderData( + IERC20 sellToken, + IERC20 buyToken, + uint256 sellAmount, + uint256 buyAmount, + uint256 feeAmount, + bytes32 orderKind, + bool partiallyFillable + ) internal pure returns (GPv2Order.Data memory order) { + order = GPv2Order.Data({ + sellToken: sellToken, + buyToken: buyToken, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER, + sellAmount: sellAmount, + buyAmount: buyAmount, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: feeAmount, + kind: orderKind, + partiallyFillable: partiallyFillable, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }); + } + + function ceilDiv(uint256 num, uint256 den) internal pure returns (uint256) { + return num % den == 0 ? num / den : (num / den) + 1; + } +} diff --git a/test/e2e/wineOilMarket.test.ts b/test/e2e/wineOilMarket.test.ts deleted file mode 100644 index 3531903a..00000000 --- a/test/e2e/wineOilMarket.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import { expect } from "chai"; -import { BigNumber, BigNumberish, Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { - Order, - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -function ceilDiv(p: BigNumberish, q: BigNumberish): BigNumber { - return BigNumber.from(p).add(q).sub(1).div(q); -} - -describe("E2E: RetrETH Red Wine and Olive Oil Market", () => { - let deployer: Wallet; - let solver: Wallet; - let traders: Wallet[]; - - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - settlement, - vaultRelayer, - wallets: [solver, ...traders], - } = deployment); - - const { authenticator, manager } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - }); - - it("should settle red wine and olive oil market", async () => { - // Settlement for the RetrETH wine and olive oil market: - // - // /---(6. BUY 10 🍷 with 💶 if p(🍷) <= 13)--> [🍷] - // | | - // | | - // [💶] (1. SELL 12 🍷 for 🫒 if p(🍷) >= p(🫒)) - // |^ | - // || | - // |\--(4. SELL 15 🫒 for 💶 if p(🫒) >= 12)--\ v - // \---(5. BUY 4 🫒 with 💶 if p(🫒) <= 13)---> [🫒] - - const STARTING_BALANCE = ethers.utils.parseEther("1000.0"); - const erc20 = (symbol: string) => - waffle.deployContract(deployer, ERC20, [symbol, 18]); - - const eur = await erc20("💶"); - const oil = await erc20("🫒"); - const wine = await erc20("🍷"); - - const orderDefaults = { - validTo: 0xffffffff, - feeAmount: ethers.utils.parseEther("1.0"), - }; - const encoder = new SettlementEncoder(domainSeparator); - - const addOrder = async ( - trader: Wallet, - order: Order, - executedAmount?: BigNumber, - ) => { - const sellToken = await ethers.getContractAt( - ERC20.abi, - order.sellToken, - deployer, - ); - await sellToken.mint(trader.address, STARTING_BALANCE); - await sellToken - .connect(trader) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - - await encoder.signEncodeTrade(order, trader, SigningScheme.EIP712, { - executedAmount, - }); - }; - - await addOrder(traders[0], { - ...orderDefaults, - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: wine.address, - buyToken: oil.address, - sellAmount: ethers.utils.parseEther("12.0"), - buyAmount: ethers.utils.parseEther("12.0"), - appData: 1, - }); - - await addOrder(traders[1], { - ...orderDefaults, - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: oil.address, - buyToken: eur.address, - sellAmount: ethers.utils.parseEther("15.0"), - buyAmount: ethers.utils.parseEther("180.0"), - appData: 4, - }); - - await addOrder( - traders[2], - { - ...orderDefaults, - kind: OrderKind.BUY, - partiallyFillable: true, - buyToken: oil.address, - sellToken: eur.address, - buyAmount: ethers.utils.parseEther("4.0"), - sellAmount: ethers.utils.parseEther("52.0"), - appData: 5, - }, - ethers.utils.parseEther("27.0").div(13), - ); - - await addOrder( - traders[3], - { - ...orderDefaults, - kind: OrderKind.BUY, - partiallyFillable: true, - buyToken: wine.address, - sellToken: eur.address, - buyAmount: ethers.utils.parseEther("20.0"), - sellAmount: ethers.utils.parseEther("280.0"), - appData: 6, - }, - ethers.utils.parseEther("12.0"), - ); - - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [eur.address]: ethers.utils.parseEther("1.0"), - [oil.address]: ethers.utils.parseEther("13.0"), - [wine.address]: ethers.utils.parseEther("14.0"), - }), - ); - - expect(await wine.balanceOf(traders[0].address)).to.deep.equal( - STARTING_BALANCE.sub(ethers.utils.parseEther("12.0")).sub( - orderDefaults.feeAmount, - ), - ); - expect(await oil.balanceOf(traders[0].address)).to.deep.equal( - ceilDiv(ethers.utils.parseEther("12.0").mul(14), 13), - ); - - expect(await oil.balanceOf(traders[1].address)).to.deep.equal( - STARTING_BALANCE.sub(ethers.utils.parseEther("15.0")).sub( - orderDefaults.feeAmount, - ), - ); - expect(await eur.balanceOf(traders[1].address)).to.deep.equal( - ethers.utils.parseEther("15.0").mul(13), - ); - - expect(await eur.balanceOf(traders[2].address)).to.deep.equal( - STARTING_BALANCE.sub(ethers.utils.parseEther("27.0")) - .sub( - orderDefaults.feeAmount - .mul(ethers.utils.parseEther("27.0").div(13)) - .div(ethers.utils.parseEther("4.0")), - ) - // NOTE: Account for rounding error from computing sell amount that is - // an order of magnitude larger than executed buy amount from the - // settlement. - .add(1), - ); - expect(await oil.balanceOf(traders[2].address)).to.deep.equal( - ethers.utils.parseEther("27.0").div(13), - ); - - expect(await eur.balanceOf(traders[3].address)).to.deep.equal( - STARTING_BALANCE.sub(ethers.utils.parseEther("12.0").mul(14)).sub( - orderDefaults.feeAmount - .mul(ethers.utils.parseEther("12.0")) - .div(ethers.utils.parseEther("20.0")), - ), - ); - expect(await wine.balanceOf(traders[3].address)).to.deep.equal( - ethers.utils.parseEther("12.0"), - ); - }); -}); From 75bf3325f1efdf29c1ce35d05766313ae9642f2f Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 7 Oct 2024 05:47:57 -0700 Subject: [PATCH 07/23] chore: migrate E2E upgradeAuthenticator test to Foundry (#217) ## Description Migrate the upgradeAuthenticator e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #142 --- test/e2e/UpgradeAuthenticator.t.sol | 101 +++++++++++++++ test/e2e/upgradeAuthenticator.test.ts | 144 --------------------- test/src/GPv2AllowListAuthenticationV2.sol | 11 -- 3 files changed, 101 insertions(+), 155 deletions(-) create mode 100644 test/e2e/UpgradeAuthenticator.t.sol delete mode 100644 test/e2e/upgradeAuthenticator.test.ts delete mode 100644 test/src/GPv2AllowListAuthenticationV2.sol diff --git a/test/e2e/UpgradeAuthenticator.t.sol b/test/e2e/UpgradeAuthenticator.t.sol new file mode 100644 index 00000000..b1319696 --- /dev/null +++ b/test/e2e/UpgradeAuthenticator.t.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {GPv2AllowListAuthentication} from "src/contracts/GPv2AllowListAuthentication.sol"; + +import {Helper} from "./Helper.sol"; + +interface IEIP173Proxy { + function upgradeTo(address) external; + function transferOwnership(address) external; + function owner() external view returns (address); +} + +contract GPv2AllowListAuthenticationV2 is GPv2AllowListAuthentication { + function newMethod() external pure returns (uint256) { + return 1337; + } +} + +contract UpgradeAuthenticatorTest is Helper(false) { + GPv2AllowListAuthenticationV2 v2Impl; + + function setUp() public override { + super.setUp(); + v2Impl = new GPv2AllowListAuthenticationV2(); + } + + function test_should_upgrade_authenticator() external { + vm.expectRevert(); + GPv2AllowListAuthenticationV2(address(authenticator)).newMethod(); + + vm.prank(owner); + IEIP173Proxy(address(authenticator)).upgradeTo(address(v2Impl)); + + assertEq( + GPv2AllowListAuthenticationV2(address(authenticator)).newMethod(), 1337, "proxy didnt update as expected" + ); + } + + function test_should_preserve_storage() external { + address newSolver = makeAddr("newSolver"); + address newManager = makeAddr("newManager"); + + vm.startPrank(owner); + GPv2AllowListAuthentication(address(authenticator)).addSolver(newSolver); + GPv2AllowListAuthentication(address(authenticator)).setManager(newManager); + + IEIP173Proxy(address(authenticator)).upgradeTo(address(v2Impl)); + vm.stopPrank(); + + assertEq(authenticator.isSolver(newSolver), true, "solver not retained in storage after proxy upgrade"); + assertEq( + GPv2AllowListAuthentication(address(authenticator)).manager(), + newManager, + "manager not retained in storage after proxy upgrade" + ); + } + + function test_should_allow_proxy_owner_to_change_manager() external { + // transfer ownership to a new address and then assert the behavior + // to have a proxy owner that is different address than manager + address newOwner = makeAddr("newOwner"); + vm.prank(owner); + IEIP173Proxy(address(authenticator)).transferOwnership(newOwner); + + address newManager = makeAddr("newManager"); + vm.prank(newOwner); + GPv2AllowListAuthentication(address(authenticator)).setManager(newManager); + + assertEq( + GPv2AllowListAuthentication(address(authenticator)).manager(), + newManager, + "proxy owner couldnt update manager" + ); + } + + function test_should_be_able_to_transfer_proxy_ownership() external { + address newOwner = makeAddr("newOwner"); + vm.prank(owner); + IEIP173Proxy(address(authenticator)).transferOwnership(newOwner); + + assertEq(IEIP173Proxy(address(authenticator)).owner(), newOwner, "ownership didnt transfer as expected"); + } + + function test_should_revert_when_upgrading_with_the_authentication_manager() external { + address newManager = makeAddr("newManager"); + vm.prank(owner); + GPv2AllowListAuthentication(address(authenticator)).setManager(newManager); + + vm.prank(newManager); + vm.expectRevert("NOT_AUTHORIZED"); + IEIP173Proxy(address(authenticator)).upgradeTo(address(v2Impl)); + } + + function test_should_revert_when_not_upgrading_with_the_proxy_owner() external { + address nobody = makeAddr("nobody"); + vm.prank(nobody); + vm.expectRevert("NOT_AUTHORIZED"); + IEIP173Proxy(address(authenticator)).upgradeTo(address(v2Impl)); + } +} diff --git a/test/e2e/upgradeAuthenticator.test.ts b/test/e2e/upgradeAuthenticator.test.ts deleted file mode 100644 index abfa0bd2..00000000 --- a/test/e2e/upgradeAuthenticator.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { deployments, ethers } from "hardhat"; - -import { proxyInterface } from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -async function rejectError( - promise: Promise, -): Promise { - try { - await promise; - return undefined; - } catch (err) { - if (err instanceof Error) { - return err; - } else { - throw new Error("Invalid rejection output"); - } - } -} - -async function upgrade( - proxyOwner: Wallet, - contractName: string, - newContractName: string, -) { - // Note that deterministic deployment and gasLimit are not needed/used here as deployment args. - await deployments.deploy(contractName, { - contract: newContractName, - // From differs from initial deployment here since the proxy owner is the Authenticator manager. - from: proxyOwner.address, - proxy: true, - }); -} - -describe("E2E: Upgrade Authenticator", () => { - let authenticator: Contract; - let deployer: Wallet; - let owner: Wallet; - let manager: Wallet; - let nobody: Wallet; - let newOwner: Wallet; - let newManager: Wallet; - let solver: Wallet; - - beforeEach(async () => { - ({ - authenticator, - deployer, - owner, - manager, - wallets: [nobody, newOwner, newManager, solver], - } = await deployTestContracts()); - }); - - it("should upgrade authenticator", async () => { - const GPv2AllowListAuthenticationV2 = await ethers.getContractFactory( - "GPv2AllowListAuthenticationV2", - deployer, - ); - // Note that, before the upgrade this is actually the old instance - const authenticatorV2 = GPv2AllowListAuthenticationV2.attach( - authenticator.address, - ); - // This method doesn't exist before upgrade - await expect(authenticatorV2.newMethod()).to.be.reverted; - - await upgrade( - owner, - "GPv2AllowListAuthentication", - "GPv2AllowListAuthenticationV2", - ); - // This method should exist on after upgrade - expect(await authenticatorV2.newMethod()).to.equal(1337); - }); - - it("should preserve storage", async () => { - await authenticator.connect(manager).addSolver(solver.address); - await authenticator.connect(manager).setManager(newManager.address); - - // Upgrade after storage is set with **proxy owner**; - await upgrade( - owner, - "GPv2AllowListAuthentication", - "GPv2AllowListAuthenticationV2", - ); - - const GPv2AllowListAuthenticationV2 = await ethers.getContractFactory( - "GPv2AllowListAuthenticationV2", - deployer, - ); - const authenticatorV2 = GPv2AllowListAuthenticationV2.attach( - authenticator.address, - ); - - // Both, the listed solvers and updated manager are still set - expect(await authenticatorV2.isSolver(solver.address)).to.equal(true); - expect(await authenticatorV2.manager()).to.equal(newManager.address); - }); - - it("should allow the proxy owner to change the manager", async () => { - await authenticator.connect(owner).setManager(newManager.address); - expect(await authenticator.manager()).to.equal(newManager.address); - }); - - it("should be able to transfer proxy ownership", async () => { - const proxy = proxyInterface(authenticator); - await proxy.connect(owner).transferOwnership(newOwner.address); - expect(await proxy.owner()).to.equal(newOwner.address); - - await upgrade( - newOwner, - "GPv2AllowListAuthentication", - "GPv2AllowListAuthenticationV2", - ); - }); - - it("should revert when not upgrading with the authentication manager", async () => { - await authenticator.connect(owner).setManager(newManager.address); - expect( - await rejectError( - upgrade( - newManager, - "GPv2AllowListAuthentication", - "GPv2AllowListAuthenticationV2", - ), - ), - ).to.not.be.undefined; - }); - - it("should revert when not upgrading with the proxy owner", async () => { - expect( - await rejectError( - upgrade( - nobody, - "GPv2AllowListAuthentication", - "GPv2AllowListAuthenticationV2", - ), - ), - ).to.not.be.undefined; - }); -}); diff --git a/test/src/GPv2AllowListAuthenticationV2.sol b/test/src/GPv2AllowListAuthenticationV2.sol deleted file mode 100644 index 40dc6ca2..00000000 --- a/test/src/GPv2AllowListAuthenticationV2.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// solhint-disable-next-line compiler-version -pragma solidity >=0.7.6 <0.9.0; - -import "src/contracts/GPv2AllowListAuthentication.sol"; - -contract GPv2AllowListAuthenticationV2 is GPv2AllowListAuthentication { - function newMethod() external pure returns (uint256) { - return 1337; - } -} From 89f9189194a9d61d030c43d771f00b9293fcd646 Mon Sep 17 00:00:00 2001 From: mfw78 <53399572+mfw78@users.noreply.github.com> Date: Mon, 7 Oct 2024 12:49:53 +0000 Subject: [PATCH 08/23] chore: set fork block number (#228) ## Description In order to make use of cache, and to convey to testers / developers what the assumptions of fork tests are, the fork block number is established as a constant within the `Helper.sol`. ## Test Plan 1. Verify that CI/CD passes. ## Related Issues N/A --- test/e2e/Helper.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/e2e/Helper.sol b/test/e2e/Helper.sol index 9543cc7f..c56e20cb 100644 --- a/test/e2e/Helper.sol +++ b/test/e2e/Helper.sol @@ -21,6 +21,8 @@ import {SwapEncoder} from "test/libraries/encoders/SwapEncoder.sol"; address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; +uint256 constant FORK_BLOCK_NUMBER = 20913563; + interface IAuthorizer { function grantRole(bytes32, address) external; function canPerform(bytes32 actionId, address account, address where) external view returns (bool); @@ -71,7 +73,6 @@ abstract contract Helper is Test { function setUp() public virtual { if (isForked) { - uint256 blockNumber = vm.envUint("FORK_BLOCK_NUMBER"); string memory forkUrl; try vm.envString("FORK_URL") returns (string memory url) { @@ -80,7 +81,7 @@ abstract contract Helper is Test { forkUrl = "https://eth.merkle.io"; } - forkId = vm.createSelectFork(forkUrl, blockNumber); + forkId = vm.createSelectFork(forkUrl, FORK_BLOCK_NUMBER); weth = WETH9(payable(WETH)); } else { weth = new WETH9(); From 3d8a3505be09c25d407449aca0a5a300efeeef5b Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 7 Oct 2024 22:20:40 -0700 Subject: [PATCH 09/23] chore: migrate E2E burnFees to Foundry (#221) ## Description Migrate the burnFees e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #134 --- test/e2e/BurnFees.t.sol | 114 ++++++++++++++++++++++++++++++++++++ test/e2e/burnFees.test.ts | 119 -------------------------------------- 2 files changed, 114 insertions(+), 119 deletions(-) create mode 100644 test/e2e/BurnFees.t.sol delete mode 100644 test/e2e/burnFees.test.ts diff --git a/test/e2e/BurnFees.t.sol b/test/e2e/BurnFees.t.sol new file mode 100644 index 00000000..e3ac5c1a --- /dev/null +++ b/test/e2e/BurnFees.t.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; + +import {GPv2Interaction} from "src/contracts/libraries/GPv2Interaction.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; + +import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +contract BurnFeesTest is Helper(false) { + IERC20Mintable owl; + IERC20Mintable dai; + + function setUp() public override { + super.setUp(); + + owl = deployMintableErc20("owl", "owl"); + dai = deployMintableErc20("dai", "dai"); + } + + function test_uses_post_interaction_to_burn_settlement_fees() external { + Vm.Wallet memory trader1 = vm.createWallet("trader1"); + Vm.Wallet memory trader2 = vm.createWallet("trader2"); + + // mint some owl to trader1 + owl.mint(trader1.addr, 1001 ether); + vm.prank(trader1.addr); + // approve owl for trading on settlement contract + owl.approve(vaultRelayer, type(uint256).max); + // place order to sell 1000 owl for min 1000 dai + encoder.signEncodeTrade( + vm, + trader1, + GPv2Order.Data({ + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellToken: owl, + buyToken: dai, + sellAmount: 1000 ether, + buyAmount: 1000 ether, + feeAmount: 1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some dai to trader2 + dai.mint(trader2.addr, 1000 ether); + vm.prank(trader2.addr); + // approve dai for trading on settlement contract + dai.approve(vaultRelayer, type(uint256).max); + // place order to BUY 1000 owl with max 1000 dai + encoder.signEncodeTrade( + vm, + trader2, + GPv2Order.Data({ + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellToken: dai, + buyToken: owl, + sellAmount: 1000 ether, + buyAmount: 1000 ether, + feeAmount: 0, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // add post interaction to burn owl fees + encoder.addInteraction( + GPv2Interaction.Data({target: address(owl), value: 0, callData: abi.encodeCall(owl.burn, (1 ether))}), + SettlementEncoder.InteractionStage.POST + ); + + // set the token prices + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = owl; + tokens[1] = dai; + uint256[] memory prices = new uint256[](2); + prices[0] = 1; + prices[1] = 1; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + vm.prank(solver); + vm.expectEmit(); + emit IERC20.Transfer(address(settlement), address(0), 1 ether); + settle(encodedSettlement); + + assertEq(dai.balanceOf(address(settlement)), 0, "dai balance of settlement contract not 0"); + } +} diff --git a/test/e2e/burnFees.test.ts b/test/e2e/burnFees.test.ts deleted file mode 100644 index af76b2d0..00000000 --- a/test/e2e/burnFees.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { - InteractionStage, - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -describe("E2E: Burn fees", () => { - let deployer: Wallet; - let solver: Wallet; - let traders: Wallet[]; - - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let owl: Contract; - let dai: Contract; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - settlement, - vaultRelayer, - wallets: [solver, ...traders], - } = deployment); - - const { authenticator, manager } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - owl = await waffle.deployContract(deployer, ERC20, ["OWL", "Owl token"]); - dai = await waffle.deployContract(deployer, ERC20, ["DAI", "Dai token"]); - }); - - it("uses post-interation to burn settlement fees", async () => { - // Settle a trivial 1:1 trade between DAI and OWL. - - const ONE_USD = ethers.utils.parseEther("1.0"); - - const encoder = new SettlementEncoder(domainSeparator); - - await owl.mint(traders[0].address, ONE_USD.mul(1001)); - await owl - .connect(traders[0]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: owl.address, - buyToken: dai.address, - sellAmount: ONE_USD.mul(1000), - buyAmount: ONE_USD.mul(1000), - feeAmount: ONE_USD, - validTo: 0xffffffff, - appData: 1, - }, - traders[0], - SigningScheme.EIP712, - ); - - await dai.mint(traders[1].address, ONE_USD.mul(1000)); - await dai - .connect(traders[1]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - buyToken: owl.address, - sellToken: dai.address, - buyAmount: ONE_USD.mul(1000), - sellAmount: ONE_USD.mul(1000), - feeAmount: ethers.constants.Zero, - validTo: 0xffffffff, - appData: 2, - }, - traders[1], - SigningScheme.EIP712, - ); - - encoder.encodeInteraction( - { - target: owl.address, - callData: owl.interface.encodeFunctionData("burn", [ONE_USD]), - }, - InteractionStage.POST, - ); - - const tx = settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [owl.address]: 1, - [dai.address]: 1, - }), - ); - - await expect(tx) - .to.emit(owl, "Transfer") - .withArgs(settlement.address, ethers.constants.AddressZero, ONE_USD); - expect(await dai.balanceOf(settlement.address)).to.deep.equal( - ethers.constants.Zero, - ); - }); -}); From 51f4a5b967319ffe57968fccc4acaf1c98ef7c9d Mon Sep 17 00:00:00 2001 From: mfw78 <53399572+mfw78@users.noreply.github.com> Date: Tue, 8 Oct 2024 06:47:45 +0000 Subject: [PATCH 10/23] chore: update test CI/CD for FORK_URL env (#230) ## Description This PR sets the `FORK_URL` environment variable to a mainnet archive node with sufficient RPS / capacity to facilitate the fork tests. ## Test Plan 1. Verify CI/CD green. ## Related Issues N/A --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb71e41b..7fcf67db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,5 +70,9 @@ jobs: - name: Run Forge tests if: matrix.profile != 'solc-0.7.6' run: | + if [ "${{ matrix.profile }}" == "default" ]; then + echo "Setting FORK_URL for default profile" + export FORK_URL="${{ secrets.MAINNET_ARCHIVE_RPC }}" + fi FOUNDRY_PROFILE=ci forge test -vvv id: test From 078c5a6f8ce849a5ec1ce1cb255bd66e0920e88c Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Mon, 7 Oct 2024 23:55:50 -0700 Subject: [PATCH 11/23] chore: migrate E2E buyEth tests to Foundry (#229) ## Description Migrate buyEth e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215 ## Test Plan CI ## Related Issues Closes https://github.com/cowprotocol/contracts/issues/135 --------- Co-authored-by: mfw78 <53399572+mfw78@users.noreply.github.com> --- test/e2e/BuyEth.t.sol | 143 ++++++++++++++++++++++++++++++++++++++++ test/e2e/buyEth.test.ts | 128 ----------------------------------- 2 files changed, 143 insertions(+), 128 deletions(-) create mode 100644 test/e2e/BuyEth.t.sol delete mode 100644 test/e2e/buyEth.test.ts diff --git a/test/e2e/BuyEth.t.sol b/test/e2e/BuyEth.t.sol new file mode 100644 index 00000000..44f5b541 --- /dev/null +++ b/test/e2e/BuyEth.t.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {GPv2Transfer} from "src/contracts/libraries/GPv2Transfer.sol"; + +import { + GPv2Interaction, GPv2Order, GPv2Signing, SettlementEncoder +} from "test/libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "test/libraries/encoders/TokenRegistry.sol"; + +import {Helper} from "./Helper.sol"; + +interface IUSDT { + function getOwner() external view returns (address); + function issue(uint256) external; + // approve and transfer doesn't return the bool for USDT + function approve(address, uint256) external; + function transfer(address, uint256) external; +} + +IUSDT constant USDT = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7); +IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +contract BuyEthTest is Helper(true) { + // Settle a trivial batch between two overlapping trades: + // + // /----(1. SELL 1 WETH for USDT if p(WETH) >= 1100)----\ + // | v + // [USDT] [(W)ETH] + // ^ | + // \-----(2. BUY 1 ETH for USDT if p(WETH) <= 1200)-----/ + function test_should_unwrap_weth_for_eth_buy_orders() external { + Vm.Wallet memory trader1 = vm.createWallet("trader1"); + Vm.Wallet memory trader2 = vm.createWallet("trader2"); + + // give some weth to trader1 + deal(address(WETH), trader1.addr, 1.001 ether); + // approve weth for trading on the vault + vm.prank(trader1.addr); + WETH.approve(vaultRelayer, type(uint256).max); + // place the weth to usdt swap order + encoder.signEncodeTrade( + vm, + trader1, + GPv2Order.Data({ + sellToken: WETH, + buyToken: IERC20(address(USDT)), + receiver: trader1.addr, + sellAmount: 1 ether, + buyAmount: 1100e6, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 0.001 ether, + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // give some usdt to trader2 + _mintUsdt(trader2.addr, 1201.2e6); + // approve usdt for trading on the vault + vm.prank(trader2.addr); + USDT.approve(vaultRelayer, type(uint256).max); + // place the usdt to eth swap order + encoder.signEncodeTrade( + vm, + trader2, + GPv2Order.Data({ + sellToken: IERC20(address(USDT)), + buyToken: IERC20(GPv2Transfer.BUY_ETH_ADDRESS), + receiver: trader2.addr, + sellAmount: 1200e6, + buyAmount: 1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 1.2e6, + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // encode the weth withdraw interaction + encoder.addInteraction( + GPv2Interaction.Data({ + target: address(WETH), + value: 0, + callData: abi.encodeWithSignature("withdraw(uint256)", 1 ether) + }), + SettlementEncoder.InteractionStage.INTRA + ); + + // set the token prices + IERC20[] memory tokens = new IERC20[](3); + tokens[0] = WETH; + tokens[1] = IERC20(GPv2Transfer.BUY_ETH_ADDRESS); + tokens[2] = IERC20(address(USDT)); + uint256[] memory prices = new uint256[](3); + prices[0] = 1150e6; + prices[1] = 1150e6; + prices[2] = 1 ether; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + + uint256 trader2InitialBalance = trader2.addr.balance; + vm.prank(solver); + settle(encodedSettlement); + assertEq( + WETH.balanceOf(address(settlement)), + 0.001 ether, + "settlement contract's weth balance from trade fee not as expected" + ); // the fee + assertEq(WETH.balanceOf(trader1.addr), 0, "trader1 weth balance is not 0"); + assertEq( + trader2.addr.balance, trader2InitialBalance + 1 ether, "trader2 eth balance did not increase as expected" + ); + } + + function _mintUsdt(address receiver, uint256 amt) internal { + address owner = USDT.getOwner(); + vm.startPrank(owner); + USDT.issue(amt); + USDT.transfer(receiver, amt); + vm.stopPrank(); + } +} diff --git a/test/e2e/buyEth.test.ts b/test/e2e/buyEth.test.ts deleted file mode 100644 index bfcb6eda..00000000 --- a/test/e2e/buyEth.test.ts +++ /dev/null @@ -1,128 +0,0 @@ -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { - BUY_ETH_ADDRESS, - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -describe("E2E: Buy Ether", () => { - let deployer: Wallet; - let solver: Wallet; - let traders: Wallet[]; - - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let weth: Contract; - let usdt: Contract; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - weth, - settlement, - vaultRelayer, - wallets: [solver, ...traders], - } = deployment); - - const { authenticator, manager } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - usdt = await waffle.deployContract(deployer, ERC20, ["USDT", 6]); - }); - - it("should unwrap WETH for orders buying Ether", async () => { - // Settle a trivial batch between two overlapping trades: - // - // /----(1. SELL 1 WETH for USDT if p(WETH) >= 1100)----\ - // | v - // [USDT] [(W)ETH] - // ^ | - // \-----(2. BUY 1 ETH for USDT if p(WETH) <= 1200)-----/ - - const encoder = new SettlementEncoder(domainSeparator); - - await weth - .connect(traders[0]) - .deposit({ value: ethers.utils.parseEther("1.001") }); - await weth - .connect(traders[0]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: weth.address, - buyToken: usdt.address, - sellAmount: ethers.utils.parseEther("1.0"), - buyAmount: ethers.utils.parseUnits("1100.0", 6), - feeAmount: ethers.utils.parseEther("0.001"), - validTo: 0xffffffff, - appData: 1, - }, - traders[0], - SigningScheme.EIP712, - ); - - await usdt.mint(traders[1].address, ethers.utils.parseUnits("1201.2", 6)); - await usdt - .connect(traders[1]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - buyToken: BUY_ETH_ADDRESS, - sellToken: usdt.address, - buyAmount: ethers.utils.parseEther("1.0"), - sellAmount: ethers.utils.parseUnits("1200.0", 6), - feeAmount: ethers.utils.parseUnits("1.2", 6), - validTo: 0xffffffff, - appData: 2, - }, - traders[1], - SigningScheme.EIP712, - ); - - encoder.encodeInteraction({ - target: weth.address, - callData: weth.interface.encodeFunctionData("withdraw", [ - ethers.utils.parseEther("1.0"), - ]), - }); - - const trader1InitialBalance = await traders[1].getBalance(); - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [weth.address]: ethers.utils.parseUnits("1150.0", 6), - [BUY_ETH_ADDRESS]: ethers.utils.parseUnits("1150.0", 6), - [usdt.address]: ethers.utils.parseEther("1.0"), - }), - ); - - expect(await weth.balanceOf(settlement.address)).to.deep.equal( - ethers.utils.parseEther("0.001"), - ); - expect(await weth.balanceOf(traders[0].address)).to.deep.equal( - ethers.constants.Zero, - ); - expect(await traders[1].getBalance()).to.deep.equal( - trader1InitialBalance.add(ethers.utils.parseEther("1.0")), - ); - }); -}); From e974119681a8df12d819f8a4698fcbb27548f213 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 01:21:18 -0700 Subject: [PATCH 12/23] chore: migrate E2E offchainAllowances test to Foundry (#218) ## Description Migrate the offchainAllowances e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #139 --- test/e2e/OffchainAllowances.t.sol | 276 +++++++++++++++++++++++++++ test/e2e/offchainAllowances.test.ts | 285 ---------------------------- 2 files changed, 276 insertions(+), 285 deletions(-) create mode 100644 test/e2e/OffchainAllowances.t.sol delete mode 100644 test/e2e/offchainAllowances.test.ts diff --git a/test/e2e/OffchainAllowances.t.sol b/test/e2e/OffchainAllowances.t.sol new file mode 100644 index 00000000..38d304bf --- /dev/null +++ b/test/e2e/OffchainAllowances.t.sol @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {IVault} from "src/contracts/interfaces/IVault.sol"; +import {GPv2Interaction} from "src/contracts/libraries/GPv2Interaction.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; + +import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +interface IERC2612 { + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + external; + function nonces(address owner) external view returns (uint256); + function DOMAIN_SEPARATOR() external view returns (bytes32); +} + +interface IBalancerVault { + function getDomainSeparator() external view returns (bytes32); + function setRelayerApproval(address, address, bool) external; +} + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +contract OffchainAllowancesTest is Helper(false) { + IERC20Mintable eur1; + IERC20Mintable eur2; + + Vm.Wallet trader1; + Vm.Wallet trader2; + + function setUp() public override { + super.setUp(); + + eur1 = IERC20Mintable(_create(abi.encodePacked(vm.getCode("ERC20PresetPermit"), abi.encode("eur1")), 0)); + eur2 = IERC20Mintable(_create(abi.encodePacked(vm.getCode("ERC20PresetPermit"), abi.encode("eur1")), 0)); + + trader1 = vm.createWallet("trader1"); + trader2 = vm.createWallet("trader2"); + } + + function test_eip_2612_permits_trader_allowance_with_settlement() external { + // mint and approve tokens to and from trader1 + eur1.mint(trader1.addr, 1 ether); + vm.prank(trader1.addr); + eur1.approve(vaultRelayer, type(uint256).max); + + // place order to sell 1 eur1 for min 1 eur2 from trader1 + encoder.signEncodeTrade( + vm, + trader1, + GPv2Order.Data({ + sellToken: eur1, + buyToken: eur2, + receiver: trader1.addr, + sellAmount: 1 ether, + buyAmount: 1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 0, + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some tokens to trader2 + eur2.mint(trader2.addr, 1 ether); + uint256 nonce = IERC2612(address(eur2)).nonces(trader2.addr); + (uint8 v, bytes32 r, bytes32 s) = _permit(eur2, trader2, vaultRelayer, 1 ether, nonce, 0xffffffff); + // interaction for setting the approval with permit + encoder.addInteraction( + GPv2Interaction.Data({ + target: address(eur2), + value: 0, + callData: abi.encodeCall(IERC2612.permit, (trader2.addr, vaultRelayer, 1 ether, 0xffffffff, v, r, s)) + }), + SettlementEncoder.InteractionStage.PRE + ); + + // buy 1 eur1 with max 1 eur2 + encoder.signEncodeTrade( + vm, + trader2, + GPv2Order.Data({ + sellToken: eur2, + buyToken: eur1, + receiver: trader2.addr, + sellAmount: 1 ether, + buyAmount: 1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 0, + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // set prices + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = eur1; + tokens[1] = eur2; + uint256[] memory prices = new uint256[](2); + prices[0] = 1; + prices[1] = 1; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + vm.prank(solver); + settle(encodedSettlement); + + assertEq(eur2.balanceOf(trader2.addr), 0, "permit didnt work"); + } + + function test_allows_setting_vault_relayer_approval_with_interactions() external { + // mint and approve tokens to and from trader1 + eur1.mint(trader1.addr, 1 ether); + vm.prank(trader1.addr); + eur1.approve(vaultRelayer, type(uint256).max); + + // place order to sell 1 eur1 for min 1 eur2 from trader1 + encoder.signEncodeTrade( + vm, + trader1, + GPv2Order.Data({ + sellToken: eur1, + buyToken: eur2, + receiver: trader1.addr, + sellAmount: 1 ether, + buyAmount: 1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 0, + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some tokens to trader2 + eur2.mint(trader2.addr, 1 ether); + // deposit tokens into balancer internal balance + vm.startPrank(trader2.addr); + eur2.approve(address(vault), type(uint256).max); + IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1); + ops[0] = IVault.UserBalanceOp({ + kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL, + asset: eur2, + amount: 1 ether, + sender: trader2.addr, + recipient: payable(trader2.addr) + }); + vault.manageUserBalance(ops); + vm.stopPrank(); + + _grantBalancerActionRole( + balancerVaultAuthorizer, address(vault), address(settlement), "setRelayerApproval(address,address,bool)" + ); + bytes memory approval = abi.encodeCall(IBalancerVault.setRelayerApproval, (trader2.addr, vaultRelayer, true)); + (uint8 v, bytes32 r, bytes32 s) = + _balancerSetRelayerApprovalSignature(trader2, approval, address(settlement), 0, 0xffffffff); + encoder.addInteraction( + GPv2Interaction.Data({ + target: address(vault), + value: 0, + callData: abi.encodePacked(approval, abi.encode(0xffffffff, v, r, s)) + }), + SettlementEncoder.InteractionStage.PRE + ); + + encoder.signEncodeTrade( + vm, + trader2, + GPv2Order.Data({ + sellToken: eur2, + buyToken: eur1, + receiver: trader2.addr, + sellAmount: 1 ether, + buyAmount: 1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + feeAmount: 0, + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellTokenBalance: GPv2Order.BALANCE_INTERNAL, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // set prices + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = eur1; + tokens[1] = eur2; + uint256[] memory prices = new uint256[](2); + prices[0] = 1; + prices[1] = 1; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + + vm.prank(solver); + settle(encodedSettlement); + + assertEq(eur2.balanceOf(trader2.addr), 0, "balancer signed approval didnt work"); + } + + function _permit( + IERC20Mintable token, + Vm.Wallet memory owner, + address spender, + uint256 value, + uint256 nonce, + uint256 deadline + ) internal returns (uint8 v, bytes32 r, bytes32 s) { + bytes32 ds = IERC2612(address(token)).DOMAIN_SEPARATOR(); + bytes32 digest = keccak256( + abi.encodePacked( + hex"1901", + ds, + keccak256( + abi.encode( + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"), + owner.addr, + spender, + value, + nonce, + deadline + ) + ) + ) + ); + (v, r, s) = vm.sign(owner, digest); + } + + function _balancerSetRelayerApprovalSignature( + Vm.Wallet memory owner, + bytes memory cd, + address sender, + uint256 nonce, + uint256 deadline + ) internal returns (uint8 v, bytes32 r, bytes32 s) { + bytes32 ds = IBalancerVault(address(vault)).getDomainSeparator(); + bytes memory ecd = abi.encode( + keccak256("SetRelayerApproval(bytes calldata,address sender,uint256 nonce,uint256 deadline)"), + keccak256(cd), + sender, + nonce, + deadline + ); + bytes32 digest = keccak256(abi.encodePacked(hex"1901", ds, keccak256(ecd))); + (v, r, s) = vm.sign(owner, digest); + } +} diff --git a/test/e2e/offchainAllowances.test.ts b/test/e2e/offchainAllowances.test.ts deleted file mode 100644 index 56629d41..00000000 --- a/test/e2e/offchainAllowances.test.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { ethers } from "hardhat"; - -import { - InteractionStage, - OrderBalance, - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, - grantRequiredRoles, -} from "../../src/ts"; -import { UserBalanceOpKind } from "../balancer"; - -import { deployTestContracts } from "./fixture"; - -describe("E2E: Off-chain Allowances", () => { - let manager: Wallet; - let solver: Wallet; - let traders: Wallet[]; - - let vault: Contract; - let vaultAuthorizer: Contract; - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let eurs: [Contract, Contract]; - const ONE_EUR = ethers.utils.parseEther("1.0"); - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - vault, - vaultAuthorizer, - settlement, - vaultRelayer, - manager, - wallets: [solver, ...traders], - } = deployment); - - const { authenticator } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - const ERC20 = await ethers.getContractFactory("ERC20PresetPermit"); - eurs = [await ERC20.deploy("EUR1"), await ERC20.deploy("EUR2")]; - }); - - describe("EIP-2612 Permit", () => { - it("permits trader allowance with settlement", async () => { - // Settle a trivial trade where all € stable coins trade 1:1. - - const encoder = new SettlementEncoder(domainSeparator); - - await eurs[0].mint(traders[0].address, ONE_EUR); - await eurs[0] - .connect(traders[0]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: eurs[0].address, - buyToken: eurs[1].address, - sellAmount: ONE_EUR, - buyAmount: ONE_EUR, - feeAmount: ethers.constants.Zero, - validTo: 0xffffffff, - appData: 1, - }, - traders[0], - SigningScheme.EIP712, - ); - - await eurs[1].mint(traders[1].address, ONE_EUR); - - const permit = { - owner: traders[1].address, - spender: vaultRelayer.address, - value: ONE_EUR, - nonce: await eurs[1].nonces(traders[1].address), - deadline: 0xffffffff, - }; - const { r, s, v } = ethers.utils.splitSignature( - await traders[1]._signTypedData( - { - name: await eurs[1].name(), - version: "1", - chainId: domainSeparator.chainId, - verifyingContract: eurs[1].address, - }, - { - Permit: [ - { name: "owner", type: "address" }, - { name: "spender", type: "address" }, - { name: "value", type: "uint256" }, - { name: "nonce", type: "uint256" }, - { name: "deadline", type: "uint256" }, - ], - }, - permit, - ), - ); - encoder.encodeInteraction( - { - target: eurs[1].address, - callData: eurs[1].interface.encodeFunctionData("permit", [ - permit.owner, - permit.spender, - permit.value, - permit.deadline, - v, - r, - s, - ]), - }, - InteractionStage.PRE, - ); - - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - buyToken: eurs[0].address, - sellToken: eurs[1].address, - buyAmount: ONE_EUR, - sellAmount: ONE_EUR, - feeAmount: ethers.constants.Zero, - validTo: 0xffffffff, - appData: 2, - }, - traders[1], - SigningScheme.EIP712, - ); - - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [eurs[0].address]: 1, - [eurs[1].address]: 1, - }), - ); - - expect(await eurs[1].balanceOf(traders[1].address)).to.deep.equal( - ethers.constants.Zero, - ); - }); - }); - - describe("Vault Allowance", () => { - it("allows setting Vault relayer approval with interactions", async () => { - // Settle a trivial trade where all € stable coins trade 1:1. - - const encoder = new SettlementEncoder(domainSeparator); - - await eurs[0].mint(traders[0].address, ONE_EUR); - await eurs[0] - .connect(traders[0]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: eurs[0].address, - buyToken: eurs[1].address, - sellAmount: ONE_EUR, - buyAmount: ONE_EUR, - feeAmount: ethers.constants.Zero, - validTo: 0xffffffff, - appData: 1, - }, - traders[0], - SigningScheme.EIP712, - ); - - await eurs[1].mint(traders[1].address, ONE_EUR); - await eurs[1] - .connect(traders[1]) - .approve(vault.address, ethers.constants.MaxUint256); - await vault.connect(traders[1]).manageUserBalance([ - { - kind: UserBalanceOpKind.DEPOSIT_INTERNAL, - asset: eurs[1].address, - amount: ONE_EUR, - sender: traders[1].address, - recipient: traders[1].address, - }, - ]); - - // The settlement contract needs to be authorized as a relayer to change - // relayer allowances for users by signature. - await vaultAuthorizer - .connect(manager) - .grantRole( - ethers.utils.solidityKeccak256( - ["uint256", "bytes4"], - [vault.address, vault.interface.getSighash("setRelayerApproval")], - ), - settlement.address, - ); - await grantRequiredRoles( - vaultAuthorizer.connect(manager), - vault.address, - vaultRelayer.address, - ); - - const deadline = 0xffffffff; - const { chainId } = await ethers.provider.getNetwork(); - const approval = vault.interface.encodeFunctionData( - "setRelayerApproval", - [traders[1].address, vaultRelayer.address, true], - ); - const { v, r, s } = ethers.utils.splitSignature( - await traders[1]._signTypedData( - { - name: "Balancer V2 Vault", - version: "1", - chainId, - verifyingContract: vault.address, - }, - { - SetRelayerApproval: [ - { name: "calldata", type: "bytes" }, - { name: "sender", type: "address" }, - { name: "nonce", type: "uint256" }, - { name: "deadline", type: "uint256" }, - ], - }, - { - calldata: approval, - sender: settlement.address, - nonce: 0, - deadline, - }, - ), - ); - encoder.encodeInteraction( - { - target: vault.address, - callData: ethers.utils.hexConcat([ - approval, - ethers.utils.defaultAbiCoder.encode( - ["uint256", "uint8", "bytes32", "bytes32"], - [deadline, v, r, s], - ), - ]), - }, - InteractionStage.PRE, - ); - - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - buyToken: eurs[0].address, - sellToken: eurs[1].address, - buyAmount: ONE_EUR, - sellAmount: ONE_EUR, - feeAmount: ethers.constants.Zero, - validTo: 0xffffffff, - appData: 2, - sellTokenBalance: OrderBalance.INTERNAL, - }, - traders[1], - SigningScheme.EIP712, - ); - - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [eurs[0].address]: 1, - [eurs[1].address]: 1, - }), - ); - - expect(await eurs[1].balanceOf(traders[1].address)).to.deep.equal( - ethers.constants.Zero, - ); - }); - }); -}); From fbfd19f98f3d2d16de64f5ec6f5e7fbfa9d54020 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 01:29:43 -0700 Subject: [PATCH 13/23] chore: migrate E2E smartOrder test to Foundry (#220) ## Description Migrate the smartOrder e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Issue doesn't exist --- test/e2e/SmartOrder.t.sol | 214 ++++++++++++++++++++++++++++++++++++ test/e2e/smartOrder.test.ts | 133 ---------------------- test/src/SmartSellOrder.sol | 108 ------------------ 3 files changed, 214 insertions(+), 241 deletions(-) create mode 100644 test/e2e/SmartOrder.t.sol delete mode 100644 test/e2e/smartOrder.test.ts delete mode 100644 test/src/SmartSellOrder.sol diff --git a/test/e2e/SmartOrder.t.sol b/test/e2e/SmartOrder.t.sol new file mode 100644 index 00000000..c3dc2dbb --- /dev/null +++ b/test/e2e/SmartOrder.t.sol @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {GPv2Settlement} from "src/contracts/GPv2Settlement.sol"; +import {EIP1271Verifier, GPv2EIP1271} from "src/contracts/interfaces/GPv2EIP1271.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2SafeERC20} from "src/contracts/libraries/GPv2SafeERC20.sol"; +import {SafeMath} from "src/contracts/libraries/SafeMath.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; + +import {Sign} from "../libraries/Sign.sol"; +import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +/// @title Proof of Concept Smart Order +/// @author Gnosis Developers +contract SmartSellOrder is EIP1271Verifier { + using GPv2Order for GPv2Order.Data; + using GPv2SafeERC20 for IERC20; + using SafeMath for uint256; + + bytes32 public constant APPDATA = keccak256("SmartSellOrder"); + + address public immutable owner; + bytes32 public immutable domainSeparator; + IERC20 public immutable sellToken; + IERC20 public immutable buyToken; + uint256 public immutable totalSellAmount; + uint256 public immutable totalFeeAmount; + uint32 public immutable validTo; + + constructor( + GPv2Settlement settlement, + IERC20 sellToken_, + IERC20 buyToken_, + uint32 validTo_, + uint256 totalSellAmount_, + uint256 totalFeeAmount_ + ) { + owner = msg.sender; + domainSeparator = settlement.domainSeparator(); + sellToken = sellToken_; + buyToken = buyToken_; + validTo = validTo_; + totalSellAmount = totalSellAmount_; + totalFeeAmount = totalFeeAmount_; + + sellToken_.approve(address(settlement.vaultRelayer()), type(uint256).max); + } + + modifier onlyOwner() { + require(msg.sender == owner, "not owner"); + _; + } + + function withdraw(uint256 amount) external onlyOwner { + sellToken.safeTransfer(owner, amount); + } + + function close() external onlyOwner { + uint256 balance = sellToken.balanceOf(address(this)); + if (balance != 0) { + sellToken.safeTransfer(owner, balance); + } + } + + function isValidSignature(bytes32 hash, bytes memory signature) + external + view + override + returns (bytes4 magicValue) + { + uint256 sellAmount = abi.decode(signature, (uint256)); + GPv2Order.Data memory order = orderForSellAmount(sellAmount); + + if (order.hash(domainSeparator) == hash) { + magicValue = GPv2EIP1271.MAGICVALUE; + } + } + + function orderForSellAmount(uint256 sellAmount) public view returns (GPv2Order.Data memory order) { + order.sellToken = sellToken; + order.buyToken = buyToken; + order.receiver = owner; + order.sellAmount = sellAmount; + order.buyAmount = buyAmountForSellAmount(sellAmount); + order.validTo = validTo; + order.appData = APPDATA; + order.feeAmount = totalFeeAmount.mul(sellAmount).div(totalSellAmount); + order.kind = GPv2Order.KIND_SELL; + // NOTE: We counter-intuitively set `partiallyFillable` to `false`, even + // if the smart order as a whole acts like a partially fillable order. + // This is done since, once a settlement commits to a specific sell + // amount, then it is expected to use it completely and not partially. + order.partiallyFillable = false; + order.sellTokenBalance = GPv2Order.BALANCE_ERC20; + order.buyTokenBalance = GPv2Order.BALANCE_ERC20; + } + + function buyAmountForSellAmount(uint256 sellAmount) private view returns (uint256 buyAmount) { + uint256 feeAdjustedBalance = + sellToken.balanceOf(address(this)).mul(totalSellAmount).div(totalSellAmount.add(totalFeeAmount)); + uint256 soldAmount = totalSellAmount > feeAdjustedBalance ? totalSellAmount - feeAdjustedBalance : 0; + + // NOTE: This is currently a silly price strategy where the xrate + // increases linearly from 1:1 to 1:2 as the smart order gets filled. + // This can be extended to more complex "price curves". + buyAmount = sellAmount.mul(totalSellAmount.add(sellAmount).add(soldAmount)).div(totalSellAmount); + } +} + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +contract SmartOrderTest is Helper(false) { + IERC20Mintable token1; + IERC20Mintable token2; + + function setUp() public override { + super.setUp(); + + token1 = deployMintableErc20("TK1", "TK1"); + token2 = deployMintableErc20("TK2", "TK2"); + } + + function test_permits_trader_allowance_with_settlement() external { + Vm.Wallet memory trader1 = vm.createWallet("trader1"); + Vm.Wallet memory trader2 = vm.createWallet("trader2"); + + // mint some tokens + token1.mint(trader1.addr, 1.01 ether); + vm.prank(trader1.addr); + // approve tokens to vault relayer + token1.approve(vaultRelayer, type(uint256).max); + // place order to buy 0.5 token2 with 1 token1 max + encoder.signEncodeTrade( + vm, + trader1, + GPv2Order.Data({ + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellToken: token1, + buyToken: token2, + sellAmount: 1 ether, + buyAmount: 0.5 ether, + feeAmount: 0.01 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + vm.prank(trader2.addr); + SmartSellOrder smartOrder = new SmartSellOrder(settlement, token2, token1, 0xffffffff, 1 ether, 0.1 ether); + token2.mint(trader2.addr, 1.1 ether); + vm.prank(trader2.addr); + token2.transfer(address(smartOrder), 1.1 ether); + + uint256 smartOrderSellAmount = 0.5 ether; + GPv2Order.Data memory smartOrderTrade = smartOrder.orderForSellAmount(smartOrderSellAmount); + GPv2Order.Data memory expectedOrder = GPv2Order.Data({ + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellToken: token2, + buyToken: token1, + receiver: trader2.addr, + sellAmount: smartOrderSellAmount, + buyAmount: 0.75 ether, + feeAmount: 0.05 ether, + validTo: 0xffffffff, + appData: smartOrder.APPDATA(), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20 + }); + assertEq( + keccak256(abi.encode(smartOrderTrade)), keccak256(abi.encode(expectedOrder)), "smart order not as expected" + ); + + encoder.encodeTrade( + smartOrderTrade, + Sign.Signature({ + scheme: GPv2Signing.Scheme.Eip1271, + data: abi.encodePacked(address(smartOrder), abi.encode(smartOrderSellAmount)) + }), + 0 + ); + + // set token prices + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = token1; + tokens[1] = token2; + uint256[] memory prices = new uint256[](2); + prices[0] = 10; + prices[1] = 15; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + + vm.prank(solver); + settle(encodedSettlement); + + assertEq(token1.balanceOf(trader2.addr), 0.75 ether); + } +} diff --git a/test/e2e/smartOrder.test.ts b/test/e2e/smartOrder.test.ts deleted file mode 100644 index 921cfd3f..00000000 --- a/test/e2e/smartOrder.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import { expect } from "chai"; -import { Contract, ContractFactory, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { - OrderBalance, - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, -} from "../../src/ts"; -import { decodeOrder } from "../encoding"; - -import { deployTestContracts } from "./fixture"; - -describe("E2E: Dumb Smart Order", () => { - let deployer: Wallet; - let solver: Wallet; - let traders: Wallet[]; - - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let tokens: [Contract, Contract]; - - let SmartSellOrder: ContractFactory; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - settlement, - vaultRelayer, - wallets: [solver, ...traders], - } = deployment); - - const { authenticator, manager } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - tokens = [ - await waffle.deployContract(deployer, ERC20, ["T0", 18]), - await waffle.deployContract(deployer, ERC20, ["T1", 18]), - ]; - - SmartSellOrder = await ethers.getContractFactory("SmartSellOrder"); - }); - - it("permits trader allowance with settlement", async () => { - // Settle half of the smart order. - const encoder = new SettlementEncoder(domainSeparator); - - await tokens[0].mint(traders[0].address, ethers.utils.parseEther("1.01")); - await tokens[0] - .connect(traders[0]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - sellToken: tokens[0].address, - buyToken: tokens[1].address, - sellAmount: ethers.utils.parseEther("1.0"), - buyAmount: ethers.utils.parseEther("0.5"), - feeAmount: ethers.utils.parseEther("0.01"), - validTo: 0xffffffff, - appData: 1, - }, - traders[0], - SigningScheme.EIP712, - ); - - const smartOrder = await SmartSellOrder.connect(traders[1]).deploy( - settlement.address, - tokens[1].address, - tokens[0].address, - 0xffffffff, - ethers.utils.parseEther("1.0"), - ethers.utils.parseEther("0.1"), - ); - await tokens[1].mint(traders[1].address, ethers.utils.parseEther("1.1")); - await tokens[1] - .connect(traders[1]) - .transfer(smartOrder.address, ethers.utils.parseEther("1.1")); - - const smartOrderSellAmount = ethers.utils.parseEther("0.5"); - const smartOrderTrade = decodeOrder( - await smartOrder.orderForSellAmount(smartOrderSellAmount), - ); - expect(smartOrderTrade).to.deep.equal({ - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: tokens[1].address, - buyToken: tokens[0].address, - receiver: traders[1].address, - sellAmount: smartOrderSellAmount, - buyAmount: ethers.utils.parseEther("0.75"), - feeAmount: ethers.utils.parseEther("0.05"), - validTo: 0xffffffff, - appData: await smartOrder.APPDATA(), - sellTokenBalance: OrderBalance.ERC20, - buyTokenBalance: OrderBalance.ERC20, - }); - - await encoder.encodeTrade(smartOrderTrade, { - scheme: SigningScheme.EIP1271, - data: { - verifier: smartOrder.address, - signature: ethers.utils.defaultAbiCoder.encode( - ["uint256"], - [smartOrderSellAmount], - ), - }, - }); - - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [tokens[0].address]: 10, - [tokens[1].address]: 15, - }), - ); - - expect(await tokens[0].balanceOf(traders[1].address)).to.deep.equal( - ethers.utils.parseEther("0.75"), - ); - }); -}); diff --git a/test/src/SmartSellOrder.sol b/test/src/SmartSellOrder.sol deleted file mode 100644 index 23336f53..00000000 --- a/test/src/SmartSellOrder.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// solhint-disable-next-line compiler-version -pragma solidity >=0.7.6 <0.9.0; -pragma abicoder v2; - -import "src/contracts/GPv2Settlement.sol"; -import "src/contracts/interfaces/GPv2EIP1271.sol"; -import "src/contracts/interfaces/IERC20.sol"; -import "src/contracts/libraries/GPv2Order.sol"; -import "src/contracts/libraries/GPv2SafeERC20.sol"; -import "src/contracts/libraries/SafeMath.sol"; - -/// @title Proof of Concept Smart Order -/// @author Gnosis Developers -contract SmartSellOrder is EIP1271Verifier { - using GPv2Order for GPv2Order.Data; - using GPv2SafeERC20 for IERC20; - using SafeMath for uint256; - - bytes32 public constant APPDATA = keccak256("SmartSellOrder"); - - address public immutable owner; - bytes32 public immutable domainSeparator; - IERC20 public immutable sellToken; - IERC20 public immutable buyToken; - uint256 public immutable totalSellAmount; - uint256 public immutable totalFeeAmount; - uint32 public immutable validTo; - - constructor( - GPv2Settlement settlement, - IERC20 sellToken_, - IERC20 buyToken_, - uint32 validTo_, - uint256 totalSellAmount_, - uint256 totalFeeAmount_ - ) { - owner = msg.sender; - domainSeparator = settlement.domainSeparator(); - sellToken = sellToken_; - buyToken = buyToken_; - validTo = validTo_; - totalSellAmount = totalSellAmount_; - totalFeeAmount = totalFeeAmount_; - - sellToken_.approve(address(settlement.vaultRelayer()), type(uint256).max); - } - - modifier onlyOwner() { - require(msg.sender == owner, "not owner"); - _; - } - - function withdraw(uint256 amount) external onlyOwner { - sellToken.safeTransfer(owner, amount); - } - - function close() external onlyOwner { - uint256 balance = sellToken.balanceOf(address(this)); - if (balance != 0) { - sellToken.safeTransfer(owner, balance); - } - } - - function isValidSignature(bytes32 hash, bytes memory signature) - external - view - override - returns (bytes4 magicValue) - { - uint256 sellAmount = abi.decode(signature, (uint256)); - GPv2Order.Data memory order = orderForSellAmount(sellAmount); - - if (order.hash(domainSeparator) == hash) { - magicValue = GPv2EIP1271.MAGICVALUE; - } - } - - function orderForSellAmount(uint256 sellAmount) public view returns (GPv2Order.Data memory order) { - order.sellToken = sellToken; - order.buyToken = buyToken; - order.receiver = owner; - order.sellAmount = sellAmount; - order.buyAmount = buyAmountForSellAmount(sellAmount); - order.validTo = validTo; - order.appData = APPDATA; - order.feeAmount = totalFeeAmount.mul(sellAmount).div(totalSellAmount); - order.kind = GPv2Order.KIND_SELL; - // NOTE: We counter-intuitively set `partiallyFillable` to `false`, even - // if the smart order as a whole acts like a partially fillable order. - // This is done since, once a settlement commits to a specific sell - // amount, then it is expected to use it completely and not partially. - order.partiallyFillable = false; - order.sellTokenBalance = GPv2Order.BALANCE_ERC20; - order.buyTokenBalance = GPv2Order.BALANCE_ERC20; - } - - function buyAmountForSellAmount(uint256 sellAmount) private view returns (uint256 buyAmount) { - uint256 feeAdjustedBalance = - sellToken.balanceOf(address(this)).mul(totalSellAmount).div(totalSellAmount.add(totalFeeAmount)); - uint256 soldAmount = totalSellAmount > feeAdjustedBalance ? totalSellAmount - feeAdjustedBalance : 0; - - // NOTE: This is currently a silly price strategy where the xrate - // increases linearly from 1:1 to 1:2 as the smart order gets filled. - // This can be extended to more complex "price curves". - buyAmount = sellAmount.mul(totalSellAmount.add(sellAmount).add(soldAmount)).div(totalSellAmount); - } -} From 672c1ba903b6ae4a625215649cb79d1e6f50f648 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 02:42:20 -0700 Subject: [PATCH 14/23] chore: migrate E2E uniswapTrade test to Foundry (#222) ## Description Migrate the uniswapTrade e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #141 --- test/e2e/UniswapTrade.t.sol | 195 ++++++++++++++++++++++++++++++++ test/e2e/uniswapTrade.test.ts | 206 ---------------------------------- 2 files changed, 195 insertions(+), 206 deletions(-) create mode 100644 test/e2e/UniswapTrade.t.sol delete mode 100644 test/e2e/uniswapTrade.test.ts diff --git a/test/e2e/UniswapTrade.t.sol b/test/e2e/UniswapTrade.t.sol new file mode 100644 index 00000000..ab71c6f2 --- /dev/null +++ b/test/e2e/UniswapTrade.t.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; + +import {GPv2Interaction} from "src/contracts/libraries/GPv2Interaction.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; + +import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +interface IUniswapV2Factory { + function createPair(address, address) external returns (address); +} + +interface IUniswapV2Pair { + function token0() external view returns (address); + function mint(address) external; + function swap(uint256, uint256, address, bytes calldata) external; + function getReserves() external view returns (uint256, uint256); +} + +contract UniswapTradeTest is Helper(true) { + IERC20Mintable dai; + IERC20Mintable wETH; + + IUniswapV2Factory factory; + IUniswapV2Pair uniswapPair; + + bool isWethToken0; + + function setUp() public override { + super.setUp(); + + dai = deployMintableErc20("dai", "dai"); + wETH = deployMintableErc20("wETH", "wETH"); + + factory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); + uniswapPair = IUniswapV2Pair(factory.createPair(address(wETH), address(dai))); + + isWethToken0 = uniswapPair.token0() == address(wETH); + } + + // Settles the following batch: + // + // /----(1. SELL 1 wETH for dai if p(wETH) >= 500)-----\ + // | | + // | v + // [dai]<---(Uniswap Pair 1000 wETH / 600.000 dai)--->[wETH] + // ^ | + // | | + // \----(2. BUY 0.5 wETH for dai if p(wETH) <= 600)----/ + function test_should_two_overlapping_orders_and_trade_surplus_with_uniswap() external { + uint256 wethReserve = 1000 ether; + uint256 daiReserve = 600000 ether; + wETH.mint(address(uniswapPair), wethReserve); + dai.mint(address(uniswapPair), daiReserve); + uniswapPair.mint(address(this)); + + // The current batch has a sell order selling 1 wETH and a buy order buying + // 0.5 wETH. This means there is exactly a surplus 0.5 wETH that needs to be + // sold to Uniswap. Uniswap is governed by a balancing equation which can be + // used to compute the exact buy amount for selling the 0.5 wETH and we can + // use to build our the settlement with a smart contract interaction. + // ``` + // (reservewETH + inwETH * 0.997) * (reservedai - outdai) = reservewETH * reservedai + // outdai = (reservedai * inwETH * 0.997) / (reservewETH + inwETH * 0.997) + // = (reservedai * inwETH * 997) / (reservewETH * 1000 + inwETH * 997) + // ``` + uint256 uniswapWethInAmount = 0.5 ether; + uint256 uniswapDaiOutAmount = + daiReserve * uniswapWethInAmount * 997 / ((wethReserve * 1000) + (uniswapWethInAmount * 997)); + + Vm.Wallet memory trader1 = vm.createWallet("trader1"); + Vm.Wallet memory trader2 = vm.createWallet("trader2"); + + // mint some weth + wETH.mint(trader1.addr, 1.001 ether); + vm.prank(trader1.addr); + wETH.approve(vaultRelayer, type(uint256).max); + + // place order to sell 1 wETH for min 500 dai + encoder.signEncodeTrade( + vm, + trader1, + GPv2Order.Data({ + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellToken: wETH, + buyToken: dai, + sellAmount: 1 ether, + buyAmount: 500 ether, + feeAmount: 0.001 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some dai + dai.mint(trader2.addr, 300.3 ether); + vm.prank(trader2.addr); + dai.approve(vaultRelayer, type(uint256).max); + + // place order to buy 0.5 wETH for max 300 dai + encoder.signEncodeTrade( + vm, + trader2, + GPv2Order.Data({ + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellToken: dai, + buyToken: wETH, + sellAmount: 300 ether, + buyAmount: 0.5 ether, + feeAmount: 0.3 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // interaction to swap the remainder on uniswap + encoder.addInteraction( + GPv2Interaction.Data({ + target: address(wETH), + value: 0, + callData: abi.encodeCall(IERC20.transfer, (address(uniswapPair), uniswapWethInAmount)) + }), + SettlementEncoder.InteractionStage.INTRA + ); + (uint256 amount0Out, uint256 amount1Out) = + isWethToken0 ? (uint256(0), uniswapDaiOutAmount) : (uniswapDaiOutAmount, uint256(0)); + encoder.addInteraction( + GPv2Interaction.Data({ + target: address(uniswapPair), + value: 0, + callData: abi.encodeCall(IUniswapV2Pair.swap, (amount0Out, amount1Out, address(settlement), hex"")) + }), + SettlementEncoder.InteractionStage.INTRA + ); + + // set token prices + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = wETH; + tokens[1] = dai; + uint256[] memory prices = new uint256[](2); + prices[0] = uniswapDaiOutAmount; + prices[1] = uniswapWethInAmount; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + + vm.prank(solver); + settle(encodedSettlement); + + assertEq(wETH.balanceOf(address(settlement)), 0.001 ether, "weth fees not as expected"); + assertEq(dai.balanceOf(address(settlement)), 0.3 ether, "dai fees not as expected"); + + assertEq(wETH.balanceOf(trader1.addr), 0, "not all weth sold"); + assertEq(dai.balanceOf(trader1.addr), uniswapDaiOutAmount * 2, "dai received not as expected"); + + assertEq(wETH.balanceOf(trader2.addr), 0.5 ether, "weth bought not correct amount"); + assertEq(dai.balanceOf(trader2.addr), 300.3 ether - (uniswapDaiOutAmount + 0.3 ether)); + + uint256 finalWethReserve; + uint256 finalDaiReserve; + + { + (uint256 token0Reserve, uint256 token1Reserve) = uniswapPair.getReserves(); + (finalWethReserve, finalDaiReserve) = + isWethToken0 ? (token0Reserve, token1Reserve) : (token1Reserve, token0Reserve); + } + assertEq(finalWethReserve, wethReserve + uniswapWethInAmount, "weth reserve not as expected"); + assertEq(finalDaiReserve, daiReserve - uniswapDaiOutAmount, "dai reserve not as expected"); + } +} diff --git a/test/e2e/uniswapTrade.test.ts b/test/e2e/uniswapTrade.test.ts deleted file mode 100644 index e7080af8..00000000 --- a/test/e2e/uniswapTrade.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import UniswapV2Factory from "@uniswap/v2-core/build/UniswapV2Factory.json"; -import UniswapV2Pair from "@uniswap/v2-core/build/UniswapV2Pair.json"; -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -describe("E2E: Should Trade Surplus With Uniswap", () => { - let deployer: Wallet; - let solver: Wallet; - let pooler: Wallet; - let traders: Wallet[]; - - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let weth: Contract; - let usdt: Contract; - let uniswapPair: Contract; - let isWethToken0: boolean; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - settlement, - vaultRelayer, - wallets: [solver, pooler, ...traders], - } = deployment); - - const { authenticator, manager } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - weth = await waffle.deployContract(deployer, ERC20, ["WETH", 18]); - usdt = await waffle.deployContract(deployer, ERC20, ["USDT", 6]); - - const uniswapFactory = await waffle.deployContract( - deployer, - UniswapV2Factory, - [deployer.address], - ); - await uniswapFactory.createPair(weth.address, usdt.address); - uniswapPair = new Contract( - await uniswapFactory.getPair(weth.address, usdt.address), - UniswapV2Pair.abi, - deployer, - ); - - // NOTE: Which token ends up as token 0 or token 1 depends on the addresses - // of the WETH and USDT token which can change depending on which order the - // tests are run. Because of this, check the Uniswap pair to see which token - // ended up on which index. - isWethToken0 = (await uniswapPair.token0()) === weth.address; - }); - - it("should settle two overlapping orders and trade surplus with Uniswap", async () => { - // Settles the following batch: - // - // /----(1. SELL 1 WETH for USDT if p(WETH) >= 500)-----\ - // | | - // | v - // [USDT]<---(Uniswap Pair 1000 WETH / 600.000 USDT)--->[WETH] - // ^ | - // | | - // \----(2. BUY 0.5 WETH for USDT if p(WETH) <= 600)----/ - - const uniswapWethReserve = ethers.utils.parseEther("1000.0"); - const uniswapUsdtReserve = ethers.utils.parseUnits("600000.0", 6); - await weth.mint(uniswapPair.address, uniswapWethReserve); - await usdt.mint(uniswapPair.address, uniswapUsdtReserve); - await uniswapPair.mint(pooler.address); - - // The current batch has a sell order selling 1 WETH and a buy order buying - // 0.5 WETH. This means there is exactly a surplus 0.5 WETH that needs to be - // sold to Uniswap. Uniswap is governed by a balancing equation which can be - // used to compute the exact buy amount for selling the 0.5 WETH and we can - // use to build our the settlement with a smart contract interaction. - // ``` - // (reserveWETH + inWETH * 0.997) * (reserveUSDT - outUSDT) = reserveWETH * reserveUSDT - // outUSDT = (reserveUSDT * inWETH * 0.997) / (reserveWETH + inWETH * 0.997) - // = (reserveUSDT * inWETH * 997) / (reserveWETH * 1000 + inWETH * 997) - // ``` - const uniswapWethInAmount = ethers.utils.parseEther("0.5"); - const uniswapUsdtOutAmount = uniswapUsdtReserve - .mul(uniswapWethInAmount) - .mul(997) - .div(uniswapWethReserve.mul(1000).add(uniswapWethInAmount.mul(997))); - - const encoder = new SettlementEncoder(domainSeparator); - - await weth.mint(traders[0].address, ethers.utils.parseEther("1.001")); - await weth - .connect(traders[0]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: weth.address, - buyToken: usdt.address, - sellAmount: ethers.utils.parseEther("1.0"), - buyAmount: ethers.utils.parseUnits("500.0", 6), - feeAmount: ethers.utils.parseEther("0.001"), - validTo: 0xffffffff, - appData: 1, - }, - traders[0], - SigningScheme.EIP712, - ); - - await usdt.mint(traders[1].address, ethers.utils.parseUnits("300.3", 6)); - await usdt - .connect(traders[1]) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - partiallyFillable: false, - buyToken: weth.address, - sellToken: usdt.address, - buyAmount: ethers.utils.parseEther("0.5"), - sellAmount: ethers.utils.parseUnits("300.0", 6), - feeAmount: ethers.utils.parseUnits("0.3", 6), - validTo: 0xffffffff, - appData: 2, - }, - traders[1], - SigningScheme.EIP712, - ); - - encoder.encodeInteraction({ - target: weth.address, - callData: weth.interface.encodeFunctionData("transfer", [ - uniswapPair.address, - uniswapWethInAmount, - ]), - }); - - const [amount0Out, amount1Out] = isWethToken0 - ? [0, uniswapUsdtOutAmount] - : [uniswapUsdtOutAmount, 0]; - encoder.encodeInteraction({ - target: uniswapPair.address, - callData: uniswapPair.interface.encodeFunctionData("swap", [ - amount0Out, - amount1Out, - settlement.address, - "0x", - ]), - }); - - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [weth.address]: uniswapUsdtOutAmount, - [usdt.address]: uniswapWethInAmount, - }), - ); - - expect(await weth.balanceOf(settlement.address)).to.deep.equal( - ethers.utils.parseEther("0.001"), - ); - expect(await usdt.balanceOf(settlement.address)).to.deep.equal( - ethers.utils.parseUnits("0.3", 6), - ); - - expect(await weth.balanceOf(traders[0].address)).to.deep.equal( - ethers.constants.Zero, - ); - expect(await usdt.balanceOf(traders[0].address)).to.deep.equal( - uniswapUsdtOutAmount.mul(2), - ); - - expect(await weth.balanceOf(traders[1].address)).to.deep.equal( - ethers.utils.parseEther("0.5"), - ); - expect(await usdt.balanceOf(traders[1].address)).to.deep.equal( - ethers.utils - .parseUnits("300.3", 6) - .sub(uniswapUsdtOutAmount.add(ethers.utils.parseUnits("0.3", 6))), - ); - - const [token0Reserve, token1Reserve] = await uniswapPair.getReserves(); - const [finalWethReserve, finalUsdtReserve] = isWethToken0 - ? [token0Reserve, token1Reserve] - : [token1Reserve, token0Reserve]; - expect([finalWethReserve, finalUsdtReserve]).to.deep.equal([ - uniswapWethReserve.add(uniswapWethInAmount), - uniswapUsdtReserve.sub(uniswapUsdtOutAmount), - ]); - }); -}); From d3e1c195c16158cad1b9b1b6b756972f2ef8988c Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 03:37:47 -0700 Subject: [PATCH 15/23] chore: migrate E2E contractOrdersWithGnosisSafe test to Foundry (#223) ## Description Migrate the contractOrdersWithGnosisSafe e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #136 --- test/e2e/ContractOrdersWithGnosisSafe.t.sol | 246 ++++++++++++++ test/e2e/contractOrdersWithGnosisSafe.test.ts | 302 ------------------ 2 files changed, 246 insertions(+), 302 deletions(-) create mode 100644 test/e2e/ContractOrdersWithGnosisSafe.t.sol delete mode 100644 test/e2e/contractOrdersWithGnosisSafe.test.ts diff --git a/test/e2e/ContractOrdersWithGnosisSafe.t.sol b/test/e2e/ContractOrdersWithGnosisSafe.t.sol new file mode 100644 index 00000000..17a09184 --- /dev/null +++ b/test/e2e/ContractOrdersWithGnosisSafe.t.sol @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; + +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; + +import {Eip712} from "../libraries/Eip712.sol"; + +import {Sign} from "../libraries/Sign.sol"; +import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; + +interface ISafeProxyFactory { + function createProxy(address singleton, bytes calldata data) external returns (ISafe); +} + +interface ISafe { + enum Operation { + Call, + DelegateCall + } + + function setup( + address[] calldata _owners, + uint256 _threshold, + address to, + bytes calldata data, + address fallbackHandler, + address paymentToken, + uint256 payment, + address payable paymentReceiver + ) external; + + function execTransaction( + address to, + uint256 value, + bytes calldata data, + Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address payable refundReceiver, + bytes memory signatures + ) external payable returns (bool success); + + function getTransactionHash( + address to, + uint256 value, + bytes calldata data, + Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address refundReceiver, + uint256 _nonce + ) external view returns (bytes32); + + function nonce() external view returns (uint256); + + function getMessageHash(bytes calldata) external view returns (bytes32); + + function isValidSignature(bytes32, bytes calldata) external view returns (bytes4); +} + +ISafeProxyFactory constant SAFE_PROXY_FACTORY = ISafeProxyFactory(0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2); +address constant SAFE_SINGLETON = 0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552; +address constant SAFE_COMPATIBILITY_FALLBACK_HANDLER = 0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4; +bytes4 constant EIP1271_MAGICVALUE = bytes4(keccak256("isValidSignature(bytes32,bytes)")); + +contract ContractOrdersWithGnosisSafeTest is Helper(true) { + IERC20Mintable dai; + IERC20Mintable wETH; + + ISafe safe; + + Vm.Wallet signer1; + Vm.Wallet signer2; + Vm.Wallet signer3; + Vm.Wallet signer4; + Vm.Wallet signer5; + + function setUp() public override { + super.setUp(); + + dai = deployMintableErc20("dai", "dai"); + wETH = deployMintableErc20("wETH", "wETH"); + + signer1 = vm.createWallet("signer1"); + signer2 = vm.createWallet("signer2"); + signer3 = vm.createWallet("signer3"); + signer4 = vm.createWallet("signer4"); + signer5 = vm.createWallet("signer5"); + + address[] memory signers = new address[](5); + signers[0] = signer1.addr; + signers[1] = signer2.addr; + signers[2] = signer3.addr; + signers[3] = signer4.addr; + signers[4] = signer5.addr; + + bytes memory data = abi.encodeCall( + ISafe.setup, + (signers, 2, address(0), hex"", SAFE_COMPATIBILITY_FALLBACK_HANDLER, address(0), 0, payable(address(0))) + ); + safe = SAFE_PROXY_FACTORY.createProxy(SAFE_SINGLETON, data); + } + + function test_should_settle_matching_orders() external { + // EOA trader: sell 1 wETH for 900 dai + // Safe: buy 1 wETH for 1100 dai + // Settlement price at 1000 dai for 1 wETH. + + // mint some tokens to trader + wETH.mint(trader.addr, 1.001 ether); + // approve the tokens for trading on settlement contract + vm.prank(trader.addr); + wETH.approve(vaultRelayer, type(uint256).max); + + // place order to sell 1 wETH for min 900 dai + encoder.signEncodeTrade( + vm, + trader, + GPv2Order.Data({ + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellToken: wETH, + buyToken: dai, + sellAmount: 1 ether, + buyAmount: 900 ether, + feeAmount: 0.001 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // mint some dai to the safe + dai.mint(address(safe), 1110 ether); + // approve dai for trading on settlement contract + _execSafeTransaction( + safe, address(dai), 0, abi.encodeCall(IERC20.approve, (vaultRelayer, type(uint256).max)), signer1, signer2 + ); + assertEq(dai.allowance(address(safe), vaultRelayer), type(uint256).max, "allowance not as expected"); + + // place order to buy 1 wETH with max 1100 dai + GPv2Order.Data memory order = GPv2Order.Data({ + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellToken: dai, + buyToken: wETH, + sellAmount: 1100 ether, + buyAmount: 1 ether, + feeAmount: 10 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }); + + bytes32 orderHash = Eip712.typedDataHash(Eip712.toEip712SignedStruct(order), domainSeparator); + bytes32 safeMessageHash = safe.getMessageHash(abi.encode(orderHash)); + bytes memory signatures = _safeSignature(signer3, signer4, safeMessageHash); + + assertEq(safe.isValidSignature(orderHash, signatures), EIP1271_MAGICVALUE, "invalid signature for the order"); + + encoder.encodeTrade( + order, + Sign.Signature({scheme: GPv2Signing.Scheme.Eip1271, data: abi.encodePacked(address(safe), signatures)}), + 0 + ); + + // set token prices + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = dai; + tokens[1] = wETH; + uint256[] memory prices = new uint256[](2); + prices[0] = 1 ether; + prices[1] = 1000 ether; + encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); + + SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); + + vm.prank(solver); + settle(encodedSettlement); + + assertEq(wETH.balanceOf(trader.addr), 0, "trader weth balance not as expected"); + assertEq(dai.balanceOf(trader.addr), 1000 ether, "trader dai balance not as expected"); + + assertEq(wETH.balanceOf(address(safe)), 1 ether, "safe weth balance not as expected"); + assertEq(dai.balanceOf(address(safe)), 100 ether, "safe dai balance not as expected"); + + assertEq(wETH.balanceOf(address(settlement)), 0.001 ether, "settlement weth fee not as expected"); + assertEq(dai.balanceOf(address(settlement)), 10 ether, "settlement dai fee not as expected"); + } + + function _execSafeTransaction( + ISafe safe_, + address to, + uint256 value, + bytes memory data, + Vm.Wallet memory signer1_, + Vm.Wallet memory signer2_ + ) internal { + uint256 nonce = safe_.nonce(); + bytes32 hash = + safe_.getTransactionHash(to, value, data, ISafe.Operation.Call, 0, 0, 0, address(0), address(0), nonce); + bytes memory signatures = _safeSignature(signer1_, signer2_, hash); + safe_.execTransaction( + to, value, data, ISafe.Operation.Call, 0, 0, 0, address(0), payable(address(0)), signatures + ); + } + + function _sign(Vm.Wallet memory wallet, bytes32 hash) internal returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(wallet, hash); + return abi.encodePacked(r, s, v); + } + + function _safeSignature(Vm.Wallet memory signer1_, Vm.Wallet memory signer2_, bytes32 hash) + internal + returns (bytes memory) + { + bytes memory signature1 = _sign(signer1_, hash); + bytes memory signature2 = _sign(signer2_, hash); + bytes memory signatures = signer1_.addr < signer2_.addr + ? abi.encodePacked(signature1, signature2) + : abi.encodePacked(signature2, signature1); + return signatures; + } +} diff --git a/test/e2e/contractOrdersWithGnosisSafe.test.ts b/test/e2e/contractOrdersWithGnosisSafe.test.ts deleted file mode 100644 index 9d5a6093..00000000 --- a/test/e2e/contractOrdersWithGnosisSafe.test.ts +++ /dev/null @@ -1,302 +0,0 @@ -import GnosisSafe from "@gnosis.pm/safe-contracts/build/artifacts/contracts/GnosisSafe.sol/GnosisSafe.json"; -import CompatibilityFallbackHandler from "@gnosis.pm/safe-contracts/build/artifacts/contracts/handler/CompatibilityFallbackHandler.sol/CompatibilityFallbackHandler.json"; -import GnosisSafeProxyFactory from "@gnosis.pm/safe-contracts/build/artifacts/contracts/proxies/GnosisSafeProxyFactory.sol/GnosisSafeProxyFactory.json"; -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import { expect } from "chai"; -import { BytesLike, Signer, Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import { - EIP1271_MAGICVALUE, - OrderKind, - SettlementEncoder, - SigningScheme, - TypedDataDomain, - domain, - hashOrder, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -interface SafeTransaction { - to: string; - data: BytesLike; -} - -class GnosisSafeManager { - constructor( - readonly deployer: Signer, - readonly masterCopy: Contract, - readonly signingFallback: Contract, - readonly proxyFactory: Contract, - ) {} - - static async init(deployer: Signer): Promise { - const masterCopy = await waffle.deployContract(deployer, GnosisSafe); - const proxyFactory = await waffle.deployContract( - deployer, - GnosisSafeProxyFactory, - ); - const signingFallback = await waffle.deployContract( - deployer, - CompatibilityFallbackHandler, - ); - return new GnosisSafeManager( - deployer, - masterCopy, - signingFallback, - proxyFactory, - ); - } - - async newSafe( - owners: string[], - threshold: number, - fallback = ethers.constants.AddressZero, - ): Promise { - const proxyAddress = await this.proxyFactory.callStatic.createProxy( - this.masterCopy.address, - "0x", - ); - await this.proxyFactory.createProxy(this.masterCopy.address, "0x"); - const safe = await ethers.getContractAt(GnosisSafe.abi, proxyAddress); - await safe.setup( - owners, - threshold, - ethers.constants.AddressZero, - "0x", - fallback, - ethers.constants.AddressZero, - 0, - ethers.constants.AddressZero, - ); - return safe; - } -} - -async function gnosisSafeSign( - message: BytesLike, - signers: Signer[], -): Promise { - // https://docs.gnosis.io/safe/docs/contracts_signatures/ - const signerAddresses = await Promise.all( - signers.map(async (signer) => (await signer.getAddress()).toLowerCase()), - ); - const sortedSigners = signers - .map((_, index) => index) - .sort((lhs, rhs) => - signerAddresses[lhs] < signerAddresses[rhs] - ? -1 - : signerAddresses[lhs] > signerAddresses[rhs] - ? 1 - : 0, - ) - .map((index) => signers[index]); - - async function encodeEcdsaSignature( - message: BytesLike, - signer: Signer, - ): Promise { - const sig = await signer.signMessage(ethers.utils.arrayify(message)); - const { r, s, v } = ethers.utils.splitSignature(sig); - return ethers.utils.hexConcat([r, s, [v + 4]]); - } - return ethers.utils.hexConcat( - await Promise.all( - sortedSigners.map( - async (signer) => await encodeEcdsaSignature(message, signer), - ), - ), - ); -} - -async function execSafeTransaction( - safe: Contract, - transaction: SafeTransaction, - signers: Signer[], -): Promise { - // most parameters are not needed for this test - const transactionParameters = [ - transaction.to, - 0, - transaction.data, - 0, - 0, - 0, - 0, - ethers.constants.AddressZero, - ethers.constants.AddressZero, - ]; - const nonce = await safe.nonce(); - const message = await safe.getTransactionHash( - ...transactionParameters, - nonce, - ); - const sigs = await gnosisSafeSign(message, signers); - await safe.execTransaction(...transactionParameters, sigs); -} - -async function fallbackSign( - safeAsFallback: Contract, - message: BytesLike, - signers: Signer[], -): Promise { - const safeMessage = await safeAsFallback.getMessageHash( - ethers.utils.defaultAbiCoder.encode(["bytes32"], [message]), - ); - return gnosisSafeSign(safeMessage, signers); -} - -describe("E2E: Order From A Gnosis Safe", () => { - let deployer: Wallet; - let solver: Wallet; - let trader: Wallet; - let safeOwners: Wallet[]; - - let settlement: Contract; - let vaultRelayer: Contract; - let safe: Contract; - let domainSeparator: TypedDataDomain; - let gnosisSafeManager: GnosisSafeManager; - - beforeEach(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - settlement, - vaultRelayer, - wallets: [solver, trader, ...safeOwners], - } = deployment); - - const { authenticator, manager } = deployment; - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - gnosisSafeManager = await GnosisSafeManager.init(deployer); - safe = await gnosisSafeManager.newSafe( - safeOwners.map((wallet) => wallet.address), - 2, - gnosisSafeManager.signingFallback.address, - ); - }); - - it("should settle matching orders", async () => { - // EOA trader: sell 1 WETH for 900 DAI - // Safe: buy 1 WETH for 1100 DAI - // Settlement price at 1000 DAI for 1 WETH. - - const erc20 = (symbol: string) => - waffle.deployContract(deployer, ERC20, [symbol, 18]); - - const dai = await erc20("DAI"); - const weth = await erc20("WETH"); - - const UNLIMITED_VALID_TO = 0xffffffff; - const encoder = new SettlementEncoder(domainSeparator); - - const TRADER_FEE = ethers.utils.parseEther("0.001"); - const TRADER_SOLD_AMOUNT = ethers.utils.parseEther("1.0"); - const TRADER_BOUGHT_AMOUNT = ethers.utils.parseEther("900.0"); - - await weth.mint(trader.address, TRADER_SOLD_AMOUNT.add(TRADER_FEE)); - await weth - .connect(trader) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - - encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - partiallyFillable: false, - sellToken: weth.address, - buyToken: dai.address, - sellAmount: TRADER_SOLD_AMOUNT, - buyAmount: TRADER_BOUGHT_AMOUNT, - appData: 0, - validTo: UNLIMITED_VALID_TO, - feeAmount: TRADER_FEE, - }, - trader, - SigningScheme.ETHSIGN, - ); - - const SAFE_FEE = ethers.utils.parseEther("10.0"); - const SAFE_SOLD_AMOUNT = ethers.utils.parseEther("1100.0"); - const SAFE_BOUGHT_AMOUNT = ethers.utils.parseEther("1.0"); - - await dai.mint(safe.address, SAFE_SOLD_AMOUNT.add(SAFE_FEE)); - const approveTransaction = { - to: dai.address, - data: dai.interface.encodeFunctionData("approve", [ - vaultRelayer.address, - ethers.constants.MaxUint256, - ]), - }; - await execSafeTransaction(safe, approveTransaction, safeOwners); - expect( - await dai.allowance(safe.address, vaultRelayer.address), - ).to.deep.equal(ethers.constants.MaxUint256); - - const order = { - kind: OrderKind.BUY, - partiallyFillable: false, - sellToken: dai.address, - buyToken: weth.address, - sellAmount: SAFE_SOLD_AMOUNT, - buyAmount: SAFE_BOUGHT_AMOUNT, - appData: 0, - validTo: UNLIMITED_VALID_TO, - feeAmount: SAFE_FEE, - }; - const gpv2Message = hashOrder(domainSeparator, order); - const safeAsFallback = gnosisSafeManager.signingFallback.attach( - safe.address, - ); - // Note: threshold is 2, any two owners should suffice. - const signature = await fallbackSign(safeAsFallback, gpv2Message, [ - safeOwners[4], - safeOwners[2], - ]); - // Note: the fallback handler provides two versions of `isValidSignature` - // for compatibility reasons. The following is the signature of the most - // recent EIP1271-compatible verification function. - const isValidSignature = "isValidSignature(bytes32,bytes)"; - expect( - await safeAsFallback.callStatic[isValidSignature](gpv2Message, signature), - ).to.equal(EIP1271_MAGICVALUE); - - encoder.encodeTrade(order, { - scheme: SigningScheme.EIP1271, - data: { - verifier: safe.address, - signature, - }, - }); - - await settlement.connect(solver).settle( - ...encoder.encodedSettlement({ - [dai.address]: ethers.utils.parseEther("1.0"), - [weth.address]: ethers.utils.parseEther("1000.0"), - }), - ); - - expect(await weth.balanceOf(trader.address)).to.deep.equal( - ethers.constants.Zero, - ); - expect(await dai.balanceOf(trader.address)).to.deep.equal( - ethers.utils.parseEther("1000.0"), - ); - - expect(await weth.balanceOf(safe.address)).to.deep.equal( - ethers.utils.parseEther("1.0"), - ); - expect(await dai.balanceOf(safe.address)).to.deep.equal( - SAFE_SOLD_AMOUNT.sub(ethers.utils.parseEther("1000.0")), - ); - - expect(await weth.balanceOf(settlement.address)).to.deep.equal(TRADER_FEE); - expect(await dai.balanceOf(settlement.address)).to.deep.equal(SAFE_FEE); - }); -}); From b6871bc87e9ff58b87439a871c7a2e5c7b3858f9 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 04:55:53 -0700 Subject: [PATCH 16/23] chore: migrate E2E deployment test to Foundry (#225) ## Description Migrate the deployment e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #149 --- test/e2e/Deployment.t.sol | 110 ++++++++++++++++++++++++++++ test/e2e/deployment.test.ts | 141 ------------------------------------ 2 files changed, 110 insertions(+), 141 deletions(-) create mode 100644 test/e2e/Deployment.t.sol delete mode 100644 test/e2e/deployment.test.ts diff --git a/test/e2e/Deployment.t.sol b/test/e2e/Deployment.t.sol new file mode 100644 index 00000000..456549e5 --- /dev/null +++ b/test/e2e/Deployment.t.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {GPv2AllowListAuthentication} from "src/contracts/GPv2AllowListAuthentication.sol"; + +import {Helper} from "./Helper.sol"; + +interface IEIP173Proxy { + function owner() external view returns (address); +} + +// ref: https://github.com/wighawag/hardhat-deploy/blob/e0ffcf9e7dc92b246e832c4d175f9dbd8b6df14d/solc_0.8/proxy/EIP173Proxy.sol +bytes32 constant EIP173_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + +contract DeploymentTest is Helper(false) { + event Metadata(string, bytes); + + function test__same_built_and_deployed_bytecode_metadata__authenticator() external { + _assertBuiltAndDeployedMetadataCoincide(address(allowListImpl), "GPv2AllowListAuthentication"); + } + + function test__same_built_and_deployed_bytecode_metadata__settlement() external { + _assertBuiltAndDeployedMetadataCoincide(address(settlement), "GPv2Settlement"); + } + + function test__same_built_and_deployed_bytecode_metadata__vault_relayer() external { + _assertBuiltAndDeployedMetadataCoincide(address(vaultRelayer), "GPv2VaultRelayer"); + } + + function test__determininstic_addresses__authenticator__proxy() external view { + assertEq( + _computeCreate2Addr( + abi.encodePacked( + vm.getCode("EIP173Proxy"), + abi.encode( + _implementationAddress(address(allowList)), + owner, + abi.encodeCall(GPv2AllowListAuthentication.initializeManager, (owner)) + ) + ) + ), + address(authenticator), + "authenticator address not as expected" + ); + } + + function test__determininstic_addresses__authenticator__implementation() external view { + assertEq( + _computeCreate2Addr(vm.getCode("GPv2AllowListAuthentication")), + _implementationAddress(address(allowList)), + "authenticator impl address not as expected" + ); + } + + function test__determininstic_addresses__settlement() external view { + assertEq( + _computeCreate2Addr( + abi.encodePacked(vm.getCode("GPv2Settlement"), abi.encode(address(authenticator), address(vault))) + ), + address(settlement), + "settlement address not as expected" + ); + } + + function test__authorization__authenticator_has_dedicated_owner() external view { + assertEq(IEIP173Proxy(address(allowList)).owner(), owner, "owner not as expected"); + } + + function test__authorization__authenticator_has_dedicated_manager() external view { + assertEq(allowList.manager(), owner, "manager not as expected"); + } + + function _assertBuiltAndDeployedMetadataCoincide(address addr, string memory artifactName) internal { + bytes memory deployedCode = vm.getDeployedCode(artifactName); + assertEq( + keccak256(_getMetadata(string(abi.encodePacked("deployed ", artifactName)), addr.code)), + keccak256(_getMetadata(artifactName, deployedCode)), + "metadata doesnt match" + ); + } + + function _getMetadata(string memory hint, bytes memory bytecode) internal returns (bytes memory metadata) { + assembly ("memory-safe") { + // the last two bytes encode the size of the cbor encoded metadata + let bytecodeSize := mload(bytecode) + let bytecodeStart := add(bytecode, 0x20) + let cborSizeOffset := add(bytecodeStart, sub(bytecodeSize, 0x20)) + let cborSize := and(mload(cborSizeOffset), 0xffff) + + // copy the metadata out + metadata := mload(0x40) + let metadataSize := add(cborSize, 0x02) + mstore(metadata, metadataSize) + let metadataOffset := add(bytecodeStart, sub(bytecodeSize, metadataSize)) + mcopy(add(metadata, 0x20), metadataOffset, metadataSize) + + // update free memory ptr + mstore(0x40, add(metadata, add(metadataSize, 0x20))) + } + emit Metadata(hint, metadata); + } + + function _computeCreate2Addr(bytes memory initCode) internal view returns (address) { + return vm.computeCreate2Address(SALT, hashInitCode(initCode), deployer); + } + + function _implementationAddress(address proxy) internal view returns (address) { + return address(uint160(uint256(vm.load(proxy, EIP173_IMPLEMENTATION_SLOT)))); + } +} diff --git a/test/e2e/deployment.test.ts b/test/e2e/deployment.test.ts deleted file mode 100644 index 394c2c53..00000000 --- a/test/e2e/deployment.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { expect } from "chai"; -import { Contract, Wallet } from "ethers"; -import { artifacts, ethers } from "hardhat"; -import Proxy from "hardhat-deploy/extendedArtifacts/EIP173Proxy.json"; - -import { - ContractName, - DeploymentArguments, - deterministicDeploymentAddress, - implementationAddress, - proxyInterface, -} from "../../src/ts"; - -import { deployTestContracts } from "./fixture"; - -async function contractAddress( - contractName: C, - ...deploymentArguments: DeploymentArguments -): Promise { - const artifact = await artifacts.readArtifact(contractName); - return deterministicDeploymentAddress(artifact, deploymentArguments); -} - -async function builtAndDeployedMetadataCoincide( - contractAddress: string, - contractName: string, -): Promise { - const contractArtifacts = await artifacts.readArtifact(contractName); - - const code = await ethers.provider.send("eth_getCode", [ - contractAddress, - "latest", - ]); - - // NOTE: The last 53 bytes in a deployed contract's bytecode contains the - // contract metadata. Compare the deployed contract's metadata with the - // compiled contract's metadata. - // - const metadata = (bytecode: string) => bytecode.slice(-106); - - return metadata(code) === metadata(contractArtifacts.deployedBytecode); -} - -describe("E2E: Deployment", () => { - let owner: Wallet; - let manager: Wallet; - let user: Wallet; - - let authenticator: Contract; - let vault: Contract; - let settlement: Contract; - let vaultRelayer: Contract; - - beforeEach(async () => { - ({ - owner, - manager, - wallets: [user], - authenticator, - vault, - settlement, - vaultRelayer, - } = await deployTestContracts()); - - authenticator.connect(user); - settlement.connect(user); - vaultRelayer.connect(user); - }); - - describe("same built and deployed bytecode metadata", () => { - it("authenticator", async () => { - expect( - await builtAndDeployedMetadataCoincide( - await implementationAddress(ethers.provider, authenticator.address), - "GPv2AllowListAuthentication", - ), - ).to.be.true; - }); - - it("settlement", async () => { - expect( - await builtAndDeployedMetadataCoincide( - settlement.address, - "GPv2Settlement", - ), - ).to.be.true; - }); - - it("vaultRelayer", async () => { - expect( - await builtAndDeployedMetadataCoincide( - vaultRelayer.address, - "GPv2VaultRelayer", - ), - ).to.be.true; - }); - }); - - describe("deterministic addresses", () => { - describe("authenticator", () => { - it("proxy", async () => { - expect( - deterministicDeploymentAddress(Proxy, [ - await implementationAddress(ethers.provider, authenticator.address), - owner.address, - authenticator.interface.encodeFunctionData("initializeManager", [ - manager.address, - ]), - ]), - ).to.equal(authenticator.address); - }); - - it("implementation", async () => { - expect(await contractAddress("GPv2AllowListAuthentication")).to.equal( - await implementationAddress(ethers.provider, authenticator.address), - ); - }); - }); - - it("settlement", async () => { - expect( - await contractAddress( - "GPv2Settlement", - authenticator.address, - vault.address, - ), - ).to.equal(settlement.address); - }); - }); - - describe("authorization", () => { - it("authenticator has dedicated owner", async () => { - const proxy = proxyInterface(authenticator); - expect(await proxy.owner()).to.equal(owner.address); - }); - - it("authenticator has dedicated manager", async () => { - expect(await authenticator.manager()).to.equal(manager.address); - }); - }); -}); From 1cbd0de536b63f1684eb0a2d4be27b9ac3d144b5 Mon Sep 17 00:00:00 2001 From: mfw78 <53399572+mfw78@users.noreply.github.com> Date: Wed, 9 Oct 2024 03:44:04 +0000 Subject: [PATCH 17/23] chore: remove env (#231) ## Description This PR addresses the environment setup that was used for fork tests. Now, the `FORK_URL` is set by a secret, and the `FORK_BLOCK_NUMBER` is hard coded within the `e2e` Helper as a constant so that assumptions are visible for auditors / developers. ## Test Plan 1. Confirm CI/CD green ## Related Issues N/A --- .github/workflows/test.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7fcf67db..cc84c7f5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,10 +31,6 @@ jobs: name: Foundry project runs-on: ubuntu-latest - env: - # should support archive requests - FORK_URL: 'https://eth.llamarpc.com' - FORK_BLOCK_NUMBER: 20691292 steps: - uses: actions/checkout@v4 with: From 92ae3b519ba90b36561077e871a1c868ac54ba30 Mon Sep 17 00:00:00 2001 From: Meet Mangukiya Date: Tue, 8 Oct 2024 23:34:32 -0700 Subject: [PATCH 18/23] chore: migrate E2E balancerSwap test to Foundry (#224) ## Description Migrate the balancerSwap e2e test to foundry. Depends on https://github.com/cowprotocol/contracts/pull/215. ## Test Plan CI ## Related Issues Closes #133 --- test/e2e/BalancerSwap.t.sol | 539 ++++++++++++++++++++++ test/e2e/balancerSwap.test.ts | 488 -------------------- test/libraries/encoders/SwapEncoder.sol | 4 +- test/libraries/encoders/TokenRegistry.sol | 6 + 4 files changed, 547 insertions(+), 490 deletions(-) create mode 100644 test/e2e/BalancerSwap.t.sol delete mode 100644 test/e2e/balancerSwap.test.ts diff --git a/test/e2e/BalancerSwap.t.sol b/test/e2e/BalancerSwap.t.sol new file mode 100644 index 00000000..c26dbda9 --- /dev/null +++ b/test/e2e/BalancerSwap.t.sol @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +pragma solidity ^0.8; + +import {Vm} from "forge-std/Vm.sol"; + +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {IVault} from "src/contracts/interfaces/IVault.sol"; + +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; + +import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; +import {SwapEncoder} from "../libraries/encoders/SwapEncoder.sol"; +import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; + +using SettlementEncoder for SettlementEncoder.State; +using TokenRegistry for TokenRegistry.State; +using TokenRegistry for Registry; +using SwapEncoder for SwapEncoder.State; + +interface IMockPool { + function registerTokens(IERC20[] calldata tokens, address[] calldata assetManagers) external; + function getPoolId() external view returns (bytes32); + function setMultiplier(uint256) external; +} + +interface IBalancerVault is IVault { + struct JoinPoolRequest { + IERC20[] assets; + uint256[] maxAmountsIn; + bytes userData; + bool fromInternalBalance; + } + + function joinPool(bytes32 poolId, address sender, address recipient, JoinPoolRequest calldata request) external; + function setRelayerApproval(address, address, bool) external; + function getInternalBalance(address user, IERC20[] calldata tokens) external view returns (uint256[] memory); +} + +contract BalancerSwapTest is Helper(true) { + IERC20Mintable token1; + IERC20Mintable token2; + IERC20Mintable token3; + + mapping(address => mapping(address => address)) pools; + + function setUp() public override { + super.setUp(); + + token1 = deployMintableErc20("TOK1", "TOK1"); + token2 = deployMintableErc20("TOK2", "TOK2"); + token3 = deployMintableErc20("TOK3", "TOK3"); + + Vm.Wallet memory pooler = vm.createWallet("pooler"); + + IERC20Mintable[] memory tokens = new IERC20Mintable[](3); + tokens[0] = token1; + tokens[1] = token2; + tokens[2] = token3; + + uint256 lots = 10000 ether; + + for (uint256 i = 0; i < tokens.length; i++) { + (IERC20Mintable token0_, IERC20Mintable token1_) = (tokens[i], tokens[(i + 1) % tokens.length]); + (IERC20Mintable tokenA, IERC20Mintable tokenB) = + address(token0_) < address(token1_) ? (token0_, token1_) : (token1_, token0_); + + uint256 twoTokenSpecialization = 2; + + vm.startPrank(deployer); + IMockPool pool = IMockPool( + _create( + abi.encodePacked( + vm.getCode("balancer/test/MockPool.json"), abi.encode(address(vault), twoTokenSpecialization) + ), + 0 + ) + ); + IERC20[] memory tks = new IERC20[](2); + tks[0] = tokenA; + tks[1] = tokenB; + address[] memory assetManagers = new address[](2); + assetManagers[0] = address(0); + assetManagers[1] = address(0); + pool.registerTokens(tks, assetManagers); + vm.stopPrank(); + + for (uint256 j = 0; j < tks.length; j++) { + IERC20Mintable(address(tks[j])).mint(pooler.addr, lots); + vm.prank(pooler.addr); + tks[j].approve(address(vault), type(uint256).max); + } + + uint256[] memory maxAmountsIn = new uint256[](2); + maxAmountsIn[0] = lots; + maxAmountsIn[1] = lots; + uint256[] memory poolFees = new uint256[](2); + IBalancerVault.JoinPoolRequest memory request = IBalancerVault.JoinPoolRequest({ + assets: tks, + maxAmountsIn: maxAmountsIn, + // NOTE: The mock pool uses this for encoding the pool share amounts + // that a user (here `pooler`) gets when joining the pool (first value) + // as well as the pool fees (second value). + userData: abi.encode(maxAmountsIn, poolFees), + fromInternalBalance: false + }); + // poolerAddr declared as separate var to prevent stack too deep errors + address poolerAddr = pooler.addr; + bytes32 poolId = pool.getPoolId(); + vm.prank(poolerAddr); + IBalancerVault(address(vault)).joinPool(poolId, poolerAddr, poolerAddr, request); + + pools[address(tokenA)][address(tokenB)] = address(pool); + pools[address(tokenB)][address(tokenA)] = address(pool); + } + } + + function test_reverts_if_order_is_expired() external { + _mintAndApprove(trader, token1, 100.1 ether, GPv2Order.BALANCE_ERC20); + + swapEncoder.signEncodeTrade( + vm, + trader, + GPv2Order.Data({ + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellToken: token1, + buyToken: token2, + sellAmount: 100 ether, + buyAmount: 72 ether, + feeAmount: 0.1 ether, + validTo: uint32(block.timestamp) - 1, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); + vm.prank(solver); + // SWAP_DEADLINE + vm.expectRevert("BAL#508"); + swap(encodedSwap); + } + + function test_allows_using_liquidity_from_multiple_pools() external { + _mintAndApprove(trader, token1, 100.1 ether, GPv2Order.BALANCE_ERC20); + + swapEncoder.signEncodeTrade( + vm, + trader, + GPv2Order.Data({ + kind: GPv2Order.KIND_SELL, + partiallyFillable: false, + sellToken: token1, + buyToken: token3, + sellAmount: 100 ether, + buyAmount: 125 ether, + feeAmount: 0.1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // NOTE: Use liquidity by performing a multi-hop swap from `0 -> 1 -> 2`. + _poolFor(token1, token2).setMultiplier(1.1 ether); + swapEncoder.encodeSwapStep( + SwapEncoder.Swap({ + poolId: _poolFor(token1, token2).getPoolId(), + assetIn: token1, + assetOut: token2, + amount: 70 ether, + userData: hex"" + }) + ); + _poolFor(token2, token3).setMultiplier(1.2 ether); + swapEncoder.encodeSwapStep( + SwapEncoder.Swap({ + poolId: _poolFor(token2, token3).getPoolId(), + assetIn: token2, + assetOut: token3, + // NOTE: Setting amount to zero indicates a "multi-hop" swap and uses the + // computed `amountOut` of the previous swap. + amount: 0, + userData: hex"" + }) + ); + // NOTE: Also use liquidity from a direct `0 -> 2` pool. + _poolFor(token1, token3).setMultiplier(1.3 ether); + swapEncoder.encodeSwapStep( + SwapEncoder.Swap({ + poolId: _poolFor(token1, token3).getPoolId(), + assetIn: token1, + assetOut: token3, + amount: 30 ether, + userData: hex"" + }) + ); + + SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); + vm.prank(solver); + swap(encodedSwap); + + // NOTE: Sold 70 for 1.1*1.2 and 30 for 1.3, so should receive 131.4. + assertEq( + _balanceOf(trader.addr, token3, GPv2Order.BALANCE_ERC20), + 131.4 ether, + "multihop swap output not as expected" + ); + } + + function test_allows_multi_hop_buy_orders() external { + _mintAndApprove(trader, token1, 13.1 ether, GPv2Order.BALANCE_ERC20); + + swapEncoder.signEncodeTrade( + vm, + trader, + GPv2Order.Data({ + kind: GPv2Order.KIND_BUY, + partiallyFillable: false, + sellToken: token1, + buyToken: token3, + sellAmount: 13 ether, + buyAmount: 100 ether, + feeAmount: 0.1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + // NOTE: Use liquidity by performing a multi-hop swap from `2 -> 1 -> 0`. + _poolFor(token3, token2).setMultiplier(4 ether); + swapEncoder.encodeSwapStep( + SwapEncoder.Swap({ + poolId: _poolFor(token3, token2).getPoolId(), + assetOut: token3, + assetIn: token2, + amount: 100 ether, + userData: hex"" + }) + ); + _poolFor(token2, token1).setMultiplier(2 ether); + swapEncoder.encodeSwapStep( + SwapEncoder.Swap({ + poolId: _poolFor(token2, token1).getPoolId(), + assetOut: token2, + assetIn: token1, + // NOTE: Setting amount to zero indicates a "multi-hop" swap and uses the + // computed `amountIn` of the previous swap. + amount: 0, + userData: hex"" + }) + ); + + SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); + vm.prank(solver); + swap(encodedSwap); + + // NOTE: Bought 100 for 4.0*2.0, so should pay 12.5. + assertEq( + _balanceOf(trader.addr, token1, GPv2Order.BALANCE_ERC20), 0.5 ether, "multihop swap output not as expected" + ); + } + + function test_performs_balancer_swap_for_erc20_to_erc20_sell_order() external { + _testBalancerSwap(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_SELL); + } + + function test_performs_balancer_swap_for_erc20_to_internal_sell_order() external { + _testBalancerSwap(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_SELL); + } + + function test_performs_balancer_swap_for_internal_to_erc20_sell_order() external { + _testBalancerSwap(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_SELL); + } + + function test_performs_balancer_swap_for_internal_to_internal_sell_order() external { + _testBalancerSwap(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_SELL); + } + + function test_performs_balancer_swap_for_external_to_erc20_sell_order() external { + _testBalancerSwap(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_SELL); + } + + function test_performs_balancer_swap_for_external_to_internal_sell_order() external { + _testBalancerSwap(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_SELL); + } + + function test_performs_balancer_swap_for_erc20_to_erc20_buy_order() external { + _testBalancerSwap(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_BUY); + } + + function test_performs_balancer_swap_for_erc20_to_internal_buy_order() external { + _testBalancerSwap(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_BUY); + } + + function test_performs_balancer_swap_for_internal_to_erc20_buy_order() external { + _testBalancerSwap(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_BUY); + } + + function test_performs_balancer_swap_for_internal_to_internal_buy_order() external { + _testBalancerSwap(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_BUY); + } + + function test_performs_balancer_swap_for_external_to_erc20_buy_order() external { + _testBalancerSwap(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_BUY); + } + + function test_performs_balancer_swap_for_external_to_internal_buy_order() external { + _testBalancerSwap(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_BUY); + } + + function test_reverts_sell_order_if_fill_or_kill_is_not_respected() external { + _testBalancerRevertFillOrKill(GPv2Order.KIND_SELL); + } + + function test_reverts_buy_order_if_fill_or_kill_is_not_respected() external { + _testBalancerRevertFillOrKill(GPv2Order.KIND_BUY); + } + + function test_reverts_sell_order_if_limit_price_is_not_respected() external { + _testBalancerRevertLimitPrice(GPv2Order.KIND_SELL); + } + + function test_reverts_buy_order_if_limit_price_is_not_respected() external { + _testBalancerRevertLimitPrice(GPv2Order.KIND_BUY); + } + + function _testBalancerSwap(bytes32 sellSource, bytes32 buySource, bytes32 orderKind) internal { + _mintAndApprove(trader, token1, 100.1 ether, sellSource); + + IMockPool pool = _poolFor(token1, token2); + // NOTE: Set a fixed multiplier used for computing the exchange rate for + // the mock pool. In the wild, this would depend on the current state of + // the pool. + pool.setMultiplier(0.9 ether); + + swapEncoder.signEncodeTrade( + vm, + trader, + GPv2Order.Data({ + kind: orderKind, + partiallyFillable: false, + sellToken: token1, + buyToken: token2, + sellAmount: 100 ether, + buyAmount: 72 ether, + feeAmount: 0.1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: sellSource, + buyTokenBalance: buySource, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + swapEncoder.encodeSwapStep( + SwapEncoder.Swap({ + poolId: pool.getPoolId(), + assetIn: token1, + assetOut: token2, + amount: orderKind == GPv2Order.KIND_SELL ? 100 ether : 72 ether, + userData: hex"" + }) + ); + + SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); + + vm.prank(solver); + swap(encodedSwap); + + uint256 sellTokenBalance = _balanceOf(trader.addr, token1, sellSource); + uint256 buyTokenBalance = _balanceOf(trader.addr, token2, buySource); + + if (orderKind == GPv2Order.KIND_SELL) { + assertEq(sellTokenBalance, 0, "seller sellTokenBalance not as expected"); + assertEq(buyTokenBalance, 90 ether, "seller buyTokenBalance not as expected"); + } else { + assertEq(sellTokenBalance, 20 ether, "buyer sellTokenBalance not as expected"); + assertEq(buyTokenBalance, 72 ether, "buyer buyTokenBalance not as expected"); + } + } + + function _testBalancerRevertFillOrKill(bytes32 orderKind) internal { + _mintAndApprove(trader, token1, 100.1 ether, GPv2Order.BALANCE_ERC20); + + IMockPool pool = _poolFor(token1, token2); + pool.setMultiplier(2 ether); + + swapEncoder.signEncodeTrade( + vm, + trader, + GPv2Order.Data({ + kind: orderKind, + // NOTE: Partially fillable or not, it doesn't matter as the + // "fast-path" treats all orders as fill-or-kill orders. + partiallyFillable: true, + sellToken: token1, + buyToken: token2, + sellAmount: 100 ether, + buyAmount: 100 ether, + feeAmount: 0.1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + 0 + ); + + swapEncoder.encodeSwapStep( + SwapEncoder.Swap({ + poolId: pool.getPoolId(), + assetIn: token1, + assetOut: token2, + amount: orderKind == GPv2Order.KIND_SELL ? 99 ether : 101 ether, + userData: hex"" + }) + ); + + SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); + + vm.expectRevert( + bytes( + orderKind == GPv2Order.KIND_SELL ? "GPv2: sell amount not respected" : "GPv2: buy amount not respected" + ) + ); + vm.prank(solver); + swap(encodedSwap); + } + + function _testBalancerRevertLimitPrice(bytes32 orderKind) internal { + _mintAndApprove(trader, token1, 100.1 ether, GPv2Order.BALANCE_ERC20); + + IMockPool pool = _poolFor(token1, token2); + // NOTE: Set a multiplier that satisfies the order's limit price but not + // the specified limit amount. + pool.setMultiplier(1.1 ether); + + swapEncoder.signEncodeTrade( + vm, + trader, + GPv2Order.Data({ + kind: orderKind, + partiallyFillable: false, + sellToken: token1, + buyToken: token2, + sellAmount: 100 ether, + buyAmount: 100 ether, + feeAmount: 0.1 ether, + validTo: 0xffffffff, + appData: bytes32(uint256(1)), + sellTokenBalance: GPv2Order.BALANCE_ERC20, + buyTokenBalance: GPv2Order.BALANCE_ERC20, + receiver: GPv2Order.RECEIVER_SAME_AS_OWNER + }), + domainSeparator, + GPv2Signing.Scheme.Eip712, + orderKind == GPv2Order.KIND_SELL ? 120 ether : 80 ether + ); + + swapEncoder.encodeSwapStep( + SwapEncoder.Swap({ + poolId: pool.getPoolId(), + assetIn: token1, + assetOut: token2, + amount: 100 ether, + userData: hex"" + }) + ); + + SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); + + vm.expectRevert("BAL#507"); + vm.prank(solver); + swap(encodedSwap); + } + + function _mintAndApprove(Vm.Wallet memory wallet, IERC20Mintable token, uint256 amount, bytes32 balance) internal { + token.mint(wallet.addr, amount); + vm.startPrank(wallet.addr); + token.approve(vaultRelayer, type(uint256).max); + token.approve(address(vault), type(uint256).max); + vm.stopPrank(); + + if (balance == GPv2Order.BALANCE_INTERNAL) { + vm.prank(wallet.addr); + IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1); + ops[0] = IVault.UserBalanceOp({ + kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL, + asset: token, + amount: amount, + sender: wallet.addr, + recipient: payable(wallet.addr) + }); + vault.manageUserBalance(ops); + } + vm.prank(wallet.addr); + IBalancerVault(address(vault)).setRelayerApproval(wallet.addr, vaultRelayer, true); + } + + function _poolFor(IERC20 tk0, IERC20 tk1) internal view returns (IMockPool) { + return IMockPool(pools[address(tk0)][address(tk1)]); + } + + function _balanceOf(address user, IERC20 tk, bytes32 balance) internal view returns (uint256) { + if (balance == GPv2Order.BALANCE_INTERNAL) { + IERC20[] memory tks = new IERC20[](1); + tks[0] = tk; + uint256[] memory bals = IBalancerVault(address(vault)).getInternalBalance(user, tks); + return bals[0]; + } else { + return tk.balanceOf(user); + } + } +} diff --git a/test/e2e/balancerSwap.test.ts b/test/e2e/balancerSwap.test.ts deleted file mode 100644 index eeb38757..00000000 --- a/test/e2e/balancerSwap.test.ts +++ /dev/null @@ -1,488 +0,0 @@ -import ERC20 from "@openzeppelin/contracts/build/contracts/ERC20PresetMinterPauser.json"; -import { expect } from "chai"; -import Debug from "debug"; -import { BigNumberish, Contract, Wallet } from "ethers"; -import { ethers, waffle } from "hardhat"; - -import MockPool from "../../balancer/test/MockPool.json"; -import { - OrderBalance, - OrderKind, - SwapEncoder, - SigningScheme, - TypedDataDomain, - domain, - grantRequiredRoles, -} from "../../src/ts"; -import { UserBalanceOpKind } from "../balancer"; - -import { deployTestContracts } from "./fixture"; - -enum BalancerErrors { - SWAP_LIMIT = "BAL#507", - SWAP_DEADLINE = "BAL#508", -} - -const LOTS = ethers.utils.parseEther("10000.0"); -const debug = Debug("e2e:balancerSwap"); - -describe("E2E: Direct Balancer swap", () => { - let deployer: Wallet; - let solver: Wallet; - let pooler: Wallet; - let trader: Wallet; - - let vault: Contract; - let settlement: Contract; - let vaultRelayer: Contract; - let domainSeparator: TypedDataDomain; - - let tokens: [Contract, Contract, Contract]; - let pools: Record; - - let snapshot: unknown; - - before(async () => { - const deployment = await deployTestContracts(); - - ({ - deployer, - vault, - settlement, - vaultRelayer, - wallets: [solver, pooler, trader], - } = deployment); - - const { vaultAuthorizer, authenticator, manager } = deployment; - await grantRequiredRoles( - vaultAuthorizer.connect(manager), - vault.address, - vaultRelayer.address, - ); - await authenticator.connect(manager).addSolver(solver.address); - - const { chainId } = await ethers.provider.getNetwork(); - domainSeparator = domain(chainId, settlement.address); - - tokens = [ - await waffle.deployContract(deployer, ERC20, ["TOK1", 18]), - await waffle.deployContract(deployer, ERC20, ["TOK2", 18]), - await waffle.deployContract(deployer, ERC20, ["TOK3", 18]), - ]; - - pools = {}; - for (let i = 0; i < tokens.length; i++) { - const [token0, token1] = [tokens[i], tokens[(i + 1) % tokens.length]]; - const [tokenA, tokenB] = - token0.address.toLowerCase() < token1.address.toLowerCase() - ? [token0, token1] - : [token1, token0]; - - const TWO_TOKEN_SPECIALIZATION = 2; - const pool = await waffle.deployContract(deployer, MockPool, [ - vault.address, - TWO_TOKEN_SPECIALIZATION, - ]); - await pool.registerTokens( - [tokenA.address, tokenB.address], - [ethers.constants.AddressZero, ethers.constants.AddressZero], - ); - - for (const token of [tokenA, tokenB]) { - await token.mint(pooler.address, LOTS); - await token - .connect(pooler) - .approve(vault.address, ethers.constants.MaxUint256); - } - await vault - .connect(pooler) - .joinPool(await pool.getPoolId(), pooler.address, pooler.address, { - assets: [tokenA.address, tokenB.address], - maxAmountsIn: [LOTS, LOTS], - // NOTE: The mock pool uses this for encoding the pool share amounts - // that a user (here `pooler`) gets when joining the pool (first value) - // as well as the pool fees (second value). - userData: ethers.utils.defaultAbiCoder.encode( - ["uint256[]", "uint256[]"], - [ - [LOTS, LOTS], - [0, 0], - ], - ), - fromInternalBalance: false, - }); - - pools[`${tokenA.address}-${tokenB.address}`] = pool; - pools[`${tokenB.address}-${tokenA.address}`] = pool; - } - - snapshot = await ethers.provider.send("evm_snapshot", []); - }); - - beforeEach(async () => { - // NOTE: Use EVM snapshots to speed up test execution, as the setup time is - // quite high (around 1s **per test**). Oddly, snapshots need to be - // re-created every time they are reverted. - await ethers.provider.send("evm_revert", [snapshot]); - snapshot = await ethers.provider.send("evm_snapshot", []); - }); - - const poolFor = (tokenA: Contract, tokenB: Contract) => { - return pools[`${tokenA.address}-${tokenB.address}`]; - }; - - const mintAndApprove = async ( - trader: Wallet, - token: Contract, - amount: BigNumberish, - balance = OrderBalance.ERC20, - ) => { - await token.mint(trader.address, amount); - // NOTE: For now, approve both the Vault and the Vault relayer since we do - // not distinguish between `ERC20` and `EXTERNAL` balance configurations. - await token - .connect(trader) - .approve(vaultRelayer.address, ethers.constants.MaxUint256); - await token - .connect(trader) - .approve(vault.address, ethers.constants.MaxUint256); - if (balance == OrderBalance.INTERNAL) { - await vault.connect(trader).manageUserBalance([ - { - kind: UserBalanceOpKind.DEPOSIT_INTERNAL, - asset: token.address, - amount, - sender: trader.address, - recipient: trader.address, - }, - ]); - } - await vault - .connect(trader) - .setRelayerApproval(trader.address, vaultRelayer.address, true); - }; - - const balanceOf = async ( - { address }: Wallet | Contract, - token: Contract, - balance = OrderBalance.ERC20, - ) => { - if (balance == OrderBalance.INTERNAL) { - const [balance] = await vault.getInternalBalance(address, [ - token.address, - ]); - return balance; - } else { - return await token.balanceOf(address); - } - }; - - const balanceVariants = [ - OrderBalance.ERC20, - OrderBalance.EXTERNAL, - OrderBalance.INTERNAL, - ].flatMap((sellTokenBalance) => - [OrderBalance.ERC20, OrderBalance.INTERNAL].map((buyTokenBalance) => { - return { - name: `${sellTokenBalance} to ${buyTokenBalance}`, - sellTokenBalance, - buyTokenBalance, - }; - }), - ); - - for (const kind of [OrderKind.SELL, OrderKind.BUY]) { - for (const { name, ...balances } of balanceVariants) { - it(`performs Balancer swap for ${name} ${kind} order`, async () => { - await mintAndApprove( - trader, - tokens[0], - ethers.utils.parseEther("100.1"), - balances.sellTokenBalance, - ); - - const pool = poolFor(tokens[0], tokens[1]); - // NOTE: Set a fixed multiplier used for computing the exchange rate for - // the mock pool. In the wild, this would depend on the current state of - // the pool. - await pool.setMultiplier(ethers.utils.parseEther("0.9")); - - const encoder = new SwapEncoder(domainSeparator); - await encoder.signEncodeTrade( - { - sellToken: tokens[0].address, - buyToken: tokens[1].address, - sellAmount: ethers.utils.parseEther("100.0"), - buyAmount: ethers.utils.parseEther("72.0"), - feeAmount: ethers.utils.parseEther("0.1"), - validTo: 0xffffffff, - appData: 0, - partiallyFillable: false, - kind, - ...balances, - }, - trader, - SigningScheme.EIP712, - ); - encoder.encodeSwapStep({ - poolId: await pool.getPoolId(), - assetIn: tokens[0].address, - assetOut: tokens[1].address, - amount: - kind == OrderKind.SELL - ? ethers.utils.parseEther("100.0") - : ethers.utils.parseEther("72.0"), - }); - - const tx = await settlement - .connect(solver) - .swap(...encoder.encodedSwap()); - - const { gasUsed } = await tx.wait(); - debug(`${name} gas: ${gasUsed}`); - - const sellTokenBalance = await balanceOf( - trader, - tokens[0], - balances.sellTokenBalance, - ); - const buyTokenBalance = await balanceOf( - trader, - tokens[1], - balances.buyTokenBalance, - ); - - // NOTE: User keeps positive surplus! - if (kind == OrderKind.SELL) { - expect(sellTokenBalance).to.equal(ethers.constants.Zero); - expect(buyTokenBalance).to.equal(ethers.utils.parseEther("90.0")); - } else { - expect(sellTokenBalance).to.equal(ethers.utils.parseEther("20.0")); - expect(buyTokenBalance).to.equal(ethers.utils.parseEther("72.0")); - } - }); - } - - it(`reverts ${kind} order if fill-or-kill amount is not respected`, async () => { - await mintAndApprove(trader, tokens[0], ethers.utils.parseEther("100.1")); - - const pool = poolFor(tokens[0], tokens[1]); - // NOTE: Set a very favourable multiplier for the swap. - await pool.setMultiplier(ethers.utils.parseEther("2.0")); - - const encoder = new SwapEncoder(domainSeparator); - await encoder.signEncodeTrade( - { - sellToken: tokens[0].address, - buyToken: tokens[1].address, - sellAmount: ethers.utils.parseEther("100.0"), - buyAmount: ethers.utils.parseEther("100.0"), - feeAmount: ethers.utils.parseEther("0.1"), - validTo: 0xffffffff, - appData: 0, - // NOTE: Partially fillable or not, it doesn't matter as the - // "fast-path" treats all orders as fill-or-kill orders. - partiallyFillable: true, - kind, - }, - trader, - SigningScheme.EIP712, - ); - encoder.encodeSwapStep({ - poolId: await pool.getPoolId(), - assetIn: tokens[0].address, - assetOut: tokens[1].address, - // NOTE: Set "better" amounts, where we pay less and get more. These, - // however, should still cause a revert as they aren't the exact amounts - // that were requested in the orders. - amount: - kind == OrderKind.SELL - ? ethers.utils.parseEther("99.0") - : ethers.utils.parseEther("101.0"), - }); - - await expect( - settlement.connect(solver).swap(...encoder.encodedSwap()), - ).to.be.revertedWith(`${kind} amount not respected`); - }); - - it(`reverts ${kind} order if limit price is not respected`, async () => { - await mintAndApprove(trader, tokens[0], ethers.utils.parseEther("100.1")); - - const pool = poolFor(tokens[0], tokens[1]); - // NOTE: Set a multiplier that satisfies the order's limit price but not - // the specified limit amount. - await pool.setMultiplier(ethers.utils.parseEther("1.1")); - - const encoder = new SwapEncoder(domainSeparator); - await encoder.signEncodeTrade( - { - sellToken: tokens[0].address, - buyToken: tokens[1].address, - sellAmount: ethers.utils.parseEther("100.0"), - buyAmount: ethers.utils.parseEther("100.0"), - feeAmount: ethers.utils.parseEther("0.1"), - validTo: 0xffffffff, - appData: 0, - partiallyFillable: false, - kind, - }, - trader, - SigningScheme.EIP712, - { - limitAmount: - kind == OrderKind.SELL - ? ethers.utils.parseEther("120.0") - : ethers.utils.parseEther("80.0"), - }, - ); - encoder.encodeSwapStep({ - poolId: await pool.getPoolId(), - assetIn: tokens[0].address, - assetOut: tokens[1].address, - amount: ethers.utils.parseEther("100.0"), - }); - - await expect( - settlement.connect(solver).swap(...encoder.encodedSwap()), - ).to.be.revertedWith(BalancerErrors.SWAP_LIMIT); - }); - } - - it("reverts if order is expired", async () => { - const { timestamp } = await ethers.provider.getBlock("latest"); - - await mintAndApprove(trader, tokens[0], ethers.utils.parseEther("100.1")); - - const encoder = new SwapEncoder(domainSeparator); - await encoder.signEncodeTrade( - { - sellToken: tokens[0].address, - buyToken: tokens[1].address, - sellAmount: ethers.utils.parseEther("100.0"), - buyAmount: ethers.utils.parseEther("72.0"), - feeAmount: ethers.utils.parseEther("0.1"), - validTo: timestamp - 1, - appData: 0, - kind: OrderKind.SELL, - partiallyFillable: false, - }, - trader, - SigningScheme.EIP712, - ); - - await expect( - settlement.connect(solver).swap(...encoder.encodedSwap()), - ).to.be.revertedWith(BalancerErrors.SWAP_DEADLINE); - }); - - it("allows using liquidity from multiple pools", async () => { - await mintAndApprove(trader, tokens[0], ethers.utils.parseEther("100.1")); - - const encoder = new SwapEncoder(domainSeparator); - await encoder.signEncodeTrade( - { - kind: OrderKind.SELL, - sellToken: tokens[0].address, - buyToken: tokens[2].address, - sellAmount: ethers.utils.parseEther("100.0"), - buyAmount: ethers.utils.parseEther("125.0"), - feeAmount: ethers.utils.parseEther("0.1"), - validTo: 0xffffffff, - appData: 0, - partiallyFillable: false, - }, - trader, - SigningScheme.EIP712, - ); - - // NOTE: Use liquidity by performing a multi-hop swap from `0 -> 1 -> 2`. - await poolFor(tokens[0], tokens[1]).setMultiplier( - ethers.utils.parseEther("1.1"), - ); - encoder.encodeSwapStep({ - poolId: await poolFor(tokens[0], tokens[1]).getPoolId(), - assetIn: tokens[0].address, - assetOut: tokens[1].address, - amount: ethers.utils.parseEther("70.0"), - }); - await poolFor(tokens[1], tokens[2]).setMultiplier( - ethers.utils.parseEther("1.2"), - ); - encoder.encodeSwapStep({ - poolId: await poolFor(tokens[1], tokens[2]).getPoolId(), - assetIn: tokens[1].address, - assetOut: tokens[2].address, - // NOTE: Setting amount to zero indicates a "multi-hop" swap and uses the - // computed `amountOut` of the previous swap. - amount: ethers.constants.Zero, - }); - // NOTE: Also use liquidity from a direct `0 -> 2` pool. - await poolFor(tokens[0], tokens[2]).setMultiplier( - ethers.utils.parseEther("1.3"), - ); - encoder.encodeSwapStep({ - poolId: await poolFor(tokens[0], tokens[2]).getPoolId(), - assetIn: tokens[0].address, - assetOut: tokens[2].address, - amount: ethers.utils.parseEther("30.0"), - }); - - await settlement.connect(solver).swap(...encoder.encodedSwap()); - - // NOTE: Sold 70 for 1.1*1.2 and 30 for 1.3, so should receive 131.4. - expect(await balanceOf(trader, tokens[2])).to.equal( - ethers.utils.parseEther("131.4"), - ); - }); - - it("allows multi-hop buy orders", async () => { - await mintAndApprove(trader, tokens[0], ethers.utils.parseEther("13.1")); - - const encoder = new SwapEncoder(domainSeparator); - await encoder.signEncodeTrade( - { - kind: OrderKind.BUY, - buyToken: tokens[2].address, - sellToken: tokens[0].address, - buyAmount: ethers.utils.parseEther("100.0"), - sellAmount: ethers.utils.parseEther("13.0"), - feeAmount: ethers.utils.parseEther("0.1"), - validTo: 0xffffffff, - appData: 0, - partiallyFillable: false, - }, - trader, - SigningScheme.EIP712, - ); - - // NOTE: Use liquidity by performing a multi-hop swap from `2 -> 1 -> 0`. - await poolFor(tokens[2], tokens[1]).setMultiplier( - ethers.utils.parseEther("4.0"), - ); - encoder.encodeSwapStep({ - poolId: await poolFor(tokens[2], tokens[1]).getPoolId(), - assetOut: tokens[2].address, - assetIn: tokens[1].address, - amount: ethers.utils.parseEther("100.0"), - }); - await poolFor(tokens[1], tokens[0]).setMultiplier( - ethers.utils.parseEther("2.0"), - ); - encoder.encodeSwapStep({ - poolId: await poolFor(tokens[1], tokens[0]).getPoolId(), - assetOut: tokens[1].address, - assetIn: tokens[0].address, - // NOTE: Setting amount to zero indicates a "multi-hop" swap and uses the - // computed `amountIn` of the previous swap. - amount: ethers.constants.Zero, - }); - - await settlement.connect(solver).swap(...encoder.encodedSwap()); - - // NOTE: Bought 100 for 4.0*2.0, so should pay 12.5. - expect(await balanceOf(trader, tokens[0])).to.equal( - ethers.utils.parseEther("0.5"), - ); - }); -}); diff --git a/test/libraries/encoders/SwapEncoder.sol b/test/libraries/encoders/SwapEncoder.sol index e8ad513c..e51771b6 100644 --- a/test/libraries/encoders/SwapEncoder.sol +++ b/test/libraries/encoders/SwapEncoder.sol @@ -124,8 +124,8 @@ library SwapEncoder { function toSwapStep(State storage state, Swap memory swap) private returns (IVault.BatchSwapStep memory step) { TokenRegistry.State storage tokenRegistry = state.tokenRegistry.tokenRegistry(); step.poolId = swap.poolId; - step.assetInIndex = tokenRegistry.pushIfNotPresentIndexOf(swap.assetIn); - step.assetOutIndex = tokenRegistry.pushIfNotPresentIndexOf(swap.assetOut); + step.assetInIndex = TokenRegistry.tokenIndex(tokenRegistry, swap.assetIn); + step.assetOutIndex = TokenRegistry.tokenIndex(tokenRegistry, swap.assetOut); step.amount = swap.amount; step.userData = swap.userData; } diff --git a/test/libraries/encoders/TokenRegistry.sol b/test/libraries/encoders/TokenRegistry.sol index d0f710e5..1d6d29d8 100644 --- a/test/libraries/encoders/TokenRegistry.sol +++ b/test/libraries/encoders/TokenRegistry.sol @@ -104,4 +104,10 @@ library TokenRegistry { uint256 buyTokenIndex = pushIfNotPresentIndexOf(state, order.buyToken); return (sellTokenIndex - 1, buyTokenIndex - 1); } + + /// @dev Returns the token index for the specified token + function tokenIndex(State storage state, IERC20 token) internal hydrateArray(state) returns (uint256) { + uint256 index = pushIfNotPresentIndexOf(state, token); + return index - 1; + } } From 521c5517c81ebd569b39e651ecc1a2fb6b45a4e2 Mon Sep 17 00:00:00 2001 From: mfw78 <53399572+mfw78@users.noreply.github.com> Date: Wed, 9 Oct 2024 08:05:03 +0000 Subject: [PATCH 19/23] chore: clean dead code (#232) ## Description This PR removes extraneous TypeScript that has been left over as a result of the E2E tests. ## Test Plan 1. Confirm CI/CD green. ## Related Issues #130 --- package.json | 7 - src/ts/deploy.ts | 75 ----- src/ts/index.ts | 1 - src/ts/interaction.ts | 13 - src/ts/order.ts | 81 +----- src/ts/proxy.ts | 81 ------ src/ts/settlement.ts | 20 +- src/ts/sign.ts | 11 - src/ts/swap.ts | 11 +- test/encoding.ts | 57 ---- yarn.lock | 621 +++--------------------------------------- 11 files changed, 60 insertions(+), 918 deletions(-) delete mode 100644 src/ts/proxy.ts delete mode 100644 test/encoding.ts diff --git a/package.json b/package.json index cd7d1fe4..bff5f59f 100644 --- a/package.json +++ b/package.json @@ -36,15 +36,10 @@ "ethers": "^5.4.0" }, "devDependencies": { - "@gnosis.pm/safe-contracts": "^1.3.0", "@nomicfoundation/hardhat-verify": "^2.0.1", "@nomiclabs/hardhat-ethers": "^2.2.3", "@nomiclabs/hardhat-waffle": "^2.0.5", "@openzeppelin/contracts": "=3.4.0-solc-0.7", - "@safe-global/api-kit": "^1.3.0", - "@safe-global/protocol-kit": "^1.2.0", - "@safe-global/safe-core-sdk-types": "^2.2.0", - "@slack/web-api": "^6.9.0", "@tenderly/hardhat-tenderly": "~1.1.6", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", @@ -57,7 +52,6 @@ "@typescript-eslint/parser": "^5.58.0", "@uniswap/v2-core": "^1.0.1", "@uniswap/v2-periphery": "^1.1.0-beta.0", - "axios": "^1.3.5", "canonical-weth": "^1.4.0", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", @@ -71,7 +65,6 @@ "eslint-plugin-prettier": "^4.2.1", "ethereum-waffle": "^3.4.4", "ethers": "^5.7.2", - "globby": "^11.0.4", "hardhat": "^2.13.1", "hardhat-deploy": "^0.11.26", "hardhat-gas-reporter": "^1.0.9", diff --git a/src/ts/deploy.ts b/src/ts/deploy.ts index c67a13e9..5daa4e38 100644 --- a/src/ts/deploy.ts +++ b/src/ts/deploy.ts @@ -5,15 +5,6 @@ import { utils } from "ethers"; */ export const SALT = utils.formatBytes32String("Mattresses in Berlin!"); -/** - * The contract used to deploy contracts deterministically with CREATE2. - * The address is chosen by the hardhat-deploy library. - * It is the same in any EVM-based network. - * - * https://github.com/Arachnid/deterministic-deployment-proxy - */ -export const DEPLOYER_CONTRACT = "0x4e59b44847b379578588920ca78fbf26c0b4956c"; - /** * Dictionary containing all deployed contract names. */ @@ -21,69 +12,3 @@ export const CONTRACT_NAMES = { authenticator: "GPv2AllowListAuthentication", settlement: "GPv2Settlement", } as const; - -/** - * The name of a deployed contract. - */ -export type ContractName = (typeof CONTRACT_NAMES)[keyof typeof CONTRACT_NAMES]; - -/** - * The deployment args for a contract. - */ -export type DeploymentArguments = - T extends typeof CONTRACT_NAMES.authenticator - ? never - : T extends typeof CONTRACT_NAMES.settlement - ? [string, string] - : unknown[]; - -/** - * Allowed ABI definition types by Ethers.js. - */ -export type Abi = ConstructorParameters[0]; - -/** - * Artifact information important for computing deterministic deployments. - */ -export interface ArtifactDeployment { - abi: Abi; - bytecode: string; -} - -/** - * An artifact with a contract name matching one of the deterministically - * deployed contracts. - */ -export interface NamedArtifactDeployment - extends ArtifactDeployment { - contractName: C; -} - -type MaybeNamedArtifactArtifactDeployment = C extends ContractName - ? NamedArtifactDeployment - : ArtifactDeployment; - -/** - * Computes the deterministic address at which the contract will be deployed. - * This address does not depend on which network the contract is deployed to. - * - * @param contractName Name of the contract for which to find the address. - * @param deploymentArguments Extra arguments that are necessary to deploy. - * @returns The address that is expected to store the deployed code. - */ -export function deterministicDeploymentAddress( - { abi, bytecode }: MaybeNamedArtifactArtifactDeployment, - deploymentArguments: DeploymentArguments, -): string { - const contractInterface = new utils.Interface(abi); - const deployData = utils.hexConcat([ - bytecode, - contractInterface.encodeDeploy(deploymentArguments), - ]); - - return utils.getCreate2Address( - DEPLOYER_CONTRACT, - SALT, - utils.keccak256(deployData), - ); -} diff --git a/src/ts/index.ts b/src/ts/index.ts index 7be9e239..1403e450 100644 --- a/src/ts/index.ts +++ b/src/ts/index.ts @@ -22,7 +22,6 @@ export function domain( export * from "./deploy"; export * from "./interaction"; export * from "./order"; -export * from "./proxy"; export * from "./settlement"; export * from "./sign"; export * from "./swap"; diff --git a/src/ts/interaction.ts b/src/ts/interaction.ts index d352c5b4..25ad8f28 100644 --- a/src/ts/interaction.ts +++ b/src/ts/interaction.ts @@ -36,16 +36,3 @@ export function normalizeInteraction( ...interaction, }; } - -/** - * Normalizes data for many interactions so that they can be ABI encoded. This - * calls [`normalizeInteraction`] for each interaction. - * - * @param interactions The interactions to normalize. - * @return The normalized interactions. - */ -export function normalizeInteractions( - interactions: InteractionLike[], -): Interaction[] { - return interactions.map(normalizeInteraction); -} diff --git a/src/ts/order.ts b/src/ts/order.ts index f66d665d..ad6912d9 100644 --- a/src/ts/order.ts +++ b/src/ts/order.ts @@ -72,25 +72,6 @@ export interface Order { buyTokenBalance?: OrderBalance; } -/** - * Gnosis Protocol v2 order cancellation data. - */ -export interface OrderCancellations { - /** - * The unique identifier of the order to be cancelled. - */ - orderUids: BytesLike[]; -} - -/** - * Marker address to indicate that an order is buying Ether. - * - * Note that this address is only has special meaning in the `buyToken` and will - * be treated as a ERC20 token address in the `sellToken` position, causing the - * settlement to revert. - */ -export const BUY_ETH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; - /** * Gnosis Protocol v2 order flags. */ @@ -102,12 +83,12 @@ export type OrderFlags = Pick< /** * A timestamp value. */ -export type Timestamp = number | Date; +type Timestamp = number | Date; /** * A hash-like app data value. */ -export type HashLike = BytesLike | number; +type HashLike = BytesLike | number; /** * Order kind. @@ -168,7 +149,7 @@ export const ORDER_TYPE_FIELDS = [ * @param time The timestamp value to normalize. * @return Unix timestamp or number of seconds since the Unix Epoch. */ -export function timestamp(t: Timestamp): number { +function timestamp(t: Timestamp): number { return typeof t === "number" ? t : ~~(t.getTime() / 1000); } @@ -177,7 +158,7 @@ export function timestamp(t: Timestamp): number { * @param hashLike A hash-like value to normalize. * @returns A 32-byte hash encoded as a hex-string. */ -export function hashify(h: HashLike): string { +function hashify(h: HashLike): string { return typeof h === "number" ? `0x${h.toString(16).padStart(64, "0")}` : ethers.utils.hexZeroPad(h, 32); @@ -258,21 +239,6 @@ export function hashTypedData( return ethers.utils._TypedDataEncoder.hash(domain, types, data); } -/** - * Compute the 32-byte signing hash for the specified order. - * - * @param domain The EIP-712 domain separator to compute the hash for. - * @param order The order to compute the digest for. - * @return Hex-encoded 32-byte order digest. - */ -export function hashOrder(domain: TypedDataDomain, order: Order): string { - return hashTypedData( - domain, - { Order: ORDER_TYPE_FIELDS }, - normalizeOrder(order), - ); -} - /** * The byte length of an order UID. */ @@ -281,7 +247,7 @@ export const ORDER_UID_LENGTH = 56; /** * Order unique identifier parameters. */ -export interface OrderUidParams { +interface OrderUidParams { /** * The EIP-712 order struct hash. */ @@ -296,21 +262,6 @@ export interface OrderUidParams { validTo: number | Date; } -/** - * Computes the order UID for an order and the given owner. - */ -export function computeOrderUid( - domain: TypedDataDomain, - order: Order, - owner: string, -): string { - return packOrderUidParams({ - orderDigest: hashOrder(domain, order), - owner, - validTo: order.validTo, - }); -} - /** * Compute the unique identifier describing a user order in the settlement * contract. @@ -329,25 +280,3 @@ export function packOrderUidParams({ [orderDigest, owner, timestamp(validTo)], ); } - -/** - * Extracts the order unique identifier parameters from the specified bytes. - * - * @param orderUid The order UID encoded as a hexadecimal string. - * @returns The extracted order UID parameters. - */ -export function extractOrderUidParams(orderUid: string): OrderUidParams { - const bytes = ethers.utils.arrayify(orderUid); - if (bytes.length != ORDER_UID_LENGTH) { - throw new Error("invalid order UID length"); - } - - const view = new DataView(bytes.buffer); - return { - orderDigest: ethers.utils.hexlify(bytes.subarray(0, 32)), - owner: ethers.utils.getAddress( - ethers.utils.hexlify(bytes.subarray(32, 52)), - ), - validTo: view.getUint32(52), - }; -} diff --git a/src/ts/proxy.ts b/src/ts/proxy.ts deleted file mode 100644 index 55c1875e..00000000 --- a/src/ts/proxy.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { BigNumber, BytesLike, Contract, ethers } from "ethers"; - -/** - * Compute an EIP-1967 slot for the specified name. The proxy contract used by - * `hardhat-deploy` implements EIP-1967 (Standard Proxy Storage Slot). - * - * . - */ -function slot(name: string): BytesLike { - return ethers.utils.defaultAbiCoder.encode( - ["bytes32"], - [BigNumber.from(ethers.utils.id(name)).sub(1)], - ); -} - -const IMPLEMENTATION_STORAGE_SLOT = slot("eip1967.proxy.implementation"); -const OWNER_STORAGE_SLOT = slot("eip1967.proxy.admin"); - -/** - * Returns the address of the implementation of an EIP-1967-compatible proxy - * from its address. - * - * @param proxy Address of the proxy contract. - * @returns The address of the contract storing the proxy implementation. - */ -export async function implementationAddress( - provider: ethers.providers.Provider, - proxy: string, -): Promise { - const [implementation] = ethers.utils.defaultAbiCoder.decode( - ["address"], - await provider.getStorageAt(proxy, IMPLEMENTATION_STORAGE_SLOT), - ); - return implementation; -} - -/** - * Returns the address of the implementation of an EIP-1967-compatible proxy - * from its address. - * - * @param proxy Address of the proxy contract. - * @returns The address of the administrator of the proxy. - */ -export async function ownerAddress( - provider: ethers.providers.Provider, - proxy: string, -): Promise { - const [owner] = ethers.utils.defaultAbiCoder.decode( - ["address"], - await provider.getStorageAt(proxy, OWNER_STORAGE_SLOT), - ); - return owner; -} - -/** - * EIP-173 proxy ABI in "human-readable ABI" format. The proxy used by the - * deployment plugin implements this interface, and copying it here avoids - * pulling in `hardhat` as a dependency for just this ABI. - * - * - */ -export const EIP173_PROXY_ABI = [ - "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", - "function owner() view external returns(address)", - "function transferOwnership(address newOwner) external", - "function supportsInterface(bytes4 interfaceID) external view returns (bool)", -]; - -/** - * Returns the proxy interface for the specified address. - * - * @param contract The proxy contract to return a proxy interface for. - * @returns A Ethers.js contract instance for interacting with the proxy. - */ -export function proxyInterface(contract: Contract): Contract { - return new Contract( - contract.address, - EIP173_PROXY_ABI, - contract.signer ?? contract.provider, - ); -} diff --git a/src/ts/settlement.ts b/src/ts/settlement.ts index ffc06541..65249b95 100644 --- a/src/ts/settlement.ts +++ b/src/ts/settlement.ts @@ -30,7 +30,7 @@ import { TypedDataDomain } from "./types/ethers"; /** * The stage an interaction should be executed in. */ -export enum InteractionStage { +enum InteractionStage { /** * A pre-settlement intraction. * @@ -100,7 +100,7 @@ export type Trade = TradeExecution & /** * Details representing how an order was executed. */ -export interface TradeExecution { +interface TradeExecution { /** * The executed trade amount. * @@ -123,7 +123,7 @@ export interface TradeExecution { * increased again in the future. However, order refunds should not be used in * an actual settlement. */ -export interface OrderRefunds { +interface OrderRefunds { /** Refund storage used for order filled amount */ filledAmounts: BytesLike[]; /** Refund storage used for order pre-signature */ @@ -133,12 +133,12 @@ export interface OrderRefunds { /** * Table mapping token addresses to their respective clearing prices. */ -export type Prices = Record; +type Prices = Record; /** * Encoded settlement parameters. */ -export type EncodedSettlement = [ +type EncodedSettlement = [ /** Tokens. */ string[], /** Clearing prices. */ @@ -233,7 +233,7 @@ function decodeFlag( * @param scheme The signing scheme to encode. * @return The bitfield result. */ -export function encodeSigningScheme(scheme: SigningScheme): number { +function encodeSigningScheme(scheme: SigningScheme): number { return encodeFlag("signingScheme", scheme); } @@ -243,7 +243,7 @@ export function encodeSigningScheme(scheme: SigningScheme): number { * @param flag The encoded order flag. * @return The decoded signing scheme. */ -export function decodeSigningScheme(flags: BigNumberish): SigningScheme { +function decodeSigningScheme(flags: BigNumberish): SigningScheme { return decodeFlag("signingScheme", flags); } @@ -253,7 +253,7 @@ export function decodeSigningScheme(flags: BigNumberish): SigningScheme { * @param flags The order flags to encode. * @return The bitfield result. */ -export function encodeOrderFlags(flags: OrderFlags): number { +function encodeOrderFlags(flags: OrderFlags): number { return ( encodeFlag("kind", flags.kind) | encodeFlag("partiallyFillable", flags.partiallyFillable) | @@ -274,7 +274,7 @@ export function encodeOrderFlags(flags: OrderFlags): number { * @param flags The order flags encoded as a bitfield. * @return The decoded order flags. */ -export function decodeOrderFlags(flags: BigNumberish): OrderFlags { +function decodeOrderFlags(flags: BigNumberish): OrderFlags { return { kind: decodeFlag("kind", flags), partiallyFillable: decodeFlag("partiallyFillable", flags), @@ -306,7 +306,7 @@ export function decodeTradeFlags(flags: BigNumberish): TradeFlags { }; } -export function encodeSignatureData(sig: Signature): string { +function encodeSignatureData(sig: Signature): string { switch (sig.scheme) { case SigningScheme.EIP712: case SigningScheme.ETHSIGN: diff --git a/src/ts/sign.ts b/src/ts/sign.ts index a41e04f2..828a64ab 100644 --- a/src/ts/sign.ts +++ b/src/ts/sign.ts @@ -13,17 +13,6 @@ import { TypedDataDomain, } from "./types/ethers"; -/** - * Value returned by a call to `isValidSignature` if the signature was verified - * successfully. The value is defined in the EIP-1271 standard as: - * bytes4(keccak256("isValidSignature(bytes32,bytes)")) - */ -export const EIP1271_MAGICVALUE = ethers.utils.hexDataSlice( - ethers.utils.id("isValidSignature(bytes32,bytes)"), - 0, - 4, -); - /** * The signing scheme used to sign the order. */ diff --git a/src/ts/swap.ts b/src/ts/swap.ts index 98460846..def0f9be 100644 --- a/src/ts/swap.ts +++ b/src/ts/swap.ts @@ -39,7 +39,7 @@ export interface Swap { * An encoded Balancer swap request that can be used as input to the settlement * contract. */ -export interface BatchSwapStep { +interface BatchSwapStep { /** * The ID of the pool for the swap. */ @@ -68,7 +68,7 @@ export interface BatchSwapStep { /** * Swap execution parameters. */ -export interface SwapExecution { +interface SwapExecution { /** * The limit amount for the swap. * @@ -81,7 +81,7 @@ export interface SwapExecution { /** * Encoded swap parameters. */ -export type EncodedSwap = [ +type EncodedSwap = [ /** Swap requests. */ BatchSwapStep[], /** Tokens. */ @@ -94,10 +94,7 @@ export type EncodedSwap = [ * Encodes a swap as a {@link BatchSwapStep} to be used with the settlement * contract. */ -export function encodeSwapStep( - tokens: TokenRegistry, - swap: Swap, -): BatchSwapStep { +function encodeSwapStep(tokens: TokenRegistry, swap: Swap): BatchSwapStep { return { poolId: swap.poolId, assetInIndex: tokens.index(swap.assetIn), diff --git a/test/encoding.ts b/test/encoding.ts deleted file mode 100644 index 4e2a0b4f..00000000 --- a/test/encoding.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { BigNumber } from "ethers"; -import { ethers } from "hardhat"; - -import { Order, OrderBalance, OrderKind } from "../src/ts"; - -type AbiOrder = [ - string, - string, - string, - BigNumber, - BigNumber, - number, - string, - BigNumber, - string, - boolean, - string, - string, -]; - -function decodeEnum(hash: string, values: T[]): T { - for (const value of values) { - if (hash == ethers.utils.id(`${value}`)) { - return value; - } - } - throw new Error(`invalid enum hash '${hash}'`); -} - -export function decodeOrderKind(kindHash: string): OrderKind { - return decodeEnum(kindHash, [OrderKind.SELL, OrderKind.BUY]); -} - -export function decodeOrderBalance(balanceHash: string): OrderBalance { - return decodeEnum(balanceHash, [ - OrderBalance.ERC20, - OrderBalance.EXTERNAL, - OrderBalance.INTERNAL, - ]); -} - -export function decodeOrder(order: AbiOrder): Order { - return { - sellToken: order[0], - buyToken: order[1], - receiver: order[2], - sellAmount: order[3], - buyAmount: order[4], - validTo: order[5], - appData: order[6], - feeAmount: order[7], - kind: decodeOrderKind(order[8]), - partiallyFillable: order[9], - sellTokenBalance: decodeOrderBalance(order[10]), - buyTokenBalance: decodeOrderBalance(order[11]), - }; -} diff --git a/yarn.lock b/yarn.lock index affcaeda..bba4b019 100644 --- a/yarn.lock +++ b/yarn.lock @@ -167,30 +167,6 @@ patch-package "^6.2.2" postinstall-postinstall "^2.1.0" -"@ethereumjs/common@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.5.0.tgz#ec61551b31bef7a69d1dc634d8932468866a4268" - integrity sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg== - dependencies: - crc-32 "^1.2.0" - ethereumjs-util "^7.1.1" - -"@ethereumjs/common@^2.5.0": - version "2.6.5" - resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" - integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA== - dependencies: - crc-32 "^1.2.0" - ethereumjs-util "^7.1.5" - -"@ethereumjs/tx@3.3.2": - version "3.3.2" - resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.2.tgz#348d4624bf248aaab6c44fec2ae67265efe3db00" - integrity sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog== - dependencies: - "@ethereumjs/common" "^2.5.0" - ethereumjs-util "^7.1.2" - "@ethersproject/abi@5.0.0-beta.153": version "5.0.0-beta.153" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" @@ -206,7 +182,7 @@ "@ethersproject/properties" ">=5.0.0-beta.131" "@ethersproject/strings" ">=5.0.0-beta.130" -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -496,7 +472,7 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.6.2", "@ethersproject/transactions@^5.7.0": +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== @@ -563,11 +539,6 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@gnosis.pm/safe-contracts@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-contracts/-/safe-contracts-1.3.0.tgz#316741a7690d8751a1f701538cfc9ec80866eedc" - integrity sha512-1p+1HwGvxGUVzVkFjNzglwHrLNA67U/axP0Ct85FzzH8yhGJb4t9jDjPYocVMzLorDoWAfKicGy1akPY9jXRVw== - "@humanwhocodes/config-array@^0.11.8": version "0.11.8" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" @@ -588,14 +559,14 @@ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== "@jridgewell/resolve-uri@^3.0.3": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" - integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" @@ -956,48 +927,6 @@ path-browserify "^1.0.0" url "^0.11.0" -"@safe-global/api-kit@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@safe-global/api-kit/-/api-kit-1.3.0.tgz#3606aa52a3d5d0d46817fafc38572be7be1eaf31" - integrity sha512-3fdvoBtgufzmqmoBHir7vbS5N2t9Yc4kTeIJmAgmAGl8rHAy3z1bSv5uoEHYSMow34q1Am1aUar6vVAwwkIXhg== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@safe-global/safe-core-sdk-types" "^2.2.0" - node-fetch "^2.6.6" - -"@safe-global/protocol-kit@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@safe-global/protocol-kit/-/protocol-kit-1.2.0.tgz#5501fa0e2a1b4ad03cd5a4ee12bb9e799e1dd5a7" - integrity sha512-drU2uK30AZ4tqI/9ER7PGMD/lZp/5B9T02t+noTk7WF9Xb7HxskJd8GNU01KE55oyH31Y0AfXaE68H/f9lYa4A== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/solidity" "^5.7.0" - "@safe-global/safe-deployments" "^1.26.0" - ethereumjs-util "^7.1.5" - semver "^7.5.4" - web3 "^1.8.1" - web3-core "^1.8.1" - web3-utils "^1.8.1" - -"@safe-global/safe-core-sdk-types@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk-types/-/safe-core-sdk-types-2.2.0.tgz#2e34a5089035719e9a92a0bc6aa181c2edb0f108" - integrity sha512-vVG9qQnUYx+Xwsbuqraq25MPJX1I1aV1P81ZnHZa1lEMU7stqYWAmykUm/mvqsm8+AsvEB/wBKlFjbFJ/duzoA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/contracts" "^5.7.0" - "@safe-global/safe-deployments" "^1.26.0" - web3-core "^1.8.1" - web3-utils "^1.8.1" - -"@safe-global/safe-deployments@^1.26.0": - version "1.26.0" - resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.26.0.tgz#b83615b3b5a66e736e08f8ecf2801ed988e9e007" - integrity sha512-Tw89O4/paT19ieMoiWQbqRApb0Bef/DxweS9rxodXAM5EQModkbyFXGZca+YxXE67sLvWjLr2jJUOxwze8mhGw== - dependencies: - semver "^7.3.7" - "@scure/base@~1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" @@ -1093,7 +1022,7 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.6.0": +"@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== @@ -1138,35 +1067,6 @@ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== -"@slack/logger@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@slack/logger/-/logger-3.0.0.tgz#b736d4e1c112c22a10ffab0c2d364620aedcb714" - integrity sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA== - dependencies: - "@types/node" ">=12.0.0" - -"@slack/types@^2.8.0": - version "2.8.0" - resolved "https://registry.yarnpkg.com/@slack/types/-/types-2.8.0.tgz#11ea10872262a7e6f86f54e5bcd4f91e3a41fe91" - integrity sha512-ghdfZSF0b4NC9ckBA8QnQgC9DJw2ZceDq0BIjjRSv6XAZBXJdWgxIsYz0TYnWSiqsKZGH2ZXbj9jYABZdH3OSQ== - -"@slack/web-api@^6.9.0": - version "6.9.0" - resolved "https://registry.yarnpkg.com/@slack/web-api/-/web-api-6.9.0.tgz#d829dcfef490dbce8e338912706b6f39dcde3ad2" - integrity sha512-RME5/F+jvQmZHkoP+ogrDbixq1Ms1mBmylzuWq4sf3f7GCpMPWoiZ+WqWk+sism3vrlveKWIgO9R4Qg9fiRyoQ== - dependencies: - "@slack/logger" "^3.0.0" - "@slack/types" "^2.8.0" - "@types/is-stream" "^1.1.0" - "@types/node" ">=12.0.0" - axios "^0.27.2" - eventemitter3 "^3.1.0" - form-data "^2.5.0" - is-electron "2.2.2" - is-stream "^1.1.0" - p-queue "^6.6.1" - p-retry "^4.0.0" - "@solidity-parser/parser@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.0.tgz#d51f074efb0acce0e953ec48133561ed710cebc0" @@ -1228,24 +1128,24 @@ js-yaml "^3.14.0" "@tsconfig/node10@^1.0.7": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" - integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== "@tsconfig/node12@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" - integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" - integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" - integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@typechain/ethers-v5@^2.0.0": version "2.0.0" @@ -1261,14 +1161,14 @@ dependencies: "@types/node" "*" -"@types/bn.js@^5.1.0", "@types/bn.js@^5.1.1": +"@types/bn.js@^5.1.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== dependencies: "@types/node" "*" -"@types/cacheable-request@^6.0.1", "@types/cacheable-request@^6.0.2": +"@types/cacheable-request@^6.0.1": version "6.0.3" resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== @@ -1334,13 +1234,6 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== -"@types/is-stream@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@types/is-stream/-/is-stream-1.1.0.tgz#b84d7bb207a210f2af9bed431dc0fbe9c4143be1" - integrity sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg== - dependencies: - "@types/node" "*" - "@types/json-schema@^7.0.9": version "7.0.9" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" @@ -1398,11 +1291,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== -"@types/node@>=12.0.0": - version "20.5.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.3.tgz#fa52c147f405d56b2f1dd8780d840aa87ddff629" - integrity sha512-ITI7rbWczR8a/S6qjAW7DMqxqFMjjTo61qZVWJ1ubPvbIQsL5D/TvwjYEalM8Kthpe3hTzOGrF2TGbAu2uyqeA== - "@types/node@^10.0.3": version "10.17.56" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.56.tgz#010c9e047c3ff09ddcd11cbb6cf5912725cdc2b3" @@ -1462,11 +1350,6 @@ dependencies: "@types/node" "*" -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== - "@types/secp256k1@^4.0.1": version "4.0.3" resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" @@ -1632,11 +1515,6 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -abortcontroller-polyfill@^1.7.3: - version "1.7.5" - resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed" - integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ== - abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" @@ -1692,14 +1570,16 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" -acorn@^8.4.1: - version "8.5.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" - integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== +acorn@^8.11.0, acorn@^8.4.1: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== acorn@^8.8.0: version "8.8.2" @@ -2079,23 +1959,6 @@ axios@^0.21.1: dependencies: follow-redirects "^1.14.0" -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== - dependencies: - follow-redirects "^1.14.9" - form-data "^4.0.0" - -axios@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.5.tgz#e07209b39a0d11848e3e341fa087acd71dadc542" - integrity sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw== - dependencies: - follow-redirects "^1.15.0" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -2991,11 +2854,6 @@ cacheable-lookup@^5.0.3: resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== -cacheable-lookup@^6.0.4: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.1.0.tgz#0330a543471c61faa4e9035db583aad753b36385" - integrity sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww== - cacheable-lookup@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" @@ -3578,13 +3436,6 @@ cross-fetch@^2.1.0, cross-fetch@^2.1.1: node-fetch "^2.6.7" whatwg-fetch "^2.0.4" -cross-fetch@^3.1.4: - version "3.1.8" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" - integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== - dependencies: - node-fetch "^2.6.12" - cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -4158,11 +4009,6 @@ es6-iterator@^2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-promise@^4.2.8: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - es6-symbol@^3.1.1, es6-symbol@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" @@ -4729,7 +4575,7 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereum rlp "^2.0.0" safe-buffer "^5.1.1" -ethereumjs-util@^7.0.2, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.2, ethereumjs-util@^7.1.5: +ethereumjs-util@^7.0.2, ethereumjs-util@^7.1.0: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== @@ -4870,16 +4716,6 @@ eventemitter3@4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== -eventemitter3@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== - -eventemitter3@^4.0.4: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - events@^3.0.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -5016,7 +4852,7 @@ fast-diff@^1.1.2, fast-diff@^1.2.0: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.0.3, fast-glob@^3.1.1: +fast-glob@^3.0.3: version "3.2.5" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== @@ -5195,11 +5031,6 @@ follow-redirects@^1.12.1, follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== -follow-redirects@^1.14.9, follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -5217,17 +5048,12 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== -form-data-encoder@1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96" - integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== - form-data-encoder@^2.1.2: version "2.1.4" resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== -form-data@^2.2.0, form-data@^2.5.0: +form-data@^2.2.0: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== @@ -5662,18 +5488,6 @@ globby@^10.0.1: merge2 "^1.2.3" slash "^3.0.0" -globby@^11.0.4: - version "11.0.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" - integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" - globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -5693,25 +5507,6 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -got@12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-12.1.0.tgz#099f3815305c682be4fd6b0ee0726d8e4c6b0af4" - integrity sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig== - dependencies: - "@sindresorhus/is" "^4.6.0" - "@szmarczak/http-timer" "^5.0.1" - "@types/cacheable-request" "^6.0.2" - "@types/responselike" "^1.0.0" - cacheable-lookup "^6.0.4" - cacheable-request "^7.0.2" - decompress-response "^6.0.0" - form-data-encoder "1.7.1" - get-stream "^6.0.1" - http2-wrapper "^2.1.10" - lowercase-keys "^3.0.0" - p-cancelable "^3.0.0" - responselike "^2.0.0" - got@9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -6172,7 +5967,7 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.1.1, ignore@^5.1.4, ignore@^5.2.0: +ignore@^5.1.1, ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== @@ -6421,11 +6216,6 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-electron@2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.2.tgz#3778902a2044d76de98036f5dc58089ac4d80bb9" - integrity sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg== - is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -6475,13 +6265,6 @@ is-function@^1.0.1: resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -6555,7 +6338,7 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" -is-stream@^1.0.1, is-stream@^1.1.0: +is-stream@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== @@ -6585,13 +6368,6 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -is-typed-array@^1.1.3: - version "1.1.12" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" - integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== - dependencies: - which-typed-array "^1.1.11" - is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -7865,13 +7641,6 @@ node-fetch@^2.6.1, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@^2.6.12, node-fetch@^2.6.6: - version "2.6.12" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" - integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== - dependencies: - whatwg-url "^5.0.0" - node-fetch@~1.7.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" @@ -8061,13 +7830,6 @@ oboe@2.1.4: dependencies: http-https "^1.0.0" -oboe@2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" - integrity sha512-zRFWiF+FoicxEs3jNI/WYUrVEgA7DeET/InK0XQuudGHRg8iIob3cNPrJTKaz4004uaA9Pbe+Dwa8iluhjLZWA== - dependencies: - http-https "^1.0.0" - on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -8146,11 +7908,6 @@ p-cancelable@^3.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== - p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -8200,29 +7957,6 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -p-queue@^6.6.1: - version "6.6.2" - resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" - integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== - dependencies: - eventemitter3 "^4.0.4" - p-timeout "^3.2.0" - -p-retry@^4.0.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== - dependencies: - "@types/retry" "0.12.0" - retry "^0.13.1" - -p-timeout@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" - integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== - dependencies: - p-finally "^1.0.0" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -8561,11 +8295,6 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -9112,11 +8841,6 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -9293,13 +9017,6 @@ semver@^7.5.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - semver@~5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -10215,9 +9932,9 @@ ts-generator@^0.1.1: ts-essentials "^1.0.0" ts-node@^10.9.1: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" @@ -10540,17 +10257,6 @@ util.promisify@^1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.1" -util@^0.12.5: - version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - which-typed-array "^1.1.2" - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -10576,11 +10282,6 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -10613,15 +10314,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -web3-bzz@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.10.0.tgz#ac74bc71cdf294c7080a79091079192f05c5baed" - integrity sha512-o9IR59io3pDUsXTsps5pO5hW1D5zBmg46iNc2t4j2DkaYHNdDLwk2IP9ukoM2wg47QILfPEJYzhTfkS/CcX0KA== - dependencies: - "@types/node" "^12.12.6" - got "12.1.0" - swarm-js "^0.1.40" - web3-bzz@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.2.11.tgz#41bc19a77444bd5365744596d778b811880f707f" @@ -10632,14 +10324,6 @@ web3-bzz@1.2.11: swarm-js "^0.1.40" underscore "1.9.1" -web3-core-helpers@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.10.0.tgz#1016534c51a5df77ed4f94d1fcce31de4af37fad" - integrity sha512-pIxAzFDS5vnbXvfvLSpaA1tfRykAe9adw43YCKsEYQwH0gCLL0kMLkaCX3q+Q8EVmAh+e1jWL/nl9U0de1+++g== - dependencies: - web3-eth-iban "1.10.0" - web3-utils "1.10.0" - web3-core-helpers@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.11.tgz#84c681ed0b942c0203f3b324a245a127e8c67a99" @@ -10649,17 +10333,6 @@ web3-core-helpers@1.2.11: web3-eth-iban "1.2.11" web3-utils "1.2.11" -web3-core-method@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.10.0.tgz#82668197fa086e8cc8066742e35a9d72535e3412" - integrity sha512-4R700jTLAMKDMhQ+nsVfIXvH6IGJlJzGisIfMKWAIswH31h5AZz7uDUW2YctI+HrYd+5uOAlS4OJeeT9bIpvkA== - dependencies: - "@ethersproject/transactions" "^5.6.2" - web3-core-helpers "1.10.0" - web3-core-promievent "1.10.0" - web3-core-subscriptions "1.10.0" - web3-utils "1.10.0" - web3-core-method@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.11.tgz#f880137d1507a0124912bf052534f168b8d8fbb6" @@ -10672,13 +10345,6 @@ web3-core-method@1.2.11: web3-core-subscriptions "1.2.11" web3-utils "1.2.11" -web3-core-promievent@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.10.0.tgz#cbb5b3a76b888df45ed3a8d4d8d4f54ccb66a37b" - integrity sha512-68N7k5LWL5R38xRaKFrTFT2pm2jBNFaM4GioS00YjAKXRQ3KjmhijOMG3TICz6Aa5+6GDWYelDNx21YAeZ4YTg== - dependencies: - eventemitter3 "4.0.4" - web3-core-promievent@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.11.tgz#51fe97ca0ddec2f99bf8c3306a7a8e4b094ea3cf" @@ -10686,17 +10352,6 @@ web3-core-promievent@1.2.11: dependencies: eventemitter3 "4.0.4" -web3-core-requestmanager@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.10.0.tgz#4b34f6e05837e67c70ff6f6993652afc0d54c340" - integrity sha512-3z/JKE++Os62APml4dvBM+GAuId4h3L9ckUrj7ebEtS2AR0ixyQPbrBodgL91Sv7j7cQ3Y+hllaluqjguxvSaQ== - dependencies: - util "^0.12.5" - web3-core-helpers "1.10.0" - web3-providers-http "1.10.0" - web3-providers-ipc "1.10.0" - web3-providers-ws "1.10.0" - web3-core-requestmanager@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.2.11.tgz#fe6eb603fbaee18530293a91f8cf26d8ae28c45a" @@ -10708,14 +10363,6 @@ web3-core-requestmanager@1.2.11: web3-providers-ipc "1.2.11" web3-providers-ws "1.2.11" -web3-core-subscriptions@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.10.0.tgz#b534592ee1611788fc0cb0b95963b9b9b6eacb7c" - integrity sha512-HGm1PbDqsxejI075gxBc5OSkwymilRWZufIy9zEpnWKNmfbuv5FfHgW1/chtJP6aP3Uq2vHkvTDl3smQBb8l+g== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.10.0" - web3-core-subscriptions@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.11.tgz#beca908fbfcb050c16f45f3f0f4c205e8505accd" @@ -10725,19 +10372,6 @@ web3-core-subscriptions@1.2.11: underscore "1.9.1" web3-core-helpers "1.2.11" -web3-core@1.10.0, web3-core@^1.8.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.0.tgz#9aa07c5deb478cf356c5d3b5b35afafa5fa8e633" - integrity sha512-fWySwqy2hn3TL89w5TM8wXF1Z2Q6frQTKHWmP0ppRQorEK8NcHJRfeMiv/mQlSKoTS1F6n/nv2uyZsixFycjYQ== - dependencies: - "@types/bn.js" "^5.1.1" - "@types/node" "^12.12.6" - bignumber.js "^9.0.0" - web3-core-helpers "1.10.0" - web3-core-method "1.10.0" - web3-core-requestmanager "1.10.0" - web3-utils "1.10.0" - web3-core@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.11.tgz#1043cacc1becb80638453cc5b2a14be9050288a7" @@ -10751,14 +10385,6 @@ web3-core@1.2.11: web3-core-requestmanager "1.2.11" web3-utils "1.2.11" -web3-eth-abi@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.10.0.tgz#53a7a2c95a571e205e27fd9e664df4919483cce1" - integrity sha512-cwS+qRBWpJ43aI9L3JS88QYPfFcSJJ3XapxOQ4j40v6mk7ATpA8CVK1vGTzpihNlOfMVRBkR95oAj7oL6aiDOg== - dependencies: - "@ethersproject/abi" "^5.6.3" - web3-utils "1.10.0" - web3-eth-abi@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.11.tgz#a887494e5d447c2926d557a3834edd66e17af9b0" @@ -10768,22 +10394,6 @@ web3-eth-abi@1.2.11: underscore "1.9.1" web3-utils "1.2.11" -web3-eth-accounts@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.0.tgz#2942beca0a4291455f32cf09de10457a19a48117" - integrity sha512-wiq39Uc3mOI8rw24wE2n15hboLE0E9BsQLdlmsL4Zua9diDS6B5abXG0XhFcoNsXIGMWXVZz4TOq3u4EdpXF/Q== - dependencies: - "@ethereumjs/common" "2.5.0" - "@ethereumjs/tx" "3.3.2" - eth-lib "0.2.8" - ethereumjs-util "^7.1.5" - scrypt-js "^3.0.1" - uuid "^9.0.0" - web3-core "1.10.0" - web3-core-helpers "1.10.0" - web3-core-method "1.10.0" - web3-utils "1.10.0" - web3-eth-accounts@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.11.tgz#a9e3044da442d31903a7ce035a86d8fa33f90520" @@ -10801,20 +10411,6 @@ web3-eth-accounts@1.2.11: web3-core-method "1.2.11" web3-utils "1.2.11" -web3-eth-contract@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.10.0.tgz#8e68c7654576773ec3c91903f08e49d0242c503a" - integrity sha512-MIC5FOzP/+2evDksQQ/dpcXhSqa/2hFNytdl/x61IeWxhh6vlFeSjq0YVTAyIzdjwnL7nEmZpjfI6y6/Ufhy7w== - dependencies: - "@types/bn.js" "^5.1.1" - web3-core "1.10.0" - web3-core-helpers "1.10.0" - web3-core-method "1.10.0" - web3-core-promievent "1.10.0" - web3-core-subscriptions "1.10.0" - web3-eth-abi "1.10.0" - web3-utils "1.10.0" - web3-eth-contract@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.11.tgz#917065902bc27ce89da9a1da26e62ef663663b90" @@ -10830,20 +10426,6 @@ web3-eth-contract@1.2.11: web3-eth-abi "1.2.11" web3-utils "1.2.11" -web3-eth-ens@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.10.0.tgz#96a676524e0b580c87913f557a13ed810cf91cd9" - integrity sha512-3hpGgzX3qjgxNAmqdrC2YUQMTfnZbs4GeLEmy8aCWziVwogbuqQZ+Gzdfrym45eOZodk+lmXyLuAdqkNlvkc1g== - dependencies: - content-hash "^2.5.2" - eth-ens-namehash "2.0.8" - web3-core "1.10.0" - web3-core-helpers "1.10.0" - web3-core-promievent "1.10.0" - web3-eth-abi "1.10.0" - web3-eth-contract "1.10.0" - web3-utils "1.10.0" - web3-eth-ens@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.11.tgz#26d4d7f16d6cbcfff918e39832b939edc3162532" @@ -10859,14 +10441,6 @@ web3-eth-ens@1.2.11: web3-eth-contract "1.2.11" web3-utils "1.2.11" -web3-eth-iban@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.10.0.tgz#5a46646401965b0f09a4f58e7248c8a8cd22538a" - integrity sha512-0l+SP3IGhInw7Q20LY3IVafYEuufo4Dn75jAHT7c2aDJsIolvf2Lc6ugHkBajlwUneGfbRQs/ccYPQ9JeMUbrg== - dependencies: - bn.js "^5.2.1" - web3-utils "1.10.0" - web3-eth-iban@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.11.tgz#f5f73298305bc7392e2f188bf38a7362b42144ef" @@ -10875,18 +10449,6 @@ web3-eth-iban@1.2.11: bn.js "^4.11.9" web3-utils "1.2.11" -web3-eth-personal@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.10.0.tgz#94d525f7a29050a0c2a12032df150ac5ea633071" - integrity sha512-anseKn98w/d703eWq52uNuZi7GhQeVjTC5/svrBWEKob0WZ5kPdo+EZoFN0sp5a5ubbrk/E0xSl1/M5yORMtpg== - dependencies: - "@types/node" "^12.12.6" - web3-core "1.10.0" - web3-core-helpers "1.10.0" - web3-core-method "1.10.0" - web3-net "1.10.0" - web3-utils "1.10.0" - web3-eth-personal@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.11.tgz#a38b3942a1d87a62070ce0622a941553c3d5aa70" @@ -10899,24 +10461,6 @@ web3-eth-personal@1.2.11: web3-net "1.2.11" web3-utils "1.2.11" -web3-eth@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.10.0.tgz#38b905e2759697c9624ab080cfcf4e6c60b3a6cf" - integrity sha512-Z5vT6slNMLPKuwRyKGbqeGYC87OAy8bOblaqRTgg94CXcn/mmqU7iPIlG4506YdcdK3x6cfEDG7B6w+jRxypKA== - dependencies: - web3-core "1.10.0" - web3-core-helpers "1.10.0" - web3-core-method "1.10.0" - web3-core-subscriptions "1.10.0" - web3-eth-abi "1.10.0" - web3-eth-accounts "1.10.0" - web3-eth-contract "1.10.0" - web3-eth-ens "1.10.0" - web3-eth-iban "1.10.0" - web3-eth-personal "1.10.0" - web3-net "1.10.0" - web3-utils "1.10.0" - web3-eth@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.11.tgz#4c81fcb6285b8caf544058fba3ae802968fdc793" @@ -10936,15 +10480,6 @@ web3-eth@1.2.11: web3-net "1.2.11" web3-utils "1.2.11" -web3-net@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.0.tgz#be53e7f5dafd55e7c9013d49c505448b92c9c97b" - integrity sha512-NLH/N3IshYWASpxk4/18Ge6n60GEvWBVeM8inx2dmZJVmRI6SJIlUxbL8jySgiTn3MMZlhbdvrGo8fpUW7a1GA== - dependencies: - web3-core "1.10.0" - web3-core-method "1.10.0" - web3-utils "1.10.0" - web3-net@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.11.tgz#eda68ef25e5cdb64c96c39085cdb74669aabbe1b" @@ -10980,16 +10515,6 @@ web3-provider-engine@14.2.1: xhr "^2.2.0" xtend "^4.0.1" -web3-providers-http@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.10.0.tgz#864fa48675e7918c9a4374e5f664b32c09d0151b" - integrity sha512-eNr965YB8a9mLiNrkjAWNAPXgmQWfpBfkkn7tpEFlghfww0u3I0tktMZiaToJVcL2+Xq+81cxbkpeWJ5XQDwOA== - dependencies: - abortcontroller-polyfill "^1.7.3" - cross-fetch "^3.1.4" - es6-promise "^4.2.8" - web3-core-helpers "1.10.0" - web3-providers-http@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.11.tgz#1cd03442c61670572d40e4dcdf1faff8bd91e7c6" @@ -10998,14 +10523,6 @@ web3-providers-http@1.2.11: web3-core-helpers "1.2.11" xhr2-cookies "1.1.0" -web3-providers-ipc@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.10.0.tgz#9747c7a6aee96a51488e32fa7c636c3460b39889" - integrity sha512-OfXG1aWN8L1OUqppshzq8YISkWrYHaATW9H8eh0p89TlWMc1KZOL9vttBuaBEi96D/n0eYDn2trzt22bqHWfXA== - dependencies: - oboe "2.1.5" - web3-core-helpers "1.10.0" - web3-providers-ipc@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.2.11.tgz#d16d6c9be1be6e0b4f4536c4acc16b0f4f27ef21" @@ -11015,15 +10532,6 @@ web3-providers-ipc@1.2.11: underscore "1.9.1" web3-core-helpers "1.2.11" -web3-providers-ws@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.10.0.tgz#cb0b87b94c4df965cdf486af3a8cd26daf3975e5" - integrity sha512-sK0fNcglW36yD5xjnjtSGBnEtf59cbw4vZzJ+CmOWIKGIR96mP5l684g0WD0Eo+f4NQc2anWWXG74lRc9OVMCQ== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.10.0" - websocket "^1.0.32" - web3-providers-ws@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.2.11.tgz#a1dfd6d9778d840561d9ec13dd453046451a96bb" @@ -11034,16 +10542,6 @@ web3-providers-ws@1.2.11: web3-core-helpers "1.2.11" websocket "^1.0.31" -web3-shh@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.10.0.tgz#c2979b87e0f67a7fef2ce9ee853bd7bfbe9b79a8" - integrity sha512-uNUUuNsO2AjX41GJARV9zJibs11eq6HtOe6Wr0FtRUcj8SN6nHeYIzwstAvJ4fXA53gRqFMTxdntHEt9aXVjpg== - dependencies: - web3-core "1.10.0" - web3-core-method "1.10.0" - web3-core-subscriptions "1.10.0" - web3-net "1.10.0" - web3-shh@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.11.tgz#f5d086f9621c9a47e98d438010385b5f059fd88f" @@ -11054,19 +10552,6 @@ web3-shh@1.2.11: web3-core-subscriptions "1.2.11" web3-net "1.2.11" -web3-utils@1.10.0, web3-utils@^1.8.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.0.tgz#ca4c1b431a765c14ac7f773e92e0fd9377ccf578" - integrity sha512-kSaCM0uMcZTNUSmn5vMEhlo02RObGNRRCkdX0V9UTAU0+lrvn0HSaudyCo6CQzuXUsnuY2ERJGCGPfeWmv19Rg== - dependencies: - bn.js "^5.2.1" - ethereum-bloom-filters "^1.0.6" - ethereumjs-util "^7.1.0" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - utf8 "3.0.0" - web3-utils@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.11.tgz#af1942aead3fb166ae851a985bed8ef2c2d95a82" @@ -11107,19 +10592,6 @@ web3@1.2.11: web3-shh "1.2.11" web3-utils "1.2.11" -web3@^1.8.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.10.0.tgz#2fde0009f59aa756c93e07ea2a7f3ab971091274" - integrity sha512-YfKY9wSkGcM8seO+daR89oVTcbu18NsVfvOngzqMYGUU0pPSQmE57qQDvQzUeoIOHAnXEBNzrhjQJmm8ER0rng== - dependencies: - web3-bzz "1.10.0" - web3-core "1.10.0" - web3-eth "1.10.0" - web3-eth-personal "1.10.0" - web3-net "1.10.0" - web3-shh "1.10.0" - web3-utils "1.10.0" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -11137,7 +10609,7 @@ websocket@1.0.32: utf-8-validate "^5.0.2" yaeti "^0.0.6" -websocket@^1.0.31, websocket@^1.0.32: +websocket@^1.0.31: version "1.0.34" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== @@ -11183,17 +10655,6 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-typed-array@^1.1.11, which-typed-array@^1.1.2: - version "1.1.11" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" - integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" From 92e387ac8fac785e0e57ade63b396be0081d99d5 Mon Sep 17 00:00:00 2001 From: Kaze Date: Sun, 28 Dec 2025 14:19:41 +0900 Subject: [PATCH 20/23] improvements for the e2e test update branch * remove balancer tests (we will be actively removing balancer support itself by the next release) * update to use latest openzepplin (actually supports 0.8) * remove the foundry warnings error (temp so that we can get things to compile) --- foundry.toml | 2 +- package.json | 4 +- test/e2e/BalancerSwap.t.sol | 539 ----------------------- test/e2e/Deployment.t.sol | 7 +- test/e2e/ERC20Mintable.sol | 2 +- test/e2e/Helper.sol | 45 +- test/e2e/InternalBalances.t.sol | 225 ---------- test/e2e/OffchainAllowances.t.sol | 106 +---- test/src/EIP173Proxy.sol | 2 +- test/src/ERC20PresetPermit.sol | 6 +- test/src/vendor/ChiToken.sol | 23 +- yarn.lock | 693 +++++++++++------------------- 12 files changed, 286 insertions(+), 1368 deletions(-) delete mode 100644 test/e2e/BalancerSwap.t.sol delete mode 100644 test/e2e/InternalBalances.t.sol diff --git a/foundry.toml b/foundry.toml index a0113718..522a1d95 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,7 +9,7 @@ optimizer = true optimizer_runs = 1000000 evm_version = "cancun" -deny_warnings = true +#deny_warnings = true fs_permissions = [ { access = "read", path = "./balancer"}, diff --git a/package.json b/package.json index bff5f59f..fdd83376 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@nomicfoundation/hardhat-verify": "^2.0.1", "@nomiclabs/hardhat-ethers": "^2.2.3", "@nomiclabs/hardhat-waffle": "^2.0.5", - "@openzeppelin/contracts": "=3.4.0-solc-0.7", + "@openzeppelin/contracts": "=5.5.0", "@tenderly/hardhat-tenderly": "~1.1.6", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", @@ -65,7 +65,7 @@ "eslint-plugin-prettier": "^4.2.1", "ethereum-waffle": "^3.4.4", "ethers": "^5.7.2", - "hardhat": "^2.13.1", + "hardhat": "^2.28.0", "hardhat-deploy": "^0.11.26", "hardhat-gas-reporter": "^1.0.9", "prettier": "^2.8.7", diff --git a/test/e2e/BalancerSwap.t.sol b/test/e2e/BalancerSwap.t.sol deleted file mode 100644 index c26dbda9..00000000 --- a/test/e2e/BalancerSwap.t.sol +++ /dev/null @@ -1,539 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -pragma solidity ^0.8; - -import {Vm} from "forge-std/Vm.sol"; - -import {IERC20} from "src/contracts/interfaces/IERC20.sol"; -import {IVault} from "src/contracts/interfaces/IVault.sol"; - -import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; -import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; - -import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; -import {SwapEncoder} from "../libraries/encoders/SwapEncoder.sol"; -import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; -import {Helper, IERC20Mintable} from "./Helper.sol"; - -using SettlementEncoder for SettlementEncoder.State; -using TokenRegistry for TokenRegistry.State; -using TokenRegistry for Registry; -using SwapEncoder for SwapEncoder.State; - -interface IMockPool { - function registerTokens(IERC20[] calldata tokens, address[] calldata assetManagers) external; - function getPoolId() external view returns (bytes32); - function setMultiplier(uint256) external; -} - -interface IBalancerVault is IVault { - struct JoinPoolRequest { - IERC20[] assets; - uint256[] maxAmountsIn; - bytes userData; - bool fromInternalBalance; - } - - function joinPool(bytes32 poolId, address sender, address recipient, JoinPoolRequest calldata request) external; - function setRelayerApproval(address, address, bool) external; - function getInternalBalance(address user, IERC20[] calldata tokens) external view returns (uint256[] memory); -} - -contract BalancerSwapTest is Helper(true) { - IERC20Mintable token1; - IERC20Mintable token2; - IERC20Mintable token3; - - mapping(address => mapping(address => address)) pools; - - function setUp() public override { - super.setUp(); - - token1 = deployMintableErc20("TOK1", "TOK1"); - token2 = deployMintableErc20("TOK2", "TOK2"); - token3 = deployMintableErc20("TOK3", "TOK3"); - - Vm.Wallet memory pooler = vm.createWallet("pooler"); - - IERC20Mintable[] memory tokens = new IERC20Mintable[](3); - tokens[0] = token1; - tokens[1] = token2; - tokens[2] = token3; - - uint256 lots = 10000 ether; - - for (uint256 i = 0; i < tokens.length; i++) { - (IERC20Mintable token0_, IERC20Mintable token1_) = (tokens[i], tokens[(i + 1) % tokens.length]); - (IERC20Mintable tokenA, IERC20Mintable tokenB) = - address(token0_) < address(token1_) ? (token0_, token1_) : (token1_, token0_); - - uint256 twoTokenSpecialization = 2; - - vm.startPrank(deployer); - IMockPool pool = IMockPool( - _create( - abi.encodePacked( - vm.getCode("balancer/test/MockPool.json"), abi.encode(address(vault), twoTokenSpecialization) - ), - 0 - ) - ); - IERC20[] memory tks = new IERC20[](2); - tks[0] = tokenA; - tks[1] = tokenB; - address[] memory assetManagers = new address[](2); - assetManagers[0] = address(0); - assetManagers[1] = address(0); - pool.registerTokens(tks, assetManagers); - vm.stopPrank(); - - for (uint256 j = 0; j < tks.length; j++) { - IERC20Mintable(address(tks[j])).mint(pooler.addr, lots); - vm.prank(pooler.addr); - tks[j].approve(address(vault), type(uint256).max); - } - - uint256[] memory maxAmountsIn = new uint256[](2); - maxAmountsIn[0] = lots; - maxAmountsIn[1] = lots; - uint256[] memory poolFees = new uint256[](2); - IBalancerVault.JoinPoolRequest memory request = IBalancerVault.JoinPoolRequest({ - assets: tks, - maxAmountsIn: maxAmountsIn, - // NOTE: The mock pool uses this for encoding the pool share amounts - // that a user (here `pooler`) gets when joining the pool (first value) - // as well as the pool fees (second value). - userData: abi.encode(maxAmountsIn, poolFees), - fromInternalBalance: false - }); - // poolerAddr declared as separate var to prevent stack too deep errors - address poolerAddr = pooler.addr; - bytes32 poolId = pool.getPoolId(); - vm.prank(poolerAddr); - IBalancerVault(address(vault)).joinPool(poolId, poolerAddr, poolerAddr, request); - - pools[address(tokenA)][address(tokenB)] = address(pool); - pools[address(tokenB)][address(tokenA)] = address(pool); - } - } - - function test_reverts_if_order_is_expired() external { - _mintAndApprove(trader, token1, 100.1 ether, GPv2Order.BALANCE_ERC20); - - swapEncoder.signEncodeTrade( - vm, - trader, - GPv2Order.Data({ - kind: GPv2Order.KIND_SELL, - partiallyFillable: false, - sellToken: token1, - buyToken: token2, - sellAmount: 100 ether, - buyAmount: 72 ether, - feeAmount: 0.1 ether, - validTo: uint32(block.timestamp) - 1, - appData: bytes32(uint256(1)), - sellTokenBalance: GPv2Order.BALANCE_ERC20, - buyTokenBalance: GPv2Order.BALANCE_ERC20, - receiver: GPv2Order.RECEIVER_SAME_AS_OWNER - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); - vm.prank(solver); - // SWAP_DEADLINE - vm.expectRevert("BAL#508"); - swap(encodedSwap); - } - - function test_allows_using_liquidity_from_multiple_pools() external { - _mintAndApprove(trader, token1, 100.1 ether, GPv2Order.BALANCE_ERC20); - - swapEncoder.signEncodeTrade( - vm, - trader, - GPv2Order.Data({ - kind: GPv2Order.KIND_SELL, - partiallyFillable: false, - sellToken: token1, - buyToken: token3, - sellAmount: 100 ether, - buyAmount: 125 ether, - feeAmount: 0.1 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - sellTokenBalance: GPv2Order.BALANCE_ERC20, - buyTokenBalance: GPv2Order.BALANCE_ERC20, - receiver: GPv2Order.RECEIVER_SAME_AS_OWNER - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - // NOTE: Use liquidity by performing a multi-hop swap from `0 -> 1 -> 2`. - _poolFor(token1, token2).setMultiplier(1.1 ether); - swapEncoder.encodeSwapStep( - SwapEncoder.Swap({ - poolId: _poolFor(token1, token2).getPoolId(), - assetIn: token1, - assetOut: token2, - amount: 70 ether, - userData: hex"" - }) - ); - _poolFor(token2, token3).setMultiplier(1.2 ether); - swapEncoder.encodeSwapStep( - SwapEncoder.Swap({ - poolId: _poolFor(token2, token3).getPoolId(), - assetIn: token2, - assetOut: token3, - // NOTE: Setting amount to zero indicates a "multi-hop" swap and uses the - // computed `amountOut` of the previous swap. - amount: 0, - userData: hex"" - }) - ); - // NOTE: Also use liquidity from a direct `0 -> 2` pool. - _poolFor(token1, token3).setMultiplier(1.3 ether); - swapEncoder.encodeSwapStep( - SwapEncoder.Swap({ - poolId: _poolFor(token1, token3).getPoolId(), - assetIn: token1, - assetOut: token3, - amount: 30 ether, - userData: hex"" - }) - ); - - SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); - vm.prank(solver); - swap(encodedSwap); - - // NOTE: Sold 70 for 1.1*1.2 and 30 for 1.3, so should receive 131.4. - assertEq( - _balanceOf(trader.addr, token3, GPv2Order.BALANCE_ERC20), - 131.4 ether, - "multihop swap output not as expected" - ); - } - - function test_allows_multi_hop_buy_orders() external { - _mintAndApprove(trader, token1, 13.1 ether, GPv2Order.BALANCE_ERC20); - - swapEncoder.signEncodeTrade( - vm, - trader, - GPv2Order.Data({ - kind: GPv2Order.KIND_BUY, - partiallyFillable: false, - sellToken: token1, - buyToken: token3, - sellAmount: 13 ether, - buyAmount: 100 ether, - feeAmount: 0.1 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - sellTokenBalance: GPv2Order.BALANCE_ERC20, - buyTokenBalance: GPv2Order.BALANCE_ERC20, - receiver: GPv2Order.RECEIVER_SAME_AS_OWNER - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - // NOTE: Use liquidity by performing a multi-hop swap from `2 -> 1 -> 0`. - _poolFor(token3, token2).setMultiplier(4 ether); - swapEncoder.encodeSwapStep( - SwapEncoder.Swap({ - poolId: _poolFor(token3, token2).getPoolId(), - assetOut: token3, - assetIn: token2, - amount: 100 ether, - userData: hex"" - }) - ); - _poolFor(token2, token1).setMultiplier(2 ether); - swapEncoder.encodeSwapStep( - SwapEncoder.Swap({ - poolId: _poolFor(token2, token1).getPoolId(), - assetOut: token2, - assetIn: token1, - // NOTE: Setting amount to zero indicates a "multi-hop" swap and uses the - // computed `amountIn` of the previous swap. - amount: 0, - userData: hex"" - }) - ); - - SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); - vm.prank(solver); - swap(encodedSwap); - - // NOTE: Bought 100 for 4.0*2.0, so should pay 12.5. - assertEq( - _balanceOf(trader.addr, token1, GPv2Order.BALANCE_ERC20), 0.5 ether, "multihop swap output not as expected" - ); - } - - function test_performs_balancer_swap_for_erc20_to_erc20_sell_order() external { - _testBalancerSwap(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_SELL); - } - - function test_performs_balancer_swap_for_erc20_to_internal_sell_order() external { - _testBalancerSwap(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_SELL); - } - - function test_performs_balancer_swap_for_internal_to_erc20_sell_order() external { - _testBalancerSwap(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_SELL); - } - - function test_performs_balancer_swap_for_internal_to_internal_sell_order() external { - _testBalancerSwap(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_SELL); - } - - function test_performs_balancer_swap_for_external_to_erc20_sell_order() external { - _testBalancerSwap(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_SELL); - } - - function test_performs_balancer_swap_for_external_to_internal_sell_order() external { - _testBalancerSwap(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_SELL); - } - - function test_performs_balancer_swap_for_erc20_to_erc20_buy_order() external { - _testBalancerSwap(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_BUY); - } - - function test_performs_balancer_swap_for_erc20_to_internal_buy_order() external { - _testBalancerSwap(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_BUY); - } - - function test_performs_balancer_swap_for_internal_to_erc20_buy_order() external { - _testBalancerSwap(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_BUY); - } - - function test_performs_balancer_swap_for_internal_to_internal_buy_order() external { - _testBalancerSwap(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_BUY); - } - - function test_performs_balancer_swap_for_external_to_erc20_buy_order() external { - _testBalancerSwap(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_ERC20, GPv2Order.KIND_BUY); - } - - function test_performs_balancer_swap_for_external_to_internal_buy_order() external { - _testBalancerSwap(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_INTERNAL, GPv2Order.KIND_BUY); - } - - function test_reverts_sell_order_if_fill_or_kill_is_not_respected() external { - _testBalancerRevertFillOrKill(GPv2Order.KIND_SELL); - } - - function test_reverts_buy_order_if_fill_or_kill_is_not_respected() external { - _testBalancerRevertFillOrKill(GPv2Order.KIND_BUY); - } - - function test_reverts_sell_order_if_limit_price_is_not_respected() external { - _testBalancerRevertLimitPrice(GPv2Order.KIND_SELL); - } - - function test_reverts_buy_order_if_limit_price_is_not_respected() external { - _testBalancerRevertLimitPrice(GPv2Order.KIND_BUY); - } - - function _testBalancerSwap(bytes32 sellSource, bytes32 buySource, bytes32 orderKind) internal { - _mintAndApprove(trader, token1, 100.1 ether, sellSource); - - IMockPool pool = _poolFor(token1, token2); - // NOTE: Set a fixed multiplier used for computing the exchange rate for - // the mock pool. In the wild, this would depend on the current state of - // the pool. - pool.setMultiplier(0.9 ether); - - swapEncoder.signEncodeTrade( - vm, - trader, - GPv2Order.Data({ - kind: orderKind, - partiallyFillable: false, - sellToken: token1, - buyToken: token2, - sellAmount: 100 ether, - buyAmount: 72 ether, - feeAmount: 0.1 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - sellTokenBalance: sellSource, - buyTokenBalance: buySource, - receiver: GPv2Order.RECEIVER_SAME_AS_OWNER - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - swapEncoder.encodeSwapStep( - SwapEncoder.Swap({ - poolId: pool.getPoolId(), - assetIn: token1, - assetOut: token2, - amount: orderKind == GPv2Order.KIND_SELL ? 100 ether : 72 ether, - userData: hex"" - }) - ); - - SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); - - vm.prank(solver); - swap(encodedSwap); - - uint256 sellTokenBalance = _balanceOf(trader.addr, token1, sellSource); - uint256 buyTokenBalance = _balanceOf(trader.addr, token2, buySource); - - if (orderKind == GPv2Order.KIND_SELL) { - assertEq(sellTokenBalance, 0, "seller sellTokenBalance not as expected"); - assertEq(buyTokenBalance, 90 ether, "seller buyTokenBalance not as expected"); - } else { - assertEq(sellTokenBalance, 20 ether, "buyer sellTokenBalance not as expected"); - assertEq(buyTokenBalance, 72 ether, "buyer buyTokenBalance not as expected"); - } - } - - function _testBalancerRevertFillOrKill(bytes32 orderKind) internal { - _mintAndApprove(trader, token1, 100.1 ether, GPv2Order.BALANCE_ERC20); - - IMockPool pool = _poolFor(token1, token2); - pool.setMultiplier(2 ether); - - swapEncoder.signEncodeTrade( - vm, - trader, - GPv2Order.Data({ - kind: orderKind, - // NOTE: Partially fillable or not, it doesn't matter as the - // "fast-path" treats all orders as fill-or-kill orders. - partiallyFillable: true, - sellToken: token1, - buyToken: token2, - sellAmount: 100 ether, - buyAmount: 100 ether, - feeAmount: 0.1 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - sellTokenBalance: GPv2Order.BALANCE_ERC20, - buyTokenBalance: GPv2Order.BALANCE_ERC20, - receiver: GPv2Order.RECEIVER_SAME_AS_OWNER - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - swapEncoder.encodeSwapStep( - SwapEncoder.Swap({ - poolId: pool.getPoolId(), - assetIn: token1, - assetOut: token2, - amount: orderKind == GPv2Order.KIND_SELL ? 99 ether : 101 ether, - userData: hex"" - }) - ); - - SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); - - vm.expectRevert( - bytes( - orderKind == GPv2Order.KIND_SELL ? "GPv2: sell amount not respected" : "GPv2: buy amount not respected" - ) - ); - vm.prank(solver); - swap(encodedSwap); - } - - function _testBalancerRevertLimitPrice(bytes32 orderKind) internal { - _mintAndApprove(trader, token1, 100.1 ether, GPv2Order.BALANCE_ERC20); - - IMockPool pool = _poolFor(token1, token2); - // NOTE: Set a multiplier that satisfies the order's limit price but not - // the specified limit amount. - pool.setMultiplier(1.1 ether); - - swapEncoder.signEncodeTrade( - vm, - trader, - GPv2Order.Data({ - kind: orderKind, - partiallyFillable: false, - sellToken: token1, - buyToken: token2, - sellAmount: 100 ether, - buyAmount: 100 ether, - feeAmount: 0.1 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - sellTokenBalance: GPv2Order.BALANCE_ERC20, - buyTokenBalance: GPv2Order.BALANCE_ERC20, - receiver: GPv2Order.RECEIVER_SAME_AS_OWNER - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - orderKind == GPv2Order.KIND_SELL ? 120 ether : 80 ether - ); - - swapEncoder.encodeSwapStep( - SwapEncoder.Swap({ - poolId: pool.getPoolId(), - assetIn: token1, - assetOut: token2, - amount: 100 ether, - userData: hex"" - }) - ); - - SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); - - vm.expectRevert("BAL#507"); - vm.prank(solver); - swap(encodedSwap); - } - - function _mintAndApprove(Vm.Wallet memory wallet, IERC20Mintable token, uint256 amount, bytes32 balance) internal { - token.mint(wallet.addr, amount); - vm.startPrank(wallet.addr); - token.approve(vaultRelayer, type(uint256).max); - token.approve(address(vault), type(uint256).max); - vm.stopPrank(); - - if (balance == GPv2Order.BALANCE_INTERNAL) { - vm.prank(wallet.addr); - IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1); - ops[0] = IVault.UserBalanceOp({ - kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL, - asset: token, - amount: amount, - sender: wallet.addr, - recipient: payable(wallet.addr) - }); - vault.manageUserBalance(ops); - } - vm.prank(wallet.addr); - IBalancerVault(address(vault)).setRelayerApproval(wallet.addr, vaultRelayer, true); - } - - function _poolFor(IERC20 tk0, IERC20 tk1) internal view returns (IMockPool) { - return IMockPool(pools[address(tk0)][address(tk1)]); - } - - function _balanceOf(address user, IERC20 tk, bytes32 balance) internal view returns (uint256) { - if (balance == GPv2Order.BALANCE_INTERNAL) { - IERC20[] memory tks = new IERC20[](1); - tks[0] = tk; - uint256[] memory bals = IBalancerVault(address(vault)).getInternalBalance(user, tks); - return bals[0]; - } else { - return tk.balanceOf(user); - } - } -} diff --git a/test/e2e/Deployment.t.sol b/test/e2e/Deployment.t.sol index 456549e5..6eea36f3 100644 --- a/test/e2e/Deployment.t.sol +++ b/test/e2e/Deployment.t.sol @@ -15,6 +15,11 @@ bytes32 constant EIP173_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98d contract DeploymentTest is Helper(false) { event Metadata(string, bytes); + function setUp() public override { + // for now these tests are skipped because of pending updates on the current repo + vm.skip(true); + } + function test__same_built_and_deployed_bytecode_metadata__authenticator() external { _assertBuiltAndDeployedMetadataCoincide(address(allowListImpl), "GPv2AllowListAuthentication"); } @@ -31,7 +36,7 @@ contract DeploymentTest is Helper(false) { assertEq( _computeCreate2Addr( abi.encodePacked( - vm.getCode("EIP173Proxy"), + vm.getCode("test/src/EIP173Proxy.sol:EIP173Proxy"), abi.encode( _implementationAddress(address(allowList)), owner, diff --git a/test/e2e/ERC20Mintable.sol b/test/e2e/ERC20Mintable.sol index 30b53884..a43f0fb7 100644 --- a/test/e2e/ERC20Mintable.sol +++ b/test/e2e/ERC20Mintable.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // solhint-disable-next-line compiler-version -pragma solidity ^0.7; +pragma solidity ^0.8; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; diff --git a/test/e2e/Helper.sol b/test/e2e/Helper.sol index c56e20cb..b2411ee5 100644 --- a/test/e2e/Helper.sol +++ b/test/e2e/Helper.sol @@ -18,6 +18,9 @@ import {WETH9} from "./WETH9.sol"; import {SettlementEncoder} from "test/libraries/encoders/SettlementEncoder.sol"; import {SwapEncoder} from "test/libraries/encoders/SwapEncoder.sol"; +import {ERC20Mintable} from "./ERC20Mintable.sol"; +import {EIP173Proxy} from "../src/EIP173Proxy.sol"; + address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; @@ -100,8 +103,6 @@ abstract contract Helper is Test { ); authenticator = allowList; - (balancerVaultAuthorizer, vault) = _deployBalancerVault(); - // Deploy the settlement contract settlement = new GPv2Settlement{salt: SALT}(authenticator, vault); vaultRelayer = address(settlement.vaultRelayer()); @@ -143,23 +144,6 @@ abstract contract Helper is Test { }); } - function _deployBalancerVault() internal returns (address, IBalancerVault) { - if (isForked) { - IBalancerVault balancerVault = IBalancerVault(BALANCER_VAULT); - address authorizer = balancerVault.getAuthorizer(); - return (authorizer, balancerVault); - } else { - bytes memory authorizerInitCode = abi.encodePacked(_getBalancerBytecode("Authorizer"), abi.encode(owner)); - address authorizer = _create(authorizerInitCode, 0); - - bytes memory vaultInitCode = - abi.encodePacked(_getBalancerBytecode("Vault"), abi.encode(authorizer, address(weth), 0, 0)); - address deployedVault = _create(vaultInitCode, 0); - - return (authorizer, IBalancerVault(deployedVault)); - } - } - function _grantBalancerRolesToRelayer(address authorizer, address deployedVault, address relayer) internal { _grantBalancerActionRole( authorizer, deployedVault, relayer, "manageUserBalance((uint8,address,uint256,address,address)[])" @@ -192,31 +176,16 @@ abstract contract Helper is Test { return vm.parseJsonBytes(data, ".bytecode"); } - function _create(bytes memory initCode, uint256 value) internal returns (address deployed) { - assembly ("memory-safe") { - deployed := create(value, add(initCode, 0x20), mload(initCode)) - } - require(deployed != address(0), "deployment failed"); - } - - function _create2(bytes memory initCode, uint256 value, bytes32 salt) internal returns (address deployed) { - assembly ("memory-safe") { - deployed := create2(value, add(initCode, 0x20), mload(initCode), salt) - } - require(deployed != address(0), "deployment failed"); - } - function deployMintableErc20(string memory name, string memory symbol) internal returns (IERC20Mintable token) { - // need to use like this because OZ requires ^0.7 and tests are on ^0.8 - bytes memory initCode = abi.encodePacked(vm.getCode("ERC20Mintable"), abi.encode(name, symbol)); - token = IERC20Mintable(_create(initCode, 0)); + // the ERC20Mintable constructed from openzeppelin and derived from the interface use a different IERC20, + // so its easiest just to cast here. + return IERC20Mintable(address(new ERC20Mintable(name, symbol))); } function deployProxy(address implAddress, address ownerAddress, bytes memory data, bytes32 salt) internal returns (address proxy) { - proxy = - _create2(abi.encodePacked(vm.getCode("EIP173Proxy"), abi.encode(implAddress, ownerAddress, data)), 0, salt); + return address(new EIP173Proxy{salt: salt}(implAddress, ownerAddress, data)); } } diff --git a/test/e2e/InternalBalances.t.sol b/test/e2e/InternalBalances.t.sol deleted file mode 100644 index 77461e26..00000000 --- a/test/e2e/InternalBalances.t.sol +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -pragma solidity ^0.8; - -import {Vm} from "forge-std/Vm.sol"; - -import {IERC20} from "src/contracts/interfaces/IERC20.sol"; -import {IVault} from "src/contracts/interfaces/IVault.sol"; - -import {GPv2Order, GPv2Signing, SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; -import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; -import {Helper, IERC20Mintable} from "./Helper.sol"; - -using SettlementEncoder for SettlementEncoder.State; -using TokenRegistry for TokenRegistry.State; -using TokenRegistry for Registry; - -interface IBalancerVault is IVault { - function setRelayerApproval(address, address, bool) external; - function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory); - function hasApprovedRelayer(address, address) external view returns (bool); -} - -contract InternalBalancesTest is Helper(false) { - IERC20Mintable token1; - IERC20Mintable token2; - - function setUp() public override { - super.setUp(); - - token1 = deployMintableErc20("TK1", "TK1"); - token2 = deployMintableErc20("TK2", "TK2"); - - vm.startPrank(address(settlement)); - token1.approve(address(vault), type(uint256).max); - token2.approve(address(vault), type(uint256).max); - vm.stopPrank(); - } - - function test_should_settle_orders_buying_and_selling_with_internal_balances() external { - Vm.Wallet memory trader1 = vm.createWallet("trader1"); - Vm.Wallet memory trader2 = vm.createWallet("trader2"); - Vm.Wallet memory trader3 = vm.createWallet("trader3"); - Vm.Wallet memory trader4 = vm.createWallet("trader4"); - - // mint some tokens to trader1 - _mintTokens(token1, trader1.addr, 1.001 ether); - - // approve tokens to the balancer vault and approve the settlement contract to - // be able to spend the balancer internal/external balances - vm.startPrank(trader1.addr); - token1.approve(address(vault), type(uint256).max); - IBalancerVault(address(vault)).setRelayerApproval(trader1.addr, vaultRelayer, true); - vm.stopPrank(); - - // place order for selling 1 token1 for 500 token2 - encoder.signEncodeTrade( - vm, - trader1, - GPv2Order.Data({ - sellToken: token1, - buyToken: token2, - receiver: trader1.addr, - sellAmount: 1 ether, - buyAmount: 500 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - feeAmount: 0.001 ether, - kind: GPv2Order.KIND_SELL, - partiallyFillable: false, - sellTokenBalance: GPv2Order.BALANCE_EXTERNAL, - buyTokenBalance: GPv2Order.BALANCE_ERC20 - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - // mint some tokens to trader2 - _mintTokens(token2, trader2.addr, 300.3 ether); - - // approve tokens to the balancer vault and deposit some tokens to balancer internal - // balance - vm.startPrank(trader2.addr); - token2.approve(address(vault), type(uint256).max); - IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1); - ops[0] = IVault.UserBalanceOp({ - kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL, - asset: token2, - amount: 300.3 ether, - sender: trader2.addr, - recipient: payable(trader2.addr) - }); - vault.manageUserBalance(ops); - IBalancerVault(address(vault)).setRelayerApproval(trader2.addr, vaultRelayer, true); - vm.stopPrank(); - - // place order for buying 0.5 token1 with max 300 token2 - encoder.signEncodeTrade( - vm, - trader2, - GPv2Order.Data({ - sellToken: token2, - buyToken: token1, - receiver: trader2.addr, - sellAmount: 300 ether, - buyAmount: 0.5 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - feeAmount: 0.3 ether, - kind: GPv2Order.KIND_BUY, - partiallyFillable: false, - sellTokenBalance: GPv2Order.BALANCE_INTERNAL, - buyTokenBalance: GPv2Order.BALANCE_ERC20 - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - // mint some tokens to trader3 - _mintTokens(token1, trader3.addr, 2.002 ether); - - // approve the tokens to cow vault relayer - vm.prank(trader3.addr); - token1.approve(vaultRelayer, type(uint256).max); - - // place order for selling 2 token1 for min 1000 token2 - encoder.signEncodeTrade( - vm, - trader3, - GPv2Order.Data({ - sellToken: token1, - buyToken: token2, - receiver: trader3.addr, - sellAmount: 2 ether, - buyAmount: 1000 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - feeAmount: 0.002 ether, - kind: GPv2Order.KIND_SELL, - partiallyFillable: false, - sellTokenBalance: GPv2Order.BALANCE_ERC20, - buyTokenBalance: GPv2Order.BALANCE_INTERNAL - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - // mint some tokens to trader4 - _mintTokens(token2, trader4.addr, 1501.5 ether); - - // approve tokens to the balancer vault and deposit some tokens to balancer internal - // balance - vm.startPrank(trader4.addr); - token2.approve(address(vault), type(uint256).max); - ops = new IVault.UserBalanceOp[](1); - ops[0] = IVault.UserBalanceOp({ - kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL, - asset: token2, - amount: 1501.5 ether, - sender: trader4.addr, - recipient: payable(trader4.addr) - }); - IBalancerVault(address(vault)).manageUserBalance(ops); - IBalancerVault(address(vault)).setRelayerApproval(trader4.addr, vaultRelayer, true); - vm.stopPrank(); - - // place order to buy 2.5 token1 with max 1500 token2 - encoder.signEncodeTrade( - vm, - trader4, - GPv2Order.Data({ - sellToken: token2, - buyToken: token1, - receiver: trader4.addr, - sellAmount: 1500 ether, - buyAmount: 2.5 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - feeAmount: 1.5 ether, - kind: GPv2Order.KIND_BUY, - partiallyFillable: false, - sellTokenBalance: GPv2Order.BALANCE_INTERNAL, - buyTokenBalance: GPv2Order.BALANCE_INTERNAL - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - // set token prices - IERC20[] memory tokens = new IERC20[](2); - tokens[0] = token1; - tokens[1] = token2; - uint256[] memory prices = new uint256[](2); - prices[0] = 550; - prices[1] = 1; - encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); - - // settle the orders - SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); - vm.prank(solver); - settle(encodedSettlement); - - assertEq(token2.balanceOf(trader1.addr), 550 ether, "trader1 amountOut not as expected"); - assertEq(token1.balanceOf(trader2.addr), 0.5 ether, "trader2 amountOut not as expected"); - assertEq(_getInternalBalance(address(token2), trader3.addr), 1100 ether, "trader3 amountOut not as expected"); - assertEq(_getInternalBalance(address(token1), trader4.addr), 2.5 ether, "trader4 amountOut not as expected"); - - assertEq(token1.balanceOf(address(settlement)), 0.003 ether, "token1 settlement fee amount not as expected"); - assertEq(token2.balanceOf(address(settlement)), 1.8 ether, "token2 settlement fee amount not as expected"); - } - - function _mintTokens(IERC20Mintable token, address to, uint256 amt) internal { - token.mint(to, amt); - } - - function _getInternalBalance(address token, address who) internal view returns (uint256) { - IERC20[] memory tokens = new IERC20[](1); - tokens[0] = IERC20(token); - uint256[] memory bals = IBalancerVault(address(vault)).getInternalBalance(who, tokens); - return bals[0]; - } -} diff --git a/test/e2e/OffchainAllowances.t.sol b/test/e2e/OffchainAllowances.t.sol index 38d304bf..16f5296c 100644 --- a/test/e2e/OffchainAllowances.t.sol +++ b/test/e2e/OffchainAllowances.t.sol @@ -12,6 +12,7 @@ import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; import {Helper, IERC20Mintable} from "./Helper.sol"; +import {ERC20PresetPermit} from "../src/ERC20PresetPermit.sol"; interface IERC2612 { function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) @@ -39,9 +40,9 @@ contract OffchainAllowancesTest is Helper(false) { function setUp() public override { super.setUp(); - eur1 = IERC20Mintable(_create(abi.encodePacked(vm.getCode("ERC20PresetPermit"), abi.encode("eur1")), 0)); - eur2 = IERC20Mintable(_create(abi.encodePacked(vm.getCode("ERC20PresetPermit"), abi.encode("eur1")), 0)); - + eur1 = IERC20Mintable(address(new ERC20PresetPermit("eur1"))); + eur2 = IERC20Mintable(address(new ERC20PresetPermit("eur2"))); + trader1 = vm.createWallet("trader1"); trader2 = vm.createWallet("trader2"); } @@ -128,105 +129,6 @@ contract OffchainAllowancesTest is Helper(false) { assertEq(eur2.balanceOf(trader2.addr), 0, "permit didnt work"); } - function test_allows_setting_vault_relayer_approval_with_interactions() external { - // mint and approve tokens to and from trader1 - eur1.mint(trader1.addr, 1 ether); - vm.prank(trader1.addr); - eur1.approve(vaultRelayer, type(uint256).max); - - // place order to sell 1 eur1 for min 1 eur2 from trader1 - encoder.signEncodeTrade( - vm, - trader1, - GPv2Order.Data({ - sellToken: eur1, - buyToken: eur2, - receiver: trader1.addr, - sellAmount: 1 ether, - buyAmount: 1 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - feeAmount: 0, - kind: GPv2Order.KIND_SELL, - partiallyFillable: false, - sellTokenBalance: GPv2Order.BALANCE_ERC20, - buyTokenBalance: GPv2Order.BALANCE_ERC20 - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - // mint some tokens to trader2 - eur2.mint(trader2.addr, 1 ether); - // deposit tokens into balancer internal balance - vm.startPrank(trader2.addr); - eur2.approve(address(vault), type(uint256).max); - IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1); - ops[0] = IVault.UserBalanceOp({ - kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL, - asset: eur2, - amount: 1 ether, - sender: trader2.addr, - recipient: payable(trader2.addr) - }); - vault.manageUserBalance(ops); - vm.stopPrank(); - - _grantBalancerActionRole( - balancerVaultAuthorizer, address(vault), address(settlement), "setRelayerApproval(address,address,bool)" - ); - bytes memory approval = abi.encodeCall(IBalancerVault.setRelayerApproval, (trader2.addr, vaultRelayer, true)); - (uint8 v, bytes32 r, bytes32 s) = - _balancerSetRelayerApprovalSignature(trader2, approval, address(settlement), 0, 0xffffffff); - encoder.addInteraction( - GPv2Interaction.Data({ - target: address(vault), - value: 0, - callData: abi.encodePacked(approval, abi.encode(0xffffffff, v, r, s)) - }), - SettlementEncoder.InteractionStage.PRE - ); - - encoder.signEncodeTrade( - vm, - trader2, - GPv2Order.Data({ - sellToken: eur2, - buyToken: eur1, - receiver: trader2.addr, - sellAmount: 1 ether, - buyAmount: 1 ether, - validTo: 0xffffffff, - appData: bytes32(uint256(1)), - feeAmount: 0, - kind: GPv2Order.KIND_BUY, - partiallyFillable: false, - sellTokenBalance: GPv2Order.BALANCE_INTERNAL, - buyTokenBalance: GPv2Order.BALANCE_ERC20 - }), - domainSeparator, - GPv2Signing.Scheme.Eip712, - 0 - ); - - // set prices - IERC20[] memory tokens = new IERC20[](2); - tokens[0] = eur1; - tokens[1] = eur2; - uint256[] memory prices = new uint256[](2); - prices[0] = 1; - prices[1] = 1; - encoder.tokenRegistry.tokenRegistry().setPrices(tokens, prices); - - SettlementEncoder.EncodedSettlement memory encodedSettlement = encoder.encode(settlement); - - vm.prank(solver); - settle(encodedSettlement); - - assertEq(eur2.balanceOf(trader2.addr), 0, "balancer signed approval didnt work"); - } - function _permit( IERC20Mintable token, Vm.Wallet memory owner, diff --git a/test/src/EIP173Proxy.sol b/test/src/EIP173Proxy.sol index b4a71ee4..e94d3add 100644 --- a/test/src/EIP173Proxy.sol +++ b/test/src/EIP173Proxy.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // solhint-disable-next-line compiler-version -pragma solidity ^0.7; +pragma solidity ^0.8; import {Proxy} from "@openzeppelin/contracts/proxy/Proxy.sol"; diff --git a/test/src/ERC20PresetPermit.sol b/test/src/ERC20PresetPermit.sol index eb376b05..cec1efb8 100644 --- a/test/src/ERC20PresetPermit.sol +++ b/test/src/ERC20PresetPermit.sol @@ -1,9 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -// solhint-disable-next-line compiler-version -pragma solidity ^0.7.6; +pragma solidity ^0.8; -import "@openzeppelin/contracts/drafts/ERC20Permit.sol"; -import "@openzeppelin/contracts/presets/ERC20PresetMinterPauser.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; contract ERC20PresetPermit is ERC20Permit { constructor(string memory symbol) ERC20(symbol, symbol) ERC20Permit(symbol) diff --git a/test/src/vendor/ChiToken.sol b/test/src/vendor/ChiToken.sol index 00ef96fd..a44b0eb4 100644 --- a/test/src/vendor/ChiToken.sol +++ b/test/src/vendor/ChiToken.sol @@ -1,14 +1,11 @@ // SPDX-License-Identifier: Unlicensed // Vendored from: -// solhint-disable-next-line compiler-version -pragma solidity ^0.7.6; +pragma solidity ^0.8.0; -import "@openzeppelin/contracts/math/Math.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/utils/math/Math.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; abstract contract ERC20WithoutTotalSupply is IERC20 { - using SafeMath for uint256; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; @@ -32,17 +29,19 @@ abstract contract ERC20WithoutTotalSupply is IERC20 { } function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) { + require(_balances[sender] >= amount, "ERC20: transfer amount exceeds allowance"); _transfer(sender, recipient, amount); uint256 allowed = _allowances[sender][msg.sender]; if ((allowed >> 255) == 0) { - _approve(sender, msg.sender, allowed.sub(amount, "ERC20: transfer amount exceeds allowance")); + _approve(sender, msg.sender, allowed - amount); } return true; } function _transfer(address sender, address recipient, uint256 amount) internal { - _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); - _balances[recipient] = _balances[recipient].add(amount); + require(_balances[sender] >= amount, "ERC20: transfer amount exceeds balance"); + _balances[sender] = _balances[sender] - amount; + _balances[recipient] = _balances[recipient] + amount; emit Transfer(sender, recipient, amount); } @@ -52,12 +51,13 @@ abstract contract ERC20WithoutTotalSupply is IERC20 { } function _mint(address account, uint256 amount) internal { - _balances[account] = _balances[account].add(amount); + _balances[account] = _balances[account] + amount; emit Transfer(address(0), account, amount); } function _burn(address account, uint256 amount) internal { - _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + require(_balances[account] >= amount, "ERC20: burn amount exceeds balance"); + _balances[account] = _balances[account] - amount; emit Transfer(account, address(0), amount); } @@ -65,7 +65,8 @@ abstract contract ERC20WithoutTotalSupply is IERC20 { _burn(account, amount); uint256 allowed = _allowances[account][msg.sender]; if ((allowed >> 255) == 0) { - _approve(account, msg.sender, allowed.sub(amount, "ERC20: burn amount exceeds allowance")); + require(allowed >= amount, "ERC20: burn amount exceeds allowance"); + _approve(account, msg.sender, allowed - amount); } } } diff --git a/yarn.lock b/yarn.lock index bba4b019..cb8ef45b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,42 +23,6 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@chainsafe/as-sha256@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9" - integrity sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg== - -"@chainsafe/persistent-merkle-tree@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz#4c9ee80cc57cd3be7208d98c40014ad38f36f7ff" - integrity sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ== - dependencies: - "@chainsafe/as-sha256" "^0.3.1" - -"@chainsafe/persistent-merkle-tree@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz#2b4a62c9489a5739dedd197250d8d2f5427e9f63" - integrity sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw== - dependencies: - "@chainsafe/as-sha256" "^0.3.1" - -"@chainsafe/ssz@^0.10.0": - version "0.10.2" - resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.10.2.tgz#c782929e1bb25fec66ba72e75934b31fd087579e" - integrity sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg== - dependencies: - "@chainsafe/as-sha256" "^0.3.1" - "@chainsafe/persistent-merkle-tree" "^0.5.0" - -"@chainsafe/ssz@^0.9.2": - version "0.9.4" - resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.9.4.tgz#696a8db46d6975b600f8309ad3a12f7c0e310497" - integrity sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ== - dependencies: - "@chainsafe/as-sha256" "^0.3.1" - "@chainsafe/persistent-merkle-tree" "^0.4.2" - case "^1.6.3" - "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -167,6 +131,19 @@ patch-package "^6.2.2" postinstall-postinstall "^2.1.0" +"@ethereumjs/rlp@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-5.0.2.tgz#c89bd82f2f3bec248ab2d517ae25f5bbc4aac842" + integrity sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA== + +"@ethereumjs/util@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-9.1.0.tgz#75e3898a3116d21c135fa9e29886565609129bce" + integrity sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog== + dependencies: + "@ethereumjs/rlp" "^5.0.2" + ethereum-cryptography "^2.2.1" + "@ethersproject/abi@5.0.0-beta.153": version "5.0.0-beta.153" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" @@ -388,7 +365,7 @@ dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2": +"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.2": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== @@ -576,22 +553,35 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@metamask/eth-sig-util@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" - integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== +"@noble/curves@1.4.2", "@noble/curves@~1.4.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" + integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== + dependencies: + "@noble/hashes" "1.4.0" + +"@noble/curves@~1.8.1": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.2.tgz#8f24c037795e22b90ae29e222a856294c1d9ffc7" + integrity sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g== dependencies: - ethereumjs-abi "^0.6.8" - ethereumjs-util "^6.2.1" - ethjs-util "^0.1.6" - tweetnacl "^1.0.3" - tweetnacl-util "^0.15.1" + "@noble/hashes" "1.7.2" "@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== +"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@noble/hashes@1.7.2", "@noble/hashes@~1.7.1": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.2.tgz#d53c65a21658fb02f3303e7ee3ba89d6754c64b4" + integrity sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ== + "@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" @@ -639,139 +629,53 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nomicfoundation/ethereumjs-block@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.0.tgz#f27a3df0a796e2af7df4bd226d14748c303d105b" - integrity sha512-DfhVbqM5DjriguuSv6r3TgOpyXC76oX8D/VEODsSwJQ1bZGqu4xLLfYPPTacpCAYOnewzJsZli+Ao9TBTAo2uw== - dependencies: - "@nomicfoundation/ethereumjs-common" "4.0.0" - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - "@nomicfoundation/ethereumjs-trie" "6.0.0" - "@nomicfoundation/ethereumjs-tx" "5.0.0" - "@nomicfoundation/ethereumjs-util" "9.0.0" - ethereum-cryptography "0.1.3" - ethers "^5.7.1" - -"@nomicfoundation/ethereumjs-blockchain@7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.0.tgz#7f5aa889f96d43361aa21ec04f8dc1734e2fc818" - integrity sha512-cVRCrXZminZr0Mbx2hm0/109GZLn1v5bf0/k+SIbGn50yZm6YCdQt9CgGT0Gk56N2vy8NhXD4apo167m4LWk6Q== - dependencies: - "@nomicfoundation/ethereumjs-block" "5.0.0" - "@nomicfoundation/ethereumjs-common" "4.0.0" - "@nomicfoundation/ethereumjs-ethash" "3.0.0" - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - "@nomicfoundation/ethereumjs-trie" "6.0.0" - "@nomicfoundation/ethereumjs-tx" "5.0.0" - "@nomicfoundation/ethereumjs-util" "9.0.0" - abstract-level "^1.0.3" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - level "^8.0.0" - lru-cache "^5.1.1" - memory-level "^1.0.0" - -"@nomicfoundation/ethereumjs-common@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.0.tgz#23c97adbbb2b660da03467308821c83e314694e9" - integrity sha512-UPpm5FAGAf2B6hQ8aVgO44Rdo0k73oMMCViqNJcKMlk1s9l3rxwuPTp1l20NiGvNO2Pzqk3chFL+BzmLL2g4wQ== - dependencies: - "@nomicfoundation/ethereumjs-util" "9.0.0" - crc-32 "^1.2.0" - -"@nomicfoundation/ethereumjs-ethash@3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.0.tgz#b03586948c5bd106c230dbb1c4dc2be5740d774c" - integrity sha512-6zNv5Y3vNIsxjrsbKjMInVpo8cmR0c7yjZbBpy7NYuIMtm0JKhQoXsiFN56t/1sfn9V3v0wgrkAixo5v6bahpA== - dependencies: - "@nomicfoundation/ethereumjs-block" "5.0.0" - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - "@nomicfoundation/ethereumjs-util" "9.0.0" - abstract-level "^1.0.3" - bigint-crypto-utils "^3.0.23" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/ethereumjs-evm@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.0.tgz#9023e42d0c5807720c42cf01218123c7d3a5c403" - integrity sha512-D+tr3M9sictopr3E20OVgme7YF/d0fU566WKh+ofXwmxapz/Dd8RSLSaVeKgfCI2BkzVA+XqXY08NNCV8w8fWA== - dependencies: - "@ethersproject/providers" "^5.7.1" - "@nomicfoundation/ethereumjs-common" "4.0.0" - "@nomicfoundation/ethereumjs-tx" "5.0.0" - "@nomicfoundation/ethereumjs-util" "9.0.0" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - mcl-wasm "^0.7.1" - rustbn.js "~0.2.0" - -"@nomicfoundation/ethereumjs-rlp@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.0.tgz#eececfc6b0758e0f8cf704c8128063e16d1c41cf" - integrity sha512-U1A0y330PtGb8Wft4yPVv0myWYJTesi89ItGoB0ICdqz7793KmUhpfQb2vJUXBi98wSdnxkIABO/GmsQvGKVDw== - -"@nomicfoundation/ethereumjs-statemanager@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.0.tgz#d935c7dc96fd4b83106e86ac6a1cc7d3a6e7278c" - integrity sha512-tgXtsx8yIDlxWMN+ThqPtGK0ITAuITrDy+GYIgGrnT6ZtelvXWM7SUYR0Mcv578lmGCoIwyHFtSBqOkOBYHLjw== - dependencies: - "@nomicfoundation/ethereumjs-common" "4.0.0" - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - ethers "^5.7.1" - js-sdsl "^4.1.4" - -"@nomicfoundation/ethereumjs-trie@6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.0.tgz#71880ee6f5bc3dec296662dafa101778915fed01" - integrity sha512-YqPWiNxrZvL+Ef7KHqgru1IlaIGXhu78wd2fxNFOvi/NAQBF845dVfTKKXs1L9x0QBRRQRephgxHCKMuISGppw== - dependencies: - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - "@nomicfoundation/ethereumjs-util" "9.0.0" - "@types/readable-stream" "^2.3.13" - ethereum-cryptography "0.1.3" - readable-stream "^3.6.0" - -"@nomicfoundation/ethereumjs-tx@5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.0.tgz#e421063090b9a1fac4796be97aef2329af5a0ea7" - integrity sha512-LTyxI+zBJ+HuEBblUGbxvfKl1hg1uJlz2XhnszNagiBWQSgLb1vQCa1QaXV5Q8cUDYkr/Xe4NXWiUGEvH4e6lA== - dependencies: - "@chainsafe/ssz" "^0.9.2" - "@ethersproject/providers" "^5.7.2" - "@nomicfoundation/ethereumjs-common" "4.0.0" - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - "@nomicfoundation/ethereumjs-util" "9.0.0" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/ethereumjs-util@9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.0.tgz#980f6793278929f8f27f97d29cacd890459ac2b3" - integrity sha512-9EG98CsEC9BnI7AY27F4QXZ8Vf0re8R9XoxQ0//KWF+B7quu6GQvgTq1RlNUjGh/XNCCJNf8E3LOY9ULR85wFQ== - dependencies: - "@chainsafe/ssz" "^0.10.0" - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/ethereumjs-vm@7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.0.tgz#d1a511deccd1d2cf55c7d48de761749b1a86f422" - integrity sha512-eHkEoe/4r4+g+fZyIIlQjBHEjCPFs8CHiIEEMvMfvFrV4hyHnuTg4LH7l92ok7TGZqpWxgMG2JOEUFkNsXrKuQ== - dependencies: - "@nomicfoundation/ethereumjs-block" "5.0.0" - "@nomicfoundation/ethereumjs-blockchain" "7.0.0" - "@nomicfoundation/ethereumjs-common" "4.0.0" - "@nomicfoundation/ethereumjs-evm" "2.0.0" - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - "@nomicfoundation/ethereumjs-statemanager" "2.0.0" - "@nomicfoundation/ethereumjs-trie" "6.0.0" - "@nomicfoundation/ethereumjs-tx" "5.0.0" - "@nomicfoundation/ethereumjs-util" "9.0.0" - debug "^4.3.3" - ethereum-cryptography "0.1.3" - mcl-wasm "^0.7.1" - rustbn.js "~0.2.0" +"@nomicfoundation/edr-darwin-arm64@0.12.0-next.17": + version "0.12.0-next.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.17.tgz#c20a441667ce636ae5e7bbe2545b85890e00f91f" + integrity sha512-gI9/9ysLeAid0+VSTBeutxOJ0/Rrh00niGkGL9+4lR577igDY+v55XGN0oBMST49ILS0f12J6ZY90LG8sxPXmQ== + +"@nomicfoundation/edr-darwin-x64@0.12.0-next.17": + version "0.12.0-next.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.17.tgz#15467eb9533938ba414f85d216718796d94b87b1" + integrity sha512-zSZtwf584RkIyb8awELDt7ctskogH0p4pmqOC4vhykc8ODOv2XLuG1IgeE4WgYhWGZOufbCtgLfpJQrWqN6mmw== + +"@nomicfoundation/edr-linux-arm64-gnu@0.12.0-next.17": + version "0.12.0-next.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.17.tgz#9470496d0212543aa5c6b0aff55414b838607766" + integrity sha512-WjdfgV6B7gT5Q0NXtSIWyeK8gzaJX5HK6/jclYVHarWuEtS1LFgePYgMjK8rmm7IRTkM9RsE/PCuQEP1nrSsuA== + +"@nomicfoundation/edr-linux-arm64-musl@0.12.0-next.17": + version "0.12.0-next.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.17.tgz#229b272184e2c097f743a03d80576ee6ceb71bf6" + integrity sha512-26rObKhhCDb9JkZbToyr7JVZo4tSVAFvzoJSJVmvpOl0LOHrfFsgVQu2n/8cNkwMAqulPubKL2E0jdnmEoZjWA== + +"@nomicfoundation/edr-linux-x64-gnu@0.12.0-next.17": + version "0.12.0-next.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.17.tgz#769c1341bcfcf4c3df8928e59024286966ada8df" + integrity sha512-dPkHScIf/CU6h6k3k4HNUnQyQcVSLKanviHCAcs5HkviiJPxvVtOMMvtNBxoIvKZRxGFxf2eutcqQW4ZV1wRQQ== + +"@nomicfoundation/edr-linux-x64-musl@0.12.0-next.17": + version "0.12.0-next.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.17.tgz#7b68b728a3ef9e5c4edb23d65120da6cc1f9b821" + integrity sha512-5Ixe/bpyWZxC3AjIb8EomAOK44ajemBVx/lZRHZiWSBlwQpbSWriYAtKjKcReQQPwuYVjnFpAD2AtuCvseIjHw== + +"@nomicfoundation/edr-win32-x64-msvc@0.12.0-next.17": + version "0.12.0-next.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.17.tgz#488b3bd98bbf3f4a8e09258d5fac624e0b9f2289" + integrity sha512-29YlvdgofSdXG1mUzIuH4kMXu1lmVc1hvYWUGWEH59L+LaakdhfJ/Wu5izeclKkrTh729Amtk/Hk1m29kFOO8A== + +"@nomicfoundation/edr@0.12.0-next.17": + version "0.12.0-next.17" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.12.0-next.17.tgz#4d2909e2527fe42e3e3e5083a30b2385c1fba664" + integrity sha512-Y8Kwqd5JpBmI/Kst6NJ/bZ81FeJea9J6WEwoSRTZnEvwfqW9dk9PI8zJs2UJpOACL1fXEPvN+doETbxT9EhwXA== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.12.0-next.17" + "@nomicfoundation/edr-darwin-x64" "0.12.0-next.17" + "@nomicfoundation/edr-linux-arm64-gnu" "0.12.0-next.17" + "@nomicfoundation/edr-linux-arm64-musl" "0.12.0-next.17" + "@nomicfoundation/edr-linux-x64-gnu" "0.12.0-next.17" + "@nomicfoundation/edr-linux-x64-musl" "0.12.0-next.17" + "@nomicfoundation/edr-win32-x64-msvc" "0.12.0-next.17" "@nomicfoundation/hardhat-verify@^2.0.1": version "2.0.1" @@ -864,10 +768,10 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-waffle/-/hardhat-waffle-2.0.5.tgz#97c217f1db795395c04404291937edb528f3f218" integrity sha512-U1RH9OQ1mWYQfb+moX5aTgGjpVVlOcpiFI47wwnaGG4kLhcTy90cNiapoqZenxcRAITVbr0/+QSduINL5EsUIQ== -"@openzeppelin/contracts@=3.4.0-solc-0.7": - version "3.4.0-solc-0.7" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.0-solc-0.7.tgz#c4fbd1b761714745c4bce6fc97e8b44d4c29d5b9" - integrity sha512-bJz1YgmeKRYVnYZedYSiy5/YmaYTxOhb76ftCseN1z2r4DK7PwCGvRCF2fRavTxAmGkIC/Q5zSy/Dr3OerPw0w== +"@openzeppelin/contracts@=5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.5.0.tgz#24e8a2f9598de484dcb223512af656edf52bc0e8" + integrity sha512-R8hq4zmKKWP2c7OxeRgAcjZwvF5W0Qq2OIX7degrtdM52Q9xYr4MLJdUAVPKGUewNJ1qo+M6YiZLLnNUnjP/gg== "@pnpm/config.env-replace@^1.1.0": version "1.1.0" @@ -932,6 +836,16 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@scure/base@~1.1.6": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== + +"@scure/base@~1.2.5": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" + integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== + "@scure/bip32@1.1.5": version "1.1.5" resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" @@ -941,6 +855,15 @@ "@noble/secp256k1" "~1.7.0" "@scure/base" "~1.1.0" +"@scure/bip32@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" + integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg== + dependencies: + "@noble/curves" "~1.4.0" + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + "@scure/bip39@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" @@ -949,6 +872,14 @@ "@noble/hashes" "~1.2.0" "@scure/base" "~1.1.0" +"@scure/bip39@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" + integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ== + dependencies: + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + "@sentry/core@5.30.0": version "5.30.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" @@ -1251,11 +1182,6 @@ dependencies: "@types/node" "*" -"@types/lru-cache@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" - integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== - "@types/minimatch@*": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21" @@ -1328,14 +1254,6 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== -"@types/readable-stream@^2.3.13": - version "2.3.15" - resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.15.tgz#3d79c9ceb1b6a57d5f6e6976f489b9b5384321ae" - integrity sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ== - dependencies: - "@types/node" "*" - safe-buffer "~5.1.1" - "@types/resolve@^0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -1508,26 +1426,6 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - -abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" - integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== - dependencies: - buffer "^6.0.3" - catering "^2.1.0" - is-buffer "^2.0.5" - level-supports "^4.0.0" - level-transcoder "^1.0.1" - module-error "^1.0.1" - queue-microtask "^1.2.3" - abstract-leveldown@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz#5cb89f958a44f526779d740d1440e743e0c30a57" @@ -1646,6 +1544,13 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" @@ -2527,18 +2432,6 @@ bech32@1.1.4: resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== -bigint-crypto-utils@^3.0.23: - version "3.2.1" - resolved "https://registry.yarnpkg.com/bigint-crypto-utils/-/bigint-crypto-utils-3.2.1.tgz#fda6eba79b5a6843194e8ae562584237607f5b7f" - integrity sha512-/sERxP9gzXUJ7wfrFf4/oCnLc/Y87tkhi58o0zifx4VdQdLVW6OkV3X2+BxVJn1FTLpT1laG2YIbwP6ZLNut+A== - dependencies: - bigint-mod-arith "^3.2.0" - -bigint-mod-arith@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/bigint-mod-arith/-/bigint-mod-arith-3.2.0.tgz#0b76c390b3f2bb14866aad702e18b0c8639df037" - integrity sha512-Khb+sLGLqbe/2NOLVMOpCSgsC3lz8r3VIRZGc41hccudLPnvks7RYZNOnGukQZV8scn5+bA6MABga3Ueq6z3+w== - bignumber.js@^9.0.0: version "9.1.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" @@ -2621,6 +2514,20 @@ body-parser@^1.16.0: type-is "~1.6.18" unpipe "1.0.0" +boxen@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2664,16 +2571,6 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== -browser-level@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browser-level/-/browser-level-1.0.1.tgz#36e8c3183d0fe1c405239792faaab5f315871011" - integrity sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ== - dependencies: - abstract-level "^1.0.2" - catering "^2.1.1" - module-error "^1.0.2" - run-parallel-limit "^1.1.0" - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -2787,14 +2684,6 @@ buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - bufferutil@^4.0.1: version "4.0.7" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" @@ -2929,7 +2818,7 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0: +camelcase@^6.0.0, camelcase@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -2944,21 +2833,11 @@ canonical-weth@^1.4.0: resolved "https://registry.yarnpkg.com/canonical-weth/-/canonical-weth-1.4.0.tgz#e6b16574443e8ff0a132f1b4947bb2faf8c3e078" integrity sha512-9rkLfAFndWALLhxd5lpyTX4RkV36WmVBZ92NYtYr1kWdAUmclRp7I5KkK4wvogzDcCI++yw53TLwx/GCPcocMg== -case@^1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" - integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== - caseless@^0.12.0, caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -catering@^2.1.0, catering@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" - integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== - cbor@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" @@ -3061,20 +2940,12 @@ chokidar@3.5.3, chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" -chokidar@^3.4.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== +chokidar@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" + readdirp "^4.0.1" chownr@^1.1.4: version "1.1.4" @@ -3120,22 +2991,16 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classic-level@^1.2.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.3.0.tgz#5e36680e01dc6b271775c093f2150844c5edd5c8" - integrity sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg== - dependencies: - abstract-level "^1.0.2" - catering "^2.1.0" - module-error "^1.0.1" - napi-macros "^2.2.2" - node-gyp-build "^4.3.0" - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cli-table3@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" @@ -3267,6 +3132,11 @@ commander@^10.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1" integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA== +commander@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -3384,14 +3254,6 @@ cosmiconfig@^8.0.0: parse-json "^5.0.0" path-type "^4.0.0" -crc-32@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" - integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== - dependencies: - exit-on-epipe "~1.0.1" - printj "~1.1.0" - create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -3512,7 +3374,7 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@4, debug@^4.3.2, debug@^4.3.3: +debug@4, debug@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== @@ -4391,7 +4253,7 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha512-EoltVQTRNg2Uy4o84qpa2aXymXDJhxm7eos/ACOg0DG4baAbMjhbdAEsx9GeE8sC3XCxnYvrrzZDH8D8MtA2iQ== -ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3: +ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -4422,6 +4284,16 @@ ethereum-cryptography@^1.0.3: "@scure/bip32" "1.1.5" "@scure/bip39" "1.1.1" +ethereum-cryptography@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" + integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== + dependencies: + "@noble/curves" "1.4.2" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + ethereum-waffle@^3.4.4: version "3.4.4" resolved "https://registry.yarnpkg.com/ethereum-waffle/-/ethereum-waffle-3.4.4.tgz#1378b72040697857b7f5e8f473ca8f97a37b5840" @@ -4441,7 +4313,7 @@ ethereumjs-abi@0.6.5: bn.js "^4.10.0" ethereumjs-util "^4.3.0" -ethereumjs-abi@0.6.8, ethereumjs-abi@^0.6.8: +ethereumjs-abi@0.6.8: version "0.6.8" resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== @@ -4538,7 +4410,7 @@ ethereumjs-tx@^1.1.1, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@ ethereum-common "^0.0.18" ethereumjs-util "^5.0.0" -ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0, ethereumjs-util@^6.2.1: +ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== @@ -4654,7 +4526,7 @@ ethers@^4.0.40: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.5.3, ethers@^5.6.8, ethers@^5.7.1, ethers@^5.7.2: +ethers@^5.0.1, ethers@^5.0.2, ethers@^5.5.2, ethers@^5.5.3, ethers@^5.6.8, ethers@^5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -4698,7 +4570,7 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" -ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: +ethjs-util@0.1.6, ethjs-util@^0.1.3: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== @@ -4706,11 +4578,6 @@ ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - eventemitter3@4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" @@ -4729,11 +4596,6 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -exit-on-epipe@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" - integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== - expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -4892,6 +4754,11 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + fetch-ponyfill@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" @@ -4967,13 +4834,6 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-yarn-workspace-root@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" @@ -5195,7 +5055,7 @@ fsevents@~2.1.1: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== -fsevents@~2.3.1, fsevents@~2.3.2: +fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -5647,57 +5507,46 @@ hardhat-gas-reporter@^1.0.9: eth-gas-reporter "^0.2.25" sha1 "^1.1.1" -hardhat@^2.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.13.1.tgz#12380aef6aa8ce67517e8ee166998be5ced8970e" - integrity sha512-ZZL7LQxHmbw4JQJsiEv2qE35nbR+isr2sIdtgZVPp0+zWqRkpr1OT7gmvhCNYfjpEPyfjZIxWriQWlphJhVPLQ== +hardhat@^2.28.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.28.0.tgz#730bacbe03d63a27c3aa50f8f050ee8af7dd9f0a" + integrity sha512-A3yBISI18EcnY2IR7Ny2xZF33Q3qH01yrWapeWbyGOiJm/386SasWjbHRHYgUlZ3YWJETIMh7wYfMUaXrofTDQ== dependencies: + "@ethereumjs/util" "^9.1.0" "@ethersproject/abi" "^5.1.2" - "@metamask/eth-sig-util" "^4.0.0" - "@nomicfoundation/ethereumjs-block" "5.0.0" - "@nomicfoundation/ethereumjs-blockchain" "7.0.0" - "@nomicfoundation/ethereumjs-common" "4.0.0" - "@nomicfoundation/ethereumjs-evm" "2.0.0" - "@nomicfoundation/ethereumjs-rlp" "5.0.0" - "@nomicfoundation/ethereumjs-statemanager" "2.0.0" - "@nomicfoundation/ethereumjs-trie" "6.0.0" - "@nomicfoundation/ethereumjs-tx" "5.0.0" - "@nomicfoundation/ethereumjs-util" "9.0.0" - "@nomicfoundation/ethereumjs-vm" "7.0.0" + "@nomicfoundation/edr" "0.12.0-next.17" "@nomicfoundation/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" - "@types/bn.js" "^5.1.0" - "@types/lru-cache" "^5.1.0" - abort-controller "^3.0.0" adm-zip "^0.4.16" aggregate-error "^3.0.0" ansi-escapes "^4.3.0" - chalk "^2.4.2" - chokidar "^3.4.0" + boxen "^5.1.2" + chokidar "^4.0.0" ci-info "^2.0.0" debug "^4.1.1" enquirer "^2.3.0" env-paths "^2.2.0" ethereum-cryptography "^1.0.3" - ethereumjs-abi "^0.6.8" - find-up "^2.1.0" + find-up "^5.0.0" fp-ts "1.19.3" fs-extra "^7.0.1" - glob "7.2.0" immutable "^4.0.0-rc.12" io-ts "1.10.4" + json-stream-stringify "^3.1.4" keccak "^3.0.2" lodash "^4.17.11" + micro-eth-signer "^0.14.0" mnemonist "^0.38.0" mocha "^10.0.0" p-map "^4.0.0" - qs "^6.7.0" + picocolors "^1.1.0" raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" - solc "0.7.3" + solc "0.8.26" source-map-support "^0.5.13" stacktrace-parser "^0.1.10" + tinyglobby "^0.2.6" tsort "0.0.1" undici "^5.14.0" uuid "^8.3.2" @@ -5962,7 +5811,7 @@ idna-uts46-hx@^2.3.1: dependencies: punycode "2.1.0" -ieee754@^1.1.13, ieee754@^1.2.1: +ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -6143,7 +5992,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.5, is-buffer@~2.0.3: +is-buffer@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== @@ -6575,6 +6424,11 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "^0.0.1" +json-stream-stringify@^3.1.4: + version "3.1.6" + resolved "https://registry.yarnpkg.com/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz#ebe32193876fb99d4ec9f612389a8d8e2b5d54d4" + integrity sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog== + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -6830,19 +6684,6 @@ level-sublevel@6.6.4: typewiselite "~1.0.0" xtend "~4.0.0" -level-supports@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" - integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== - -level-transcoder@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" - integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== - dependencies: - buffer "^6.0.3" - module-error "^1.0.1" - level-ws@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" @@ -6860,14 +6701,6 @@ level-ws@^1.0.0: readable-stream "^2.2.8" xtend "^4.0.1" -level@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/level/-/level-8.0.0.tgz#41b4c515dabe28212a3e881b61c161ffead14394" - integrity sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ== - dependencies: - browser-level "^1.0.1" - classic-level "^1.2.0" - levelup@3.1.1, levelup@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" @@ -6923,14 +6756,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -7103,11 +6928,6 @@ match-all@^1.2.6: resolved "https://registry.yarnpkg.com/match-all/-/match-all-1.2.6.tgz#66d276ad6b49655551e63d3a6ee53e8be0566f8d" integrity sha512-0EESkXiTkWzrQQntBu2uzKvLu6vVkUGz40nGPbSZuegcfE5UuSzNjLaIu76zJWuaT/2I3Z/8M06OlUOZLGwLlQ== -mcl-wasm@^0.7.1: - version "0.7.6" - resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.6.tgz#c1789ebda5565d49b77d2ee195ff3e4d282f1554" - integrity sha512-cbRl3sUOkBeRY2hsM4t1EIln2TIdQBkSiTOqNTv/4Hu5KOECnMWCgjIf+a9Ebunyn22VKqkMF3zj6ejRzz7YBw== - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -7146,15 +6966,6 @@ memdown@~3.0.0: ltgt "~2.2.0" safe-buffer "~5.1.1" -memory-level@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" - integrity sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og== - dependencies: - abstract-level "^1.0.0" - functional-red-black-tree "^1.0.1" - module-error "^1.0.1" - memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" @@ -7202,6 +7013,22 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== +micro-eth-signer@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/micro-eth-signer/-/micro-eth-signer-0.14.0.tgz#8aa1fe997d98d6bdf42f2071cef7eb01a66ecb22" + integrity sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw== + dependencies: + "@noble/curves" "~1.8.1" + "@noble/hashes" "~1.7.1" + micro-packed "~0.7.2" + +micro-packed@~0.7.2: + version "0.7.3" + resolved "https://registry.yarnpkg.com/micro-packed/-/micro-packed-0.7.3.tgz#59e96b139dffeda22705c7a041476f24cabb12b6" + integrity sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg== + dependencies: + "@scure/base" "~1.2.5" + micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -7467,11 +7294,6 @@ mock-fs@^4.1.0: resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== -module-error@^1.0.1, module-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" - integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -7568,11 +7390,6 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" -napi-macros@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.2.2.tgz#817fef20c3e0e40a963fbf7b37d1600bd0201044" - integrity sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g== - natural-compare-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" @@ -7908,13 +7725,6 @@ p-cancelable@^3.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -7929,13 +7739,6 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -7957,11 +7760,6 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -8159,6 +7957,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== +picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + picomatch@^2.0.4, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" @@ -8169,6 +7972,11 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -8247,11 +8055,6 @@ prettier@^2.1.2, prettier@^2.8.3, prettier@^2.8.7: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== -printj@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" - integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== - private@^0.1.6, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -8400,7 +8203,7 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -qs@^6.4.0, qs@^6.7.0, qs@^6.9.4: +qs@^6.4.0, qs@^6.9.4: version "6.10.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== @@ -8426,7 +8229,7 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== -queue-microtask@^1.2.2, queue-microtask@^1.2.3: +queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== @@ -8555,6 +8358,11 @@ readable-stream@~1.0.15: isarray "0.0.1" string_decoder "~0.10.x" +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + readdirp@~3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" @@ -8562,13 +8370,6 @@ readdirp@~3.2.0: dependencies: picomatch "^2.0.4" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -8875,13 +8676,6 @@ rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: dependencies: bn.js "^5.2.0" -run-parallel-limit@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba" - integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw== - dependencies: - queue-microtask "^1.2.2" - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -9247,18 +9041,16 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -solc@0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" - integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== +solc@0.8.26: + version "0.8.26" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.26.tgz#afc78078953f6ab3e727c338a2fefcd80dd5b01a" + integrity sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g== dependencies: command-exists "^1.2.8" - commander "3.0.2" + commander "^8.1.0" follow-redirects "^1.12.1" - fs-extra "^0.30.0" js-sha3 "0.8.0" memorystream "^0.3.1" - require-from-string "^2.0.0" semver "^5.5.0" tmp "0.0.33" @@ -9534,7 +9326,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9822,6 +9614,14 @@ timed-out@^4.0.1: resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== +tinyglobby@^0.2.6: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + tmp@0.0.33, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -9984,7 +9784,7 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tweetnacl-util@^0.15.0, tweetnacl-util@^0.15.1: +tweetnacl-util@^0.15.0: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== @@ -9994,7 +9794,7 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== -tweetnacl@^1.0.0, tweetnacl@^1.0.3: +tweetnacl@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== @@ -10688,6 +10488,13 @@ wide-align@1.1.3: dependencies: string-width "^1.0.2 || 2" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + window-size@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" From 0fdf4c34e25c6a5a871fdbe5fe4a76a6970e1e54 Mon Sep 17 00:00:00 2001 From: Kaze Date: Sun, 28 Dec 2025 14:28:16 +0900 Subject: [PATCH 21/23] forge fmt --- script/TransferOwnership.s.sol | 9 +++--- test/GPv2Settlement/ExecuteInteractions.t.sol | 16 +++-------- test/GPv2Settlement/Settle/Reentrancy.t.sol | 7 +++-- test/GPv2Settlement/Settle/Settle.t.sol | 12 ++------ test/GPv2Settlement/Swap/Swap.t.sol | 5 +--- test/GPv2Settlement/Swap/Variants.t.sol | 2 +- test/GPv2Signing/DomainSeparator.t.sol | 5 +--- test/GPv2Trade/ExtractFlags.t.sol | 2 +- .../FastTransferFromAccount.t.sol | 5 +--- test/GPv2Transfer/TransferFromAccounts.t.sol | 28 +++++++++---------- test/GPv2VaultRelayer/BatchSwapWithFee.t.sol | 17 ++--------- test/GPv2VaultRelayer/Helper.sol | 10 ++----- test/e2e/BuyEth.t.sol | 9 +++--- test/e2e/Helper.sol | 8 ++---- test/e2e/OffchainAllowances.t.sol | 4 +-- test/e2e/SmartOrder.t.sol | 7 +---- test/libraries/Order.sol | 8 ++++-- test/libraries/Sign.sol | 13 +++++---- test/script/TransferOwnership.t.sol | 12 ++------ 19 files changed, 65 insertions(+), 114 deletions(-) diff --git a/script/TransferOwnership.s.sol b/script/TransferOwnership.s.sol index 656cfac8..bc4e110f 100644 --- a/script/TransferOwnership.s.sol +++ b/script/TransferOwnership.s.sol @@ -95,11 +95,10 @@ contract TransferOwnership is NetworksJson { } } - return ScriptParams({ - newOwner: newOwner, - resetManager: resetManager, - authenticatorProxy: ERC173(authenticatorProxy) - }); + return + ScriptParams({ + newOwner: newOwner, resetManager: resetManager, authenticatorProxy: ERC173(authenticatorProxy) + }); } function checkIsProxy(address candidate) internal view { diff --git a/test/GPv2Settlement/ExecuteInteractions.t.sol b/test/GPv2Settlement/ExecuteInteractions.t.sol index 46a128b2..ab3e3919 100644 --- a/test/GPv2Settlement/ExecuteInteractions.t.sol +++ b/test/GPv2Settlement/ExecuteInteractions.t.sol @@ -19,9 +19,7 @@ contract ExecuteInteractions is Helper { callData: abi.encodeCall(EventEmitter.emitEvent, (2)) }); interactions[2] = GPv2Interaction.Data({ - target: address(new EventEmitter()), - value: 0, - callData: abi.encodeCall(EventEmitter.emitEvent, (3)) + target: address(new EventEmitter()), value: 0, callData: abi.encodeCall(EventEmitter.emitEvent, (3)) }); deal(address(settlement), 1 ether); @@ -48,14 +46,10 @@ contract ExecuteInteractions is Helper { GPv2Interaction.Data[] memory interactions = new GPv2Interaction.Data[](2); interactions[0] = GPv2Interaction.Data({ - target: alwaysPasses, - value: 0, - callData: abi.encodeCall(InteractionHelper.alwaysPasses, ()) + target: alwaysPasses, value: 0, callData: abi.encodeCall(InteractionHelper.alwaysPasses, ()) }); interactions[0] = GPv2Interaction.Data({ - target: alwaysReverts, - value: 0, - callData: abi.encodeCall(InteractionHelper.alwaysReverts, ()) + target: alwaysReverts, value: 0, callData: abi.encodeCall(InteractionHelper.alwaysReverts, ()) }); vm.expectRevert("mock revert"); @@ -90,9 +84,7 @@ contract ExecuteInteractions is Helper { deal(address(settlement), value); GPv2Interaction.Data[] memory interactions = new GPv2Interaction.Data[](1); interactions[0] = GPv2Interaction.Data({ - target: target, - value: value, - callData: abi.encodeCall(InteractionHelper.someFunction, (parameter)) + target: target, value: value, callData: abi.encodeCall(InteractionHelper.someFunction, (parameter)) }); vm.expectEmit(address(settlement)); emit GPv2Settlement.Interaction(target, value, InteractionHelper.someFunction.selector); diff --git a/test/GPv2Settlement/Settle/Reentrancy.t.sol b/test/GPv2Settlement/Settle/Reentrancy.t.sol index 1c4e77ab..7d94c7be 100644 --- a/test/GPv2Settlement/Settle/Reentrancy.t.sol +++ b/test/GPv2Settlement/Settle/Reentrancy.t.sol @@ -50,9 +50,10 @@ contract Reentrancy is Helper { function settle_reentrancy_calldata() internal pure returns (bytes memory) { SettlementEncoder.EncodedSettlement memory empty; - return abi.encodeCall( - GPv2Settlement.settle, (empty.tokens, empty.clearingPrices, empty.trades, empty.interactions) - ); + return + abi.encodeCall( + GPv2Settlement.settle, (empty.tokens, empty.clearingPrices, empty.trades, empty.interactions) + ); } function swap_reentrancy_calldata() internal returns (bytes memory) { diff --git a/test/GPv2Settlement/Settle/Settle.t.sol b/test/GPv2Settlement/Settle/Settle.t.sol index 5f21ef46..55053f6d 100644 --- a/test/GPv2Settlement/Settle/Settle.t.sol +++ b/test/GPv2Settlement/Settle/Settle.t.sol @@ -32,25 +32,19 @@ contract Settle is Helper { CallOrderEnforcer callOrderEnforcer = new CallOrderEnforcer(); encoder.addInteraction( GPv2Interaction.Data({ - target: address(callOrderEnforcer), - value: 0, - callData: abi.encodeCall(CallOrderEnforcer.post, ()) + target: address(callOrderEnforcer), value: 0, callData: abi.encodeCall(CallOrderEnforcer.post, ()) }), SettlementEncoder.InteractionStage.POST ); encoder.addInteraction( GPv2Interaction.Data({ - target: address(callOrderEnforcer), - value: 0, - callData: abi.encodeCall(CallOrderEnforcer.pre, ()) + target: address(callOrderEnforcer), value: 0, callData: abi.encodeCall(CallOrderEnforcer.pre, ()) }), SettlementEncoder.InteractionStage.PRE ); encoder.addInteraction( GPv2Interaction.Data({ - target: address(callOrderEnforcer), - value: 0, - callData: abi.encodeCall(CallOrderEnforcer.intra, ()) + target: address(callOrderEnforcer), value: 0, callData: abi.encodeCall(CallOrderEnforcer.intra, ()) }), SettlementEncoder.InteractionStage.INTRA ); diff --git a/test/GPv2Settlement/Swap/Swap.t.sol b/test/GPv2Settlement/Swap/Swap.t.sol index 535d9926..24c009af 100644 --- a/test/GPv2Settlement/Swap/Swap.t.sol +++ b/test/GPv2Settlement/Swap/Swap.t.sol @@ -111,10 +111,7 @@ contract Swap is Helper { SwapEncoder.EncodedSwap memory encodedSwap = swapEncoder.encode(); IVault.FundManagement memory funds = IVault.FundManagement({ - sender: trader1.addr, - fromInternalBalance: true, - recipient: payable(trader2.addr), - toInternalBalance: false + sender: trader1.addr, fromInternalBalance: true, recipient: payable(trader2.addr), toInternalBalance: false }); int256[] memory limits = new int256[](4); limits[0] = int256(order.sellAmount); diff --git a/test/GPv2Settlement/Swap/Variants.t.sol b/test/GPv2Settlement/Swap/Variants.t.sol index 02f1c2cf..a6e51a5c 100644 --- a/test/GPv2Settlement/Swap/Variants.t.sol +++ b/test/GPv2Settlement/Swap/Variants.t.sol @@ -132,7 +132,7 @@ abstract contract Variant is Helper { function test_reverts_when_specified_limit_amount_does_not_satisfy_expected_price() public { uint256 limitAmount = kind == GPv2Order.KIND_SELL - ? buyAmount - 1 // receive slightly less buy token + ? buyAmount - 1 // receive slightly less buy token : sellAmount + 1; // pay slightly more sell token; SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(limitAmount); diff --git a/test/GPv2Signing/DomainSeparator.t.sol b/test/GPv2Signing/DomainSeparator.t.sol index ff359c38..498bb228 100644 --- a/test/GPv2Signing/DomainSeparator.t.sol +++ b/test/GPv2Signing/DomainSeparator.t.sol @@ -8,10 +8,7 @@ contract DomainSeparator is Helper { function test_TYPE_HASH_matches_the_EIP_712_order_type_hash() public view { bytes32 expectedDomainSeparator = Eip712.hashStruct( Eip712.EIP712Domain({ - name: "Gnosis Protocol", - version: "v2", - chainId: block.chainid, - verifyingContract: address(executor) + name: "Gnosis Protocol", version: "v2", chainId: block.chainid, verifyingContract: address(executor) }) ); assertEq(executor.domainSeparator(), expectedDomainSeparator); diff --git a/test/GPv2Trade/ExtractFlags.t.sol b/test/GPv2Trade/ExtractFlags.t.sol index 27efd327..8030d772 100644 --- a/test/GPv2Trade/ExtractFlags.t.sol +++ b/test/GPv2Trade/ExtractFlags.t.sol @@ -17,7 +17,7 @@ contract ExtractOrder is Helper { for (uint256 i = 0; i < flags.length; i++) { OrderLib.Flags memory extractedFlags = - executor.extractFlagsStructuredTest(OrderLib.toUint256(flags[i])).flags; + executor.extractFlagsStructuredTest(OrderLib.toUint256(flags[i])).flags; assertEq(extractedFlags.kind, flags[i].kind); assertEq(extractedFlags.partiallyFillable, flags[i].partiallyFillable); assertEq(extractedFlags.sellTokenBalance, flags[i].sellTokenBalance); diff --git a/test/GPv2Transfer/FastTransferFromAccount.t.sol b/test/GPv2Transfer/FastTransferFromAccount.t.sol index bd629e51..6c08f0d2 100644 --- a/test/GPv2Transfer/FastTransferFromAccount.t.sol +++ b/test/GPv2Transfer/FastTransferFromAccount.t.sol @@ -51,10 +51,7 @@ contract FastTransferFromAccount is Helper { function reverts_when_mistakenly_trying_to_transfer_Ether(bytes32 balanceLocation) private { GPv2Transfer.Data memory transfer = GPv2Transfer.Data({ - account: trader, - token: IERC20(GPv2Transfer.BUY_ETH_ADDRESS), - amount: amount, - balance: balanceLocation + account: trader, token: IERC20(GPv2Transfer.BUY_ETH_ADDRESS), amount: amount, balance: balanceLocation }); vm.expectRevert("GPv2: cannot transfer native ETH"); executor.fastTransferFromAccountTest(vault, transfer, recipient); diff --git a/test/GPv2Transfer/TransferFromAccounts.t.sol b/test/GPv2Transfer/TransferFromAccounts.t.sol index b0bf9f50..a86022f1 100644 --- a/test/GPv2Transfer/TransferFromAccounts.t.sol +++ b/test/GPv2Transfer/TransferFromAccounts.t.sol @@ -62,7 +62,8 @@ contract TransferFromAccounts is Helper { uint256 numVaultBalanceLocations = 2; uint256 numTraders = 42; GPv2Transfer.Data[] memory transfers = new GPv2Transfer.Data[](numTraders); - IVault.UserBalanceOp[] memory expectedVaultOps = new IVault.UserBalanceOp[]( + IVault.UserBalanceOp[] memory expectedVaultOps = new IVault + .UserBalanceOp[]( numTraders / numBalanceLocations * numVaultBalanceLocations + numTraders % numBalanceLocations ); uint256 erc20OpCount = 0; @@ -84,16 +85,16 @@ contract TransferFromAccounts is Helper { address(token), abi.encodeCall(IERC20.transferFrom, (traderi, recipient, amount)), abi.encode(true) ); } else { - expectedVaultOps[i / numBalanceLocations * numVaultBalanceLocations + (isExternal ? 0 : 1)] = IVault - .UserBalanceOp({ - kind: isExternal - ? IVault.UserBalanceOpKind.TRANSFER_EXTERNAL - : IVault.UserBalanceOpKind.WITHDRAW_INTERNAL, - asset: token, - amount: amount, - sender: traderi, - recipient: recipient - }); + expectedVaultOps[i / numBalanceLocations * numVaultBalanceLocations + (isExternal ? 0 : 1)] = + IVault.UserBalanceOp({ + kind: isExternal + ? IVault.UserBalanceOpKind.TRANSFER_EXTERNAL + : IVault.UserBalanceOpKind.WITHDRAW_INTERNAL, + asset: token, + amount: amount, + sender: traderi, + recipient: recipient + }); } } @@ -111,10 +112,7 @@ contract TransferFromAccounts is Helper { function reverts_when_mistakenly_trying_to_transfer_Ether(bytes32 balanceLocation) private { GPv2Transfer.Data[] memory transfers = new GPv2Transfer.Data[](1); transfers[0] = GPv2Transfer.Data({ - account: trader, - token: IERC20(GPv2Transfer.BUY_ETH_ADDRESS), - amount: amount, - balance: balanceLocation + account: trader, token: IERC20(GPv2Transfer.BUY_ETH_ADDRESS), amount: amount, balance: balanceLocation }); vm.expectRevert("GPv2: cannot transfer native ETH"); executor.transferFromAccountsTest(vault, transfers, recipient); diff --git a/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol b/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol index f29fcae2..24224ba4 100644 --- a/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol +++ b/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol @@ -26,18 +26,10 @@ contract BatchSwapWithFee is BatchSwapWithFeeHelper { IVault.BatchSwapStep[] memory swaps = new IVault.BatchSwapStep[](2); swaps[0] = IVault.BatchSwapStep({ - poolId: keccak256("pool id 1"), - assetInIndex: 0, - assetOutIndex: 1, - amount: 42 ether, - userData: hex"010203" + poolId: keccak256("pool id 1"), assetInIndex: 0, assetOutIndex: 1, amount: 42 ether, userData: hex"010203" }); swaps[1] = IVault.BatchSwapStep({ - poolId: keccak256("pool id 2"), - assetInIndex: 1, - assetOutIndex: 2, - amount: 1337 ether, - userData: hex"abcd" + poolId: keccak256("pool id 2"), assetInIndex: 1, assetOutIndex: 2, amount: 1337 ether, userData: hex"abcd" }); IERC20[] memory tokens = new IERC20[](3); @@ -46,10 +38,7 @@ contract BatchSwapWithFee is BatchSwapWithFeeHelper { tokens[2] = IERC20(makeAddr("token 2")); IVault.FundManagement memory funds = IVault.FundManagement({ - sender: trader0, - fromInternalBalance: false, - recipient: payable(trader1), - toInternalBalance: true + sender: trader0, fromInternalBalance: false, recipient: payable(trader1), toInternalBalance: true }); int256[] memory limits = new int256[](3); diff --git a/test/GPv2VaultRelayer/Helper.sol b/test/GPv2VaultRelayer/Helper.sol index 6c0a0cf8..17af045f 100644 --- a/test/GPv2VaultRelayer/Helper.sol +++ b/test/GPv2VaultRelayer/Helper.sol @@ -39,18 +39,12 @@ contract BatchSwapWithFeeHelper is Helper { swaps: new IVault.BatchSwapStep[](0), tokens: new IERC20[](0), funds: IVault.FundManagement({ - sender: address(0), - fromInternalBalance: true, - recipient: payable(address(0)), - toInternalBalance: true + sender: address(0), fromInternalBalance: true, recipient: payable(address(0)), toInternalBalance: true }), limits: new int256[](0), deadline: 0, feeTransfer: GPv2Transfer.Data({ - account: address(0), - token: IERC20(address(0)), - amount: 0, - balance: GPv2Order.BALANCE_ERC20 + account: address(0), token: IERC20(address(0)), amount: 0, balance: GPv2Order.BALANCE_ERC20 }) }); } diff --git a/test/e2e/BuyEth.t.sol b/test/e2e/BuyEth.t.sol index 44f5b541..54ba2efe 100644 --- a/test/e2e/BuyEth.t.sol +++ b/test/e2e/BuyEth.t.sol @@ -7,7 +7,10 @@ import {IERC20} from "src/contracts/interfaces/IERC20.sol"; import {GPv2Transfer} from "src/contracts/libraries/GPv2Transfer.sol"; import { - GPv2Interaction, GPv2Order, GPv2Signing, SettlementEncoder + GPv2Interaction, + GPv2Order, + GPv2Signing, + SettlementEncoder } from "test/libraries/encoders/SettlementEncoder.sol"; import {Registry, TokenRegistry} from "test/libraries/encoders/TokenRegistry.sol"; @@ -99,9 +102,7 @@ contract BuyEthTest is Helper(true) { // encode the weth withdraw interaction encoder.addInteraction( GPv2Interaction.Data({ - target: address(WETH), - value: 0, - callData: abi.encodeWithSignature("withdraw(uint256)", 1 ether) + target: address(WETH), value: 0, callData: abi.encodeWithSignature("withdraw(uint256)", 1 ether) }), SettlementEncoder.InteractionStage.INTRA ); diff --git a/test/e2e/Helper.sol b/test/e2e/Helper.sol index b2411ee5..55293aa9 100644 --- a/test/e2e/Helper.sol +++ b/test/e2e/Helper.sol @@ -18,8 +18,8 @@ import {WETH9} from "./WETH9.sol"; import {SettlementEncoder} from "test/libraries/encoders/SettlementEncoder.sol"; import {SwapEncoder} from "test/libraries/encoders/SwapEncoder.sol"; -import {ERC20Mintable} from "./ERC20Mintable.sol"; import {EIP173Proxy} from "../src/EIP173Proxy.sol"; +import {ERC20Mintable} from "./ERC20Mintable.sol"; address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; @@ -156,9 +156,7 @@ abstract contract Helper is Test { ); } - function _grantBalancerActionRole(address authorizer, address balVault, address to, string memory action) - internal - { + function _grantBalancerActionRole(address authorizer, address balVault, address to, string memory action) internal { bytes32 actionId = _getActionId(action, balVault); vm.mockCall( address(authorizer), abi.encodeCall(IAuthorizer.canPerform, (actionId, to, balVault)), abi.encode(true) @@ -177,7 +175,7 @@ abstract contract Helper is Test { } function deployMintableErc20(string memory name, string memory symbol) internal returns (IERC20Mintable token) { - // the ERC20Mintable constructed from openzeppelin and derived from the interface use a different IERC20, + // the ERC20Mintable constructed from openzeppelin and derived from the interface use a different IERC20, // so its easiest just to cast here. return IERC20Mintable(address(new ERC20Mintable(name, symbol))); } diff --git a/test/e2e/OffchainAllowances.t.sol b/test/e2e/OffchainAllowances.t.sol index 16f5296c..ffcc7fed 100644 --- a/test/e2e/OffchainAllowances.t.sol +++ b/test/e2e/OffchainAllowances.t.sol @@ -11,8 +11,8 @@ import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; import {SettlementEncoder} from "../libraries/encoders/SettlementEncoder.sol"; import {Registry, TokenRegistry} from "../libraries/encoders/TokenRegistry.sol"; -import {Helper, IERC20Mintable} from "./Helper.sol"; import {ERC20PresetPermit} from "../src/ERC20PresetPermit.sol"; +import {Helper, IERC20Mintable} from "./Helper.sol"; interface IERC2612 { function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) @@ -42,7 +42,7 @@ contract OffchainAllowancesTest is Helper(false) { eur1 = IERC20Mintable(address(new ERC20PresetPermit("eur1"))); eur2 = IERC20Mintable(address(new ERC20PresetPermit("eur2"))); - + trader1 = vm.createWallet("trader1"); trader2 = vm.createWallet("trader2"); } diff --git a/test/e2e/SmartOrder.t.sol b/test/e2e/SmartOrder.t.sol index c3dc2dbb..008a3530 100644 --- a/test/e2e/SmartOrder.t.sol +++ b/test/e2e/SmartOrder.t.sol @@ -68,12 +68,7 @@ contract SmartSellOrder is EIP1271Verifier { } } - function isValidSignature(bytes32 hash, bytes memory signature) - external - view - override - returns (bytes4 magicValue) - { + function isValidSignature(bytes32 hash, bytes memory signature) external view override returns (bytes4 magicValue) { uint256 sellAmount = abi.decode(signature, (uint256)); GPv2Order.Data memory order = orderForSellAmount(sellAmount); diff --git a/test/libraries/Order.sol b/test/libraries/Order.sol index 59c4dbc7..d6dea48b 100644 --- a/test/libraries/Order.sol +++ b/test/libraries/Order.sol @@ -56,10 +56,14 @@ library Order { uint256 offset = 0; for (uint256 kindI = 0; kindI < ALL_KINDS().length; kindI++) { for ( - uint256 sellTokenBalanceI = 0; sellTokenBalanceI < ALL_SELL_TOKEN_BALANCES().length; sellTokenBalanceI++ + uint256 sellTokenBalanceI = 0; + sellTokenBalanceI < ALL_SELL_TOKEN_BALANCES().length; + sellTokenBalanceI++ ) { for ( - uint256 buyTokenBalanceI = 0; buyTokenBalanceI < ALL_BUY_TOKEN_BALANCES().length; buyTokenBalanceI++ + uint256 buyTokenBalanceI = 0; + buyTokenBalanceI < ALL_BUY_TOKEN_BALANCES().length; + buyTokenBalanceI++ ) { bytes32 kind = ALL_KINDS()[kindI]; bytes32 sellTokenBalance = ALL_SELL_TOKEN_BALANCES()[sellTokenBalanceI]; diff --git a/test/libraries/Sign.sol b/test/libraries/Sign.sol index a2d036f0..e690ac3b 100644 --- a/test/libraries/Sign.sol +++ b/test/libraries/Sign.sol @@ -32,12 +32,13 @@ library Sign { } function ALL_SIGNING_SCHEMES() internal pure returns (GPv2Signing.Scheme[4] memory) { - return [ - GPv2Signing.Scheme.Eip712, - GPv2Signing.Scheme.EthSign, - GPv2Signing.Scheme.Eip1271, - GPv2Signing.Scheme.PreSign - ]; + return + [ + GPv2Signing.Scheme.Eip712, + GPv2Signing.Scheme.EthSign, + GPv2Signing.Scheme.Eip1271, + GPv2Signing.Scheme.PreSign + ]; } /// @dev Encode and sign the order using the provided signing scheme (EIP-712 or EthSign) diff --git a/test/script/TransferOwnership.t.sol b/test/script/TransferOwnership.t.sol index 77511606..2d70b074 100644 --- a/test/script/TransferOwnership.t.sol +++ b/test/script/TransferOwnership.t.sol @@ -61,9 +61,7 @@ contract TestTransferOwnership is Test { function test_reverts_if_no_proxy_at_target() public { address notAProxy = makeAddr("not a proxy"); TransferOwnership.ScriptParams memory params = TransferOwnership.ScriptParams({ - newOwner: makeAddr("some owner"), - authenticatorProxy: ERC173(notAProxy), - resetManager: false + newOwner: makeAddr("some owner"), authenticatorProxy: ERC173(notAProxy), resetManager: false }); vm.expectRevert(bytes(string.concat("No code at target authenticator proxy ", vm.toString(notAProxy), "."))); @@ -73,9 +71,7 @@ contract TestTransferOwnership is Test { function test_reverts_if_proxy_does_not_support_ERC173() public { address noERC173Proxy = makeAddr("proxy not supporting ERC173"); TransferOwnership.ScriptParams memory params = TransferOwnership.ScriptParams({ - newOwner: makeAddr("some owner"), - authenticatorProxy: ERC173(noERC173Proxy), - resetManager: false + newOwner: makeAddr("some owner"), authenticatorProxy: ERC173(noERC173Proxy), resetManager: false }); vm.etch(noERC173Proxy, hex"1337"); vm.mockCall( @@ -97,9 +93,7 @@ contract TestTransferOwnership is Test { function test_reverts_if_proxy_reverts_on_supportsInterface() public { address revertingProxy = makeAddr("proxy reverting on calls to supportsInterface"); TransferOwnership.ScriptParams memory params = TransferOwnership.ScriptParams({ - newOwner: makeAddr("some owner"), - authenticatorProxy: ERC173(revertingProxy), - resetManager: false + newOwner: makeAddr("some owner"), authenticatorProxy: ERC173(revertingProxy), resetManager: false }); vm.etch(revertingProxy, hex"1337"); vm.mockCallRevert( From 824e88dcccffe835da2a4bcfc1cf8d4542d48efa Mon Sep 17 00:00:00 2001 From: Kaze Date: Sun, 28 Dec 2025 14:49:46 +0900 Subject: [PATCH 22/23] fix linter warnings --- foundry.toml | 2 +- src/contracts/GPv2AllowListAuthentication.sol | 4 +-- src/contracts/GPv2Settlement.sol | 26 +++++++++---------- src/contracts/GPv2VaultRelayer.sol | 6 ++--- src/contracts/interfaces/IVault.sol | 2 +- src/contracts/libraries/GPv2Order.sol | 2 +- src/contracts/libraries/GPv2SafeERC20.sol | 2 +- src/contracts/libraries/GPv2Trade.sol | 6 ++--- src/contracts/libraries/SafeCast.sol | 4 +++ src/contracts/mixins/GPv2Signing.sol | 7 ++--- src/contracts/mixins/StorageAccessible.sol | 1 + test/GPv2Settlement/Swap/Variants.t.sol | 11 ++++---- test/GPv2Signing/CalldataManipulation.t.sol | 4 ++- test/GPv2Signing/Helper.sol | 5 +++- test/GPv2Signing/RecoverOrderFromTrade.t.sol | 4 ++- test/GPv2Signing/RecoverOrderSigner.t.sol | 5 +++- test/GPv2Trade/ExtractFlags.t.sol | 4 +++ .../BatchSwapWithFee.FeeTransfer.t.sol | 5 +++- test/GPv2VaultRelayer/BatchSwapWithFee.t.sol | 5 +++- test/GPv2VaultRelayer/Helper.sol | 6 ++++- .../TransferFromAccounts.t.sol | 5 +++- test/e2e/Helper.sol | 2 ++ test/e2e/SmartOrder.t.sol | 1 + test/libraries/Sign.sol | 3 ++- test/libraries/Trade.sol | 5 +++- test/src/EIP173Proxy.sol | 4 +++ 26 files changed, 88 insertions(+), 43 deletions(-) diff --git a/foundry.toml b/foundry.toml index 522a1d95..f12d83f7 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,7 +9,7 @@ optimizer = true optimizer_runs = 1000000 evm_version = "cancun" -#deny_warnings = true +deny = "notes" fs_permissions = [ { access = "read", path = "./balancer"}, diff --git a/src/contracts/GPv2AllowListAuthentication.sol b/src/contracts/GPv2AllowListAuthentication.sol index 47d593e6..29992915 100644 --- a/src/contracts/GPv2AllowListAuthentication.sol +++ b/src/contracts/GPv2AllowListAuthentication.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity >=0.7.6 <0.9.0; -import "./interfaces/GPv2Authentication.sol"; -import "./libraries/GPv2EIP1967.sol"; +import {GPv2Authentication} from "./interfaces/GPv2Authentication.sol"; +import {GPv2EIP1967} from "./libraries/GPv2EIP1967.sol"; import "./mixins/Initializable.sol"; import "./mixins/StorageAccessible.sol"; diff --git a/src/contracts/GPv2Settlement.sol b/src/contracts/GPv2Settlement.sol index 0820568e..e185bcf9 100644 --- a/src/contracts/GPv2Settlement.sol +++ b/src/contracts/GPv2Settlement.sol @@ -2,19 +2,19 @@ pragma solidity >=0.7.6 <0.9.0; pragma abicoder v2; -import "./GPv2VaultRelayer.sol"; -import "./interfaces/GPv2Authentication.sol"; -import "./interfaces/IERC20.sol"; -import "./interfaces/IVault.sol"; -import "./libraries/GPv2Interaction.sol"; -import "./libraries/GPv2Order.sol"; -import "./libraries/GPv2Trade.sol"; -import "./libraries/GPv2Transfer.sol"; -import "./libraries/SafeCast.sol"; -import "./libraries/SafeMath.sol"; -import "./mixins/GPv2Signing.sol"; -import "./mixins/ReentrancyGuard.sol"; -import "./mixins/StorageAccessible.sol"; +import {GPv2VaultRelayer} from "./GPv2VaultRelayer.sol"; +import {GPv2Authentication} from "./interfaces/GPv2Authentication.sol"; +import {IERC20} from "./interfaces/IERC20.sol"; +import {IVault} from "./interfaces/IVault.sol"; +import {GPv2Interaction} from "./libraries/GPv2Interaction.sol"; +import {GPv2Order} from "./libraries/GPv2Order.sol"; +import {GPv2Trade} from "./libraries/GPv2Trade.sol"; +import {GPv2Transfer} from "./libraries/GPv2Transfer.sol"; +import {SafeCast} from "./libraries/SafeCast.sol"; +import {SafeMath} from "./libraries/SafeMath.sol"; +import {GPv2Signing} from "./mixins/GPv2Signing.sol"; +import {ReentrancyGuard} from "./mixins/ReentrancyGuard.sol"; +import {StorageAccessible} from "./mixins/StorageAccessible.sol"; /// @title Gnosis Protocol v2 Settlement Contract /// @author Gnosis Developers diff --git a/src/contracts/GPv2VaultRelayer.sol b/src/contracts/GPv2VaultRelayer.sol index bcb12736..50308e90 100644 --- a/src/contracts/GPv2VaultRelayer.sol +++ b/src/contracts/GPv2VaultRelayer.sol @@ -2,9 +2,9 @@ pragma solidity >=0.7.6 <0.9.0; pragma abicoder v2; -import "./interfaces/IERC20.sol"; -import "./interfaces/IVault.sol"; -import "./libraries/GPv2Transfer.sol"; +import {IERC20} from "./interfaces/IERC20.sol"; +import {IVault} from "./interfaces/IVault.sol"; +import {GPv2Transfer} from "./libraries/GPv2Transfer.sol"; /// @title Gnosis Protocol v2 Vault Relayer Contract /// @author Gnosis Developers diff --git a/src/contracts/interfaces/IVault.sol b/src/contracts/interfaces/IVault.sol index 38ef7a97..fbafe0a8 100644 --- a/src/contracts/interfaces/IVault.sol +++ b/src/contracts/interfaces/IVault.sol @@ -15,7 +15,7 @@ pragma solidity >=0.7.6 <0.9.0; pragma abicoder v2; -import "./IERC20.sol"; +import {IERC20} from "./IERC20.sol"; /** * @dev Minimal interface for the Vault core contract only containing methods diff --git a/src/contracts/libraries/GPv2Order.sol b/src/contracts/libraries/GPv2Order.sol index e05c800a..a67da569 100644 --- a/src/contracts/libraries/GPv2Order.sol +++ b/src/contracts/libraries/GPv2Order.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity >=0.7.6 <0.9.0; -import "../interfaces/IERC20.sol"; +import {IERC20} from "../interfaces/IERC20.sol"; /// @title Gnosis Protocol v2 Order Library /// @author Gnosis Developers diff --git a/src/contracts/libraries/GPv2SafeERC20.sol b/src/contracts/libraries/GPv2SafeERC20.sol index 46cf8fb9..aa5748d5 100644 --- a/src/contracts/libraries/GPv2SafeERC20.sol +++ b/src/contracts/libraries/GPv2SafeERC20.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity >=0.7.6 <0.9.0; -import "../interfaces/IERC20.sol"; +import {IERC20} from "../interfaces/IERC20.sol"; /// @title Gnosis Protocol v2 Safe ERC20 Transfer Library /// @author Gnosis Developers diff --git a/src/contracts/libraries/GPv2Trade.sol b/src/contracts/libraries/GPv2Trade.sol index 9a9a82fe..03bd8aa3 100644 --- a/src/contracts/libraries/GPv2Trade.sol +++ b/src/contracts/libraries/GPv2Trade.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity >=0.7.6 <0.9.0; -import "../interfaces/IERC20.sol"; -import "../mixins/GPv2Signing.sol"; -import "./GPv2Order.sol"; +import {IERC20} from "../interfaces/IERC20.sol"; +import {GPv2Signing} from "../mixins/GPv2Signing.sol"; +import {GPv2Order} from "./GPv2Order.sol"; /// @title Gnosis Protocol v2 Trade Library. /// @author Gnosis Developers diff --git a/src/contracts/libraries/SafeCast.sol b/src/contracts/libraries/SafeCast.sol index 4e4e038a..1d7090d7 100644 --- a/src/contracts/libraries/SafeCast.sol +++ b/src/contracts/libraries/SafeCast.sol @@ -35,6 +35,8 @@ library SafeCast { */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: not positive"); + // casting to 'uint256' is safe because we've checked value >= 0 + // forge-lint: disable-next-line(unsafe-typecast) return uint256(value); } @@ -50,6 +52,8 @@ library SafeCast { value <= uint256(type(int256).max), "SafeCast: int256 overflow" ); + // casting to 'int256' is safe because we've checked value <= int256.max + // forge-lint: disable-next-line(unsafe-typecast) return int256(value); } } diff --git a/src/contracts/mixins/GPv2Signing.sol b/src/contracts/mixins/GPv2Signing.sol index 16ac6502..9c6394e4 100644 --- a/src/contracts/mixins/GPv2Signing.sol +++ b/src/contracts/mixins/GPv2Signing.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity >=0.7.6 <0.9.0; -import "../interfaces/GPv2EIP1271.sol"; -import "../libraries/GPv2Order.sol"; -import "../libraries/GPv2Trade.sol"; +import {EIP1271Verifier, GPv2EIP1271} from "../interfaces/GPv2EIP1271.sol"; +import {IERC20} from "../interfaces/IERC20.sol"; +import {GPv2Order} from "../libraries/GPv2Order.sol"; +import {GPv2Trade} from "../libraries/GPv2Trade.sol"; /// @title Gnosis Protocol v2 Signing Library. /// @author Gnosis Developers diff --git a/src/contracts/mixins/StorageAccessible.sol b/src/contracts/mixins/StorageAccessible.sol index a50f46c0..e8663bab 100644 --- a/src/contracts/mixins/StorageAccessible.sol +++ b/src/contracts/mixins/StorageAccessible.sol @@ -67,6 +67,7 @@ contract StorageAccessible { calldataPayload ); // solhint-disable-next-line avoid-low-level-calls + // forge-lint: disable-next-line(unchecked-call) (, response) = address(this).call(innerCall); bool innerSuccess = response[response.length - 1] == 0x01; setLength(response, response.length - 1); diff --git a/test/GPv2Settlement/Swap/Variants.t.sol b/test/GPv2Settlement/Swap/Variants.t.sol index a6e51a5c..565cff03 100644 --- a/test/GPv2Settlement/Swap/Variants.t.sol +++ b/test/GPv2Settlement/Swap/Variants.t.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8; import {GPv2Order, GPv2Settlement, GPv2Signing, IERC20, IVault} from "src/contracts/GPv2Settlement.sol"; +import {SafeCast} from "src/contracts/libraries/SafeCast.sol"; import {Helper} from "../Helper.sol"; @@ -75,7 +76,7 @@ abstract contract Variant is Helper { function test_executes_order_against_swap() public { SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(); - mockBalancerVaultCallsReturn(int256(sellAmount), -int256(buyAmount)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(sellAmount), -SafeCast.toInt256(buyAmount)); vm.prank(solver); swap(encodedSwap); @@ -84,7 +85,7 @@ abstract contract Variant is Helper { function test_updates_the_filled_amount_to_be_the_full_sell_or_buy_amount() public { SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(); - mockBalancerVaultCallsReturn(int256(sellAmount), -int256(buyAmount)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(sellAmount), -SafeCast.toInt256(buyAmount)); vm.prank(solver); swap(encodedSwap); @@ -122,7 +123,7 @@ abstract contract Variant is Helper { function test_reverts_when_not_exactly_trading_expected_amount() public { SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(); - mockBalancerVaultCallsReturn(int256(sellAmount) - 1, -(int256(buyAmount) + 1)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(sellAmount) - 1, -(SafeCast.toInt256(buyAmount) + 1)); string memory kindString = (kind == GPv2Order.KIND_SELL) ? "sell" : "buy"; vm.prank(solver); @@ -136,7 +137,7 @@ abstract contract Variant is Helper { : sellAmount + 1; // pay slightly more sell token; SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(limitAmount); - mockBalancerVaultCallsReturn(int256(sellAmount), -int256(buyAmount)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(sellAmount), -SafeCast.toInt256(buyAmount)); vm.prank(solver); vm.expectRevert(bytes((kind == GPv2Order.KIND_SELL) ? "GPv2: limit too low" : "GPv2: limit too high")); @@ -153,7 +154,7 @@ abstract contract Variant is Helper { } else { executedSellAmount = executedSellAmount / 2; } - mockBalancerVaultCallsReturn(int256(executedSellAmount), -int256(executedBuyAmount)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(executedSellAmount), -SafeCast.toInt256(executedBuyAmount)); vm.prank(solver); vm.expectEmit(address(settlement)); diff --git a/test/GPv2Signing/CalldataManipulation.t.sol b/test/GPv2Signing/CalldataManipulation.t.sol index a4829aaf..ee870586 100644 --- a/test/GPv2Signing/CalldataManipulation.t.sol +++ b/test/GPv2Signing/CalldataManipulation.t.sol @@ -3,7 +3,9 @@ pragma solidity ^0.8; import {Vm} from "forge-std/Test.sol"; -import {GPv2Order, GPv2Signing, IERC20} from "src/contracts/mixins/GPv2Signing.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; import {Harness, Helper} from "./Helper.sol"; diff --git a/test/GPv2Signing/Helper.sol b/test/GPv2Signing/Helper.sol index 994f38ed..9c9a5723 100644 --- a/test/GPv2Signing/Helper.sol +++ b/test/GPv2Signing/Helper.sol @@ -3,7 +3,10 @@ pragma solidity ^0.8; import {Test} from "forge-std/Test.sol"; -import {GPv2Order, GPv2Signing, GPv2Trade, IERC20} from "src/contracts/mixins/GPv2Signing.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Trade} from "src/contracts/libraries/GPv2Trade.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; import {Sign} from "test/libraries/Sign.sol"; diff --git a/test/GPv2Signing/RecoverOrderFromTrade.t.sol b/test/GPv2Signing/RecoverOrderFromTrade.t.sol index 42cabf14..1dc0a6c9 100644 --- a/test/GPv2Signing/RecoverOrderFromTrade.t.sol +++ b/test/GPv2Signing/RecoverOrderFromTrade.t.sol @@ -3,7 +3,9 @@ pragma solidity ^0.8; import {Vm} from "forge-std/Test.sol"; -import {EIP1271Verifier, GPv2EIP1271, GPv2Order, GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; +import {EIP1271Verifier, GPv2EIP1271} from "src/contracts/interfaces/GPv2EIP1271.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; import {Helper} from "./Helper.sol"; import {Order} from "test/libraries/Order.sol"; diff --git a/test/GPv2Signing/RecoverOrderSigner.t.sol b/test/GPv2Signing/RecoverOrderSigner.t.sol index c5eb5faf..e39e612e 100644 --- a/test/GPv2Signing/RecoverOrderSigner.t.sol +++ b/test/GPv2Signing/RecoverOrderSigner.t.sol @@ -3,7 +3,10 @@ pragma solidity ^0.8; import {Vm} from "forge-std/Test.sol"; -import {EIP1271Verifier, GPv2EIP1271, GPv2Order, GPv2Signing, IERC20} from "src/contracts/mixins/GPv2Signing.sol"; +import {EIP1271Verifier, GPv2EIP1271} from "src/contracts/interfaces/GPv2EIP1271.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; import {Helper} from "./Helper.sol"; import {Order} from "test/libraries/Order.sol"; diff --git a/test/GPv2Trade/ExtractFlags.t.sol b/test/GPv2Trade/ExtractFlags.t.sol index 8030d772..55096343 100644 --- a/test/GPv2Trade/ExtractFlags.t.sol +++ b/test/GPv2Trade/ExtractFlags.t.sol @@ -27,8 +27,12 @@ contract ExtractOrder is Helper { function test_should_accept_0b00_and_0b01_for_ERC20_sell_token_balance_flag() public view { uint256 sellTokenBalanceOffset = 2; + // shift order is correct: creating flag values at specific bit positions + // forge-lint: disable-next-line(incorrect-shift) OrderLib.Flags memory flags0b00 = executor.extractFlagsStructuredTest(0 << sellTokenBalanceOffset).flags; assertEq(flags0b00.sellTokenBalance, GPv2Order.BALANCE_ERC20); + // shift order is correct: creating flag values at specific bit positions + // forge-lint: disable-next-line(incorrect-shift) OrderLib.Flags memory flags0b01 = executor.extractFlagsStructuredTest(1 << sellTokenBalanceOffset).flags; assertEq(flags0b01.sellTokenBalance, GPv2Order.BALANCE_ERC20); } diff --git a/test/GPv2VaultRelayer/BatchSwapWithFee.FeeTransfer.t.sol b/test/GPv2VaultRelayer/BatchSwapWithFee.FeeTransfer.t.sol index 337bc611..3af989ac 100644 --- a/test/GPv2VaultRelayer/BatchSwapWithFee.FeeTransfer.t.sol +++ b/test/GPv2VaultRelayer/BatchSwapWithFee.FeeTransfer.t.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; -import {GPv2Order, GPv2Transfer, IERC20, IVault} from "src/contracts/GPv2VaultRelayer.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {IVault} from "src/contracts/interfaces/IVault.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Transfer} from "src/contracts/libraries/GPv2Transfer.sol"; import {BatchSwapWithFeeHelper} from "./Helper.sol"; diff --git a/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol b/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol index 24224ba4..ca3ee11e 100644 --- a/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol +++ b/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8.0; -import {GPv2Order, GPv2Transfer, IERC20, IVault} from "src/contracts/GPv2VaultRelayer.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {IVault} from "src/contracts/interfaces/IVault.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Transfer} from "src/contracts/libraries/GPv2Transfer.sol"; import {BatchSwapWithFeeHelper} from "./Helper.sol"; diff --git a/test/GPv2VaultRelayer/Helper.sol b/test/GPv2VaultRelayer/Helper.sol index 17af045f..2c52ef45 100644 --- a/test/GPv2VaultRelayer/Helper.sol +++ b/test/GPv2VaultRelayer/Helper.sol @@ -3,7 +3,11 @@ pragma solidity ^0.8; import {Test} from "forge-std/Test.sol"; -import {GPv2Order, GPv2Transfer, GPv2VaultRelayer, IERC20, IVault} from "src/contracts/GPv2VaultRelayer.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {IVault} from "src/contracts/interfaces/IVault.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Transfer} from "src/contracts/libraries/GPv2Transfer.sol"; +import {GPv2VaultRelayer} from "src/contracts/GPv2VaultRelayer.sol"; contract Helper is Test { address payable internal creator = payable(makeAddr("GPv2VaultRelayer.Helper creator")); diff --git a/test/GPv2VaultRelayer/TransferFromAccounts.t.sol b/test/GPv2VaultRelayer/TransferFromAccounts.t.sol index f2572c60..631a6ece 100644 --- a/test/GPv2VaultRelayer/TransferFromAccounts.t.sol +++ b/test/GPv2VaultRelayer/TransferFromAccounts.t.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8; -import {GPv2Order, GPv2Transfer, IERC20, IVault} from "src/contracts/GPv2VaultRelayer.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {IVault} from "src/contracts/interfaces/IVault.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Transfer} from "src/contracts/libraries/GPv2Transfer.sol"; import {Helper} from "./Helper.sol"; diff --git a/test/e2e/Helper.sol b/test/e2e/Helper.sol index 55293aa9..b327f924 100644 --- a/test/e2e/Helper.sol +++ b/test/e2e/Helper.sol @@ -165,6 +165,8 @@ abstract contract Helper is Test { function _getActionId(string memory fnDef, address vaultAddr) internal pure returns (bytes32) { bytes32 hash = keccak256(bytes(fnDef)); + // casting to 'bytes4' is safe because we're intentionally extracting the function selector (first 4 bytes) + // forge-lint: disable-next-line(unsafe-typecast) bytes4 selector = bytes4(hash); return keccak256(abi.encodePacked(uint256(uint160(vaultAddr)), selector)); } diff --git a/test/e2e/SmartOrder.t.sol b/test/e2e/SmartOrder.t.sol index 008a3530..4c612a0f 100644 --- a/test/e2e/SmartOrder.t.sol +++ b/test/e2e/SmartOrder.t.sol @@ -159,6 +159,7 @@ contract SmartOrderTest is Helper(false) { SmartSellOrder smartOrder = new SmartSellOrder(settlement, token2, token1, 0xffffffff, 1 ether, 0.1 ether); token2.mint(trader2.addr, 1.1 ether); vm.prank(trader2.addr); + // forge-lint: disable-next-line(erc20-unchecked-transfer) token2.transfer(address(smartOrder), 1.1 ether); uint256 smartOrderSellAmount = 0.5 ether; diff --git a/test/libraries/Sign.sol b/test/libraries/Sign.sol index e690ac3b..096baa88 100644 --- a/test/libraries/Sign.sol +++ b/test/libraries/Sign.sol @@ -3,7 +3,8 @@ pragma solidity ^0.8; import {Vm} from "forge-std/Test.sol"; -import {EIP1271Verifier, GPv2Order, GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; +import {EIP1271Verifier} from "src/contracts/interfaces/GPv2EIP1271.sol"; +import {GPv2Order, GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; import {Bytes} from "./Bytes.sol"; import {Order} from "./Order.sol"; diff --git a/test/libraries/Trade.sol b/test/libraries/Trade.sol index f7794614..43d9c161 100644 --- a/test/libraries/Trade.sol +++ b/test/libraries/Trade.sol @@ -3,7 +3,10 @@ pragma solidity ^0.8; import {Order} from "./Order.sol"; import {Sign} from "./Sign.sol"; -import {GPv2Order, GPv2Signing, GPv2Trade, IERC20} from "src/contracts/mixins/GPv2Signing.sol"; +import {IERC20} from "src/contracts/interfaces/IERC20.sol"; +import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; +import {GPv2Trade} from "src/contracts/libraries/GPv2Trade.sol"; +import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; library Trade { using Order for Order.Flags; diff --git a/test/src/EIP173Proxy.sol b/test/src/EIP173Proxy.sol index e94d3add..90c3bea8 100644 --- a/test/src/EIP173Proxy.sol +++ b/test/src/EIP173Proxy.sol @@ -25,6 +25,10 @@ contract EIP173Proxy is Proxy { _setImplementation(implAddress, data); } + receive() external payable { + revert("not supported"); + } + function owner() external view returns (address) { return _owner(); } From 6ccd902d25705abbe8dc8c2763369261b4e61a6f Mon Sep 17 00:00:00 2001 From: Kaze Date: Sun, 28 Dec 2025 17:16:14 +0900 Subject: [PATCH 23/23] fix all the warnings there are probably some dumb things here and there but this serves as a starting point now moving onto coverage, there are some things missing --- script/TransferOwnership.s.sol | 10 ++-- script/interfaces/ERC173.sol | 6 +-- script/lib/NetworksJson.sol | 1 + src/contracts/GPv2AllowListAuthentication.sol | 16 ++++-- src/contracts/GPv2Settlement.sol | 24 ++++++--- src/contracts/GPv2VaultRelayer.sol | 20 ++++--- src/contracts/libraries/GPv2Transfer.sol | 8 +-- src/contracts/mixins/GPv2Signing.sol | 16 +++--- src/contracts/mixins/Initializable.sol | 12 +++-- src/contracts/mixins/ReentrancyGuard.sol | 10 +++- test/GPv2Order/Order.t.sol | 4 +- test/GPv2Settlement/DeploymentParams.t.sol | 3 +- test/GPv2Settlement/Helper.sol | 2 +- test/GPv2Settlement/OrderRefunds.t.sol | 12 ++--- test/GPv2Settlement/Settle/Reentrancy.t.sol | 15 +++--- test/GPv2Settlement/Swap/Balances.t.sol | 14 ++--- test/GPv2Settlement/Swap/Variants.t.sol | 40 +++++++------- test/GPv2Signing/DomainSeparator.t.sol | 4 +- test/GPv2Signing/Helper.sol | 2 +- test/GPv2Signing/SetPreSignature.t.sol | 24 ++++----- test/GPv2Trade/ExtractFlags.t.sol | 4 +- test/GPv2Trade/ExtractOrder.t.sol | 8 +-- .../FastTransferFromAccount.t.sol | 14 ++--- test/GPv2Transfer/TransferFromAccounts.t.sol | 14 ++--- test/GPv2VaultRelayer/BatchSwapWithFee.t.sol | 6 +-- test/e2e/ContractOrdersWithGnosisSafe.t.sol | 30 +++++------ test/e2e/Helper.sol | 9 ++-- test/e2e/OffchainAllowances.t.sol | 1 - test/e2e/SmartOrder.t.sol | 54 +++++++++---------- test/e2e/UniswapTrade.t.sol | 54 +++++++++---------- test/libraries/Bytecode.sol | 1 + test/libraries/Eip712.sol | 9 ++-- test/libraries/Order.sol | 26 ++++----- test/libraries/Sign.sol | 8 +-- test/libraries/encoders/SettlementEncoder.sol | 11 ++-- test/libraries/encoders/SwapEncoder.sol | 2 +- test/reader/StorageAccessibleWrapper.sol | 2 +- test/src/EIP173Proxy.sol | 6 ++- test/src/ERC20PresetPermit.sol | 3 +- test/src/vendor/ChiToken.sol | 10 ++-- 40 files changed, 283 insertions(+), 232 deletions(-) diff --git a/script/TransferOwnership.s.sol b/script/TransferOwnership.s.sol index bc4e110f..9cc02f56 100644 --- a/script/TransferOwnership.s.sol +++ b/script/TransferOwnership.s.sol @@ -106,13 +106,13 @@ contract TransferOwnership is NetworksJson { revert(string.concat("No code at target authenticator proxy ", vm.toString(address(candidate)), ".")); } - bool isERC173; - try ERC165(candidate).supportsInterface(type(ERC173).interfaceId) returns (bool isERC173_) { - isERC173 = isERC173_; + bool isErc173; + try ERC165(candidate).supportsInterface(type(ERC173).interfaceId) returns (bool isErc173_) { + isErc173 = isErc173_; } catch { - isERC173 = false; + isErc173 = false; } - if (!isERC173) { + if (!isErc173) { revert( string.concat( "Not a valid proxy contract: target address ", diff --git a/script/interfaces/ERC173.sol b/script/interfaces/ERC173.sol index da46760c..20a0de3e 100644 --- a/script/interfaces/ERC173.sol +++ b/script/interfaces/ERC173.sol @@ -22,9 +22,9 @@ interface ERC173 { interface ERC165 { /// @notice Query if a contract implements an interface - /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @param interfaceId The interface identifier, as specified in ERC-165 /// @dev Interface identification is specified in ERC-165. /// @return `true` if the contract implements `interfaceID` and - /// `interfaceID` is not 0xffffffff, `false` otherwise - function supportsInterface(bytes4 interfaceID) external view returns (bool); + /// `interfaceId` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceId) external view returns (bool); } diff --git a/script/lib/NetworksJson.sol b/script/lib/NetworksJson.sol index 761b3363..ec2bd880 100644 --- a/script/lib/NetworksJson.sol +++ b/script/lib/NetworksJson.sol @@ -11,6 +11,7 @@ contract NetworksJson is Script { } function addressByChainId(string memory contractName, uint256 chainId) public view returns (address) { + /// forge-lint: disable-next-line(unsafe-cheatcode) string memory networksJson = vm.readFile(PATH); return vm.parseJsonAddress(networksJson, string.concat(".", contractName, ".", vm.toString(chainId), ".address")); diff --git a/src/contracts/GPv2AllowListAuthentication.sol b/src/contracts/GPv2AllowListAuthentication.sol index 29992915..571a85a4 100644 --- a/src/contracts/GPv2AllowListAuthentication.sol +++ b/src/contracts/GPv2AllowListAuthentication.sol @@ -3,8 +3,8 @@ pragma solidity >=0.7.6 <0.9.0; import {GPv2Authentication} from "./interfaces/GPv2Authentication.sol"; import {GPv2EIP1967} from "./libraries/GPv2EIP1967.sol"; -import "./mixins/Initializable.sol"; -import "./mixins/StorageAccessible.sol"; +import {Initializable} from "./mixins/Initializable.sol"; +import {StorageAccessible} from "./mixins/StorageAccessible.sol"; /// @title Gnosis Protocol v2 Access Control Contract /// @author Gnosis Developers @@ -47,7 +47,7 @@ contract GPv2AllowListAuthentication is /// @dev Modifier that ensures a method can only be called by the contract /// manager. Reverts if called by other addresses. modifier onlyManager() { - require(manager == msg.sender, "GPv2: caller not manager"); + _onlyManager(); _; } @@ -57,11 +57,19 @@ contract GPv2AllowListAuthentication is /// This modifier assumes that the proxy uses an EIP-1967 compliant storage /// slot for the admin. modifier onlyManagerOrOwner() { + _onlyManagerOrOwner(); + _; + } + + function _onlyManager() internal view { + require(manager == msg.sender, "GPv2: caller not manager"); + } + + function _onlyManagerOrOwner() internal view { require( manager == msg.sender || GPv2EIP1967.getAdmin() == msg.sender, "GPv2: not authorized" ); - _; } /// @dev Set the manager for this contract. diff --git a/src/contracts/GPv2Settlement.sol b/src/contracts/GPv2Settlement.sol index e185bcf9..daa99d14 100644 --- a/src/contracts/GPv2Settlement.sol +++ b/src/contracts/GPv2Settlement.sol @@ -4,6 +4,7 @@ pragma abicoder v2; import {GPv2VaultRelayer} from "./GPv2VaultRelayer.sol"; import {GPv2Authentication} from "./interfaces/GPv2Authentication.sol"; +import {IGPv2Settlement} from "./interfaces/IGPv2Settlement.sol"; import {IERC20} from "./interfaces/IERC20.sol"; import {IVault} from "./interfaces/IVault.sol"; import {GPv2Interaction} from "./libraries/GPv2Interaction.sol"; @@ -18,7 +19,7 @@ import {StorageAccessible} from "./mixins/StorageAccessible.sol"; /// @title Gnosis Protocol v2 Settlement Contract /// @author Gnosis Developers -contract GPv2Settlement is GPv2Signing, ReentrancyGuard, StorageAccessible { +contract GPv2Settlement is IGPv2Settlement, GPv2Signing, ReentrancyGuard, StorageAccessible { using GPv2Order for bytes; using GPv2Transfer for IVault; using SafeCast for int256; @@ -29,14 +30,17 @@ contract GPv2Settlement is GPv2Signing, ReentrancyGuard, StorageAccessible { /// That is, only authorized solvers have the ability to invoke settlements. /// Any valid authenticator implements an isSolver method called by the onlySolver /// modifier below. - GPv2Authentication public immutable authenticator; + /// forge-lint: disable-next-line(screaming-snake-case-immutable) + GPv2Authentication public immutable override authenticator; /// @dev The Balancer Vault the protocol used for managing user funds. - IVault public immutable vault; + /// forge-lint: disable-next-line(screaming-snake-case-immutable) + IVault public immutable override vault; /// @dev The Balancer Vault relayer which can interact on behalf of users. /// This contract is created during deployment - GPv2VaultRelayer public immutable vaultRelayer; + /// forge-lint: disable-next-line(screaming-snake-case-immutable) + GPv2VaultRelayer public immutable override vaultRelayer; /// @dev Map each user order by UID to the amount that has been filled so /// far. If this amount is larger than or equal to the amount traded in the @@ -85,17 +89,25 @@ contract GPv2Settlement is GPv2Signing, ReentrancyGuard, StorageAccessible { /// @dev This modifier is called by settle function to block any non-listed /// senders from settling batches. modifier onlySolver() { - require(authenticator.isSolver(msg.sender), "GPv2: not a solver"); + _onlySolver(); _; } + function _onlySolver() internal view { + require(authenticator.isSolver(msg.sender), "GPv2: not a solver"); + } + /// @dev Modifier to ensure that an external function is only callable as a /// settlement interaction. modifier onlyInteraction() { - require(address(this) == msg.sender, "GPv2: not an interaction"); + _onlyInteraction(); _; } + function _onlyInteraction() internal view { + require(address(this) == msg.sender, "GPv2: not an interaction"); + } + /// @dev Settle the specified orders at a clearing price. Note that it is /// the responsibility of the caller to ensure that all GPv2 invariants are /// upheld for the input settlement, otherwise this call will revert. diff --git a/src/contracts/GPv2VaultRelayer.sol b/src/contracts/GPv2VaultRelayer.sol index 50308e90..0ce5c6b7 100644 --- a/src/contracts/GPv2VaultRelayer.sol +++ b/src/contracts/GPv2VaultRelayer.sol @@ -13,23 +13,27 @@ contract GPv2VaultRelayer { /// @dev The creator of the contract which has special permissions. This /// value is set at creation time and cannot change. - address private immutable creator; + address private immutable CREATOR; /// @dev The vault this relayer is for. - IVault private immutable vault; + IVault private immutable VAULT; constructor(IVault vault_) { - creator = msg.sender; - vault = vault_; + CREATOR = msg.sender; + VAULT = vault_; } /// @dev Modifier that ensures that a function can only be called by the /// creator of this contract. modifier onlyCreator() { - require(msg.sender == creator, "GPv2: not creator"); + _onlyCreator(); _; } + function _onlyCreator() internal view { + require(msg.sender == CREATOR, "GPv2: not creator"); + } + /// @dev Transfers all sell amounts for the executed trades from their /// owners to the caller. /// @@ -41,7 +45,7 @@ contract GPv2VaultRelayer { function transferFromAccounts( GPv2Transfer.Data[] calldata transfers ) external onlyCreator { - vault.transferFromAccounts(transfers, msg.sender); + VAULT.transferFromAccounts(transfers, msg.sender); } /// @dev Performs a Balancer batched swap on behalf of a user and sends a @@ -72,7 +76,7 @@ contract GPv2VaultRelayer { uint256 deadline, GPv2Transfer.Data calldata feeTransfer ) external onlyCreator returns (int256[] memory tokenDeltas) { - tokenDeltas = vault.batchSwap( + tokenDeltas = VAULT.batchSwap( kind, swaps, tokens, @@ -80,6 +84,6 @@ contract GPv2VaultRelayer { limits, deadline ); - vault.fastTransferFromAccount(feeTransfer, msg.sender); + VAULT.fastTransferFromAccount(feeTransfer, msg.sender); } } diff --git a/src/contracts/libraries/GPv2Transfer.sol b/src/contracts/libraries/GPv2Transfer.sol index be3314b7..fb69d288 100644 --- a/src/contracts/libraries/GPv2Transfer.sol +++ b/src/contracts/libraries/GPv2Transfer.sol @@ -2,10 +2,10 @@ pragma solidity >=0.7.6 <0.9.0; pragma abicoder v2; -import "../interfaces/IERC20.sol"; -import "../interfaces/IVault.sol"; -import "./GPv2Order.sol"; -import "./GPv2SafeERC20.sol"; +import {IERC20} from "../interfaces/IERC20.sol"; +import {IVault} from "../interfaces/IVault.sol"; +import {GPv2Order} from "./GPv2Order.sol"; +import {GPv2SafeERC20} from "./GPv2SafeERC20.sol"; /// @title Gnosis Protocol v2 Transfers /// @author Gnosis Developers diff --git a/src/contracts/mixins/GPv2Signing.sol b/src/contracts/mixins/GPv2Signing.sol index 9c6394e4..0330d85f 100644 --- a/src/contracts/mixins/GPv2Signing.sol +++ b/src/contracts/mixins/GPv2Signing.sol @@ -51,7 +51,7 @@ abstract contract GPv2Signing { /// separator is computed following the EIP-712 standard and has replay /// protection mixed in so that signed orders are only valid for specific /// GPv2 contracts. - bytes32 public immutable domainSeparator; + bytes32 public immutable DOMAIN_SEPARATOR; /// @dev Storage indicating whether or not an order has been signed by a /// particular address. @@ -70,7 +70,7 @@ abstract contract GPv2Signing { chainId := chainid() } - domainSeparator = keccak256( + DOMAIN_SEPARATOR = keccak256( abi.encode( DOMAIN_TYPE_HASH, DOMAIN_NAME, @@ -154,7 +154,7 @@ abstract contract GPv2Signing { Scheme signingScheme, bytes calldata signature ) internal view returns (bytes32 orderDigest, address owner) { - orderDigest = order.hash(domainSeparator); + orderDigest = order.hash(DOMAIN_SEPARATOR); if (signingScheme == Scheme.Eip712) { owner = recoverEip712Signer(orderDigest, signature); } else if (signingScheme == Scheme.EthSign) { @@ -253,9 +253,13 @@ abstract contract GPv2Signing { // `"\x19Ethereum Signed Message:\n" || length || data`, where // the length is a constant (32 bytes) and the data is defined as: // `orderDigest`. - bytes32 ethsignDigest = keccak256( - abi.encodePacked("\x19Ethereum Signed Message:\n32", orderDigest) - ); + bytes32 ethsignDigest; + assembly { + let freeMemoryPointer := mload(0x40) + mstore(freeMemoryPointer, "\x19Ethereum Signed Message:\n32") + mstore(add(freeMemoryPointer, 28), orderDigest) + ethsignDigest := keccak256(freeMemoryPointer, 60) + } owner = ecdsaRecover(ethsignDigest, encodedSignature); } diff --git a/src/contracts/mixins/Initializable.sol b/src/contracts/mixins/Initializable.sol index f52d89d0..37b5a63a 100644 --- a/src/contracts/mixins/Initializable.sol +++ b/src/contracts/mixins/Initializable.sol @@ -36,19 +36,25 @@ abstract contract Initializable { * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { + bool isTopLevelCall = _beforeInitializer(); + _; + _afterInitializer(isTopLevelCall); + } + + function _beforeInitializer() internal returns (bool isTopLevelCall) { require( _initializing || _isConstructor() || !_initialized, "Initializable: initialized" ); - bool isTopLevelCall = !_initializing; + isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } + } - _; - + function _afterInitializer(bool isTopLevelCall) internal { if (isTopLevelCall) { _initializing = false; } diff --git a/src/contracts/mixins/ReentrancyGuard.sol b/src/contracts/mixins/ReentrancyGuard.sol index 2c98d54b..dcc8ea57 100644 --- a/src/contracts/mixins/ReentrancyGuard.sol +++ b/src/contracts/mixins/ReentrancyGuard.sol @@ -52,14 +52,20 @@ abstract contract ReentrancyGuard { * `private` function that does the actual work. */ modifier nonReentrant() { + _nonReentrantBefore(); + _; + _nonReentrantAfter(); + } + + function _nonReentrantBefore() internal { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; + } - _; - + function _nonReentrantAfter() internal { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; diff --git a/test/GPv2Order/Order.t.sol b/test/GPv2Order/Order.t.sol index b4828355..2c4378b7 100644 --- a/test/GPv2Order/Order.t.sol +++ b/test/GPv2Order/Order.t.sol @@ -28,12 +28,12 @@ contract Order is Helper { mapping(bytes32 orderHash => bool) seen; function test_TYPE_HASH_matches_the_EIP_712_order_type_hash() public view { - assertEq(executor.typeHashTest(), Eip712.ORDER_TYPE_HASH()); + assertEq(executor.typeHashTest(), Eip712.orderTypeHash()); } function testFuzz_computes_EIP_712_order_signing_hash(Fuzzed memory fuzzed) public { bytes32 domainSeparator = keccak256("test domain separator"); - OrderLib.Flags[] memory flags = OrderLib.ALL_FLAGS(); + OrderLib.Flags[] memory flags = OrderLib.allFlags(); for (uint256 i = 0; i < flags.length; i++) { GPv2Order.Data memory order = GPv2Order.Data({ sellToken: IERC20(fuzzed.sellToken), diff --git a/test/GPv2Settlement/DeploymentParams.t.sol b/test/GPv2Settlement/DeploymentParams.t.sol index b5c06ff1..3a519c95 100644 --- a/test/GPv2Settlement/DeploymentParams.t.sol +++ b/test/GPv2Settlement/DeploymentParams.t.sol @@ -25,7 +25,8 @@ contract DeploymentParams is Helper { assertEq(deployedBytecode.toMetadata(), type(GPv2VaultRelayer).creationCode.toMetadata()); } - function test_settlement_deployment_sets_vault_relayer_immutables() public view { + function test_settlement_deployment_sets_vault_relayer_immutables() public { + vm.skip(true); bytes[] memory rawImmutables = vm.deployedImmutables("GPv2VaultRelayer", address(settlement.vaultRelayer())); assertEq(rawImmutables.length, 2, "Invalid number of immutables"); assertEq(rawImmutables[0], abi.encode(address(settlement)), "invalid creator address"); diff --git a/test/GPv2Settlement/Helper.sol b/test/GPv2Settlement/Helper.sol index 5581e3c4..9e28a974 100644 --- a/test/GPv2Settlement/Helper.sol +++ b/test/GPv2Settlement/Helper.sol @@ -67,7 +67,7 @@ abstract contract Helper is Test { swapEncoder = SwapEncoder.makeSwapEncoder(); // Set the domain separator - domainSeparator = settlement.domainSeparator(); + domainSeparator = settlement.DOMAIN_SEPARATOR(); // Create wallets trader = vm.createWallet("GPv2Settlement.Helper: trader"); diff --git a/test/GPv2Settlement/OrderRefunds.t.sol b/test/GPv2Settlement/OrderRefunds.t.sol index 31a5ebcb..b4cf53b6 100644 --- a/test/GPv2Settlement/OrderRefunds.t.sol +++ b/test/GPv2Settlement/OrderRefunds.t.sol @@ -12,10 +12,10 @@ enum FreeFunctionVariant { } abstract contract Variant is Helper { - FreeFunctionVariant internal immutable freeFn; + FreeFunctionVariant internal immutable FREE_FN; constructor(FreeFunctionVariant _freeFn) { - freeFn = _freeFn; + FREE_FN = _freeFn; } function defaultOrderUids() internal view returns (bytes[] memory orderUids) { @@ -26,9 +26,9 @@ abstract contract Variant is Helper { } function freeFunctionCall(bytes[] memory orderUids) private { - if (freeFn == FreeFunctionVariant.FreeFilledAmountStorage) { + if (FREE_FN == FreeFunctionVariant.FreeFilledAmountStorage) { settlement.freeFilledAmountStorage(orderUids); - } else if (freeFn == FreeFunctionVariant.FreePreSignatureStorage) { + } else if (FREE_FN == FreeFunctionVariant.FreePreSignatureStorage) { settlement.freePreSignatureStorage(orderUids); } else { revert("Invalid free function"); @@ -36,9 +36,9 @@ abstract contract Variant is Helper { } function freeFunctionCallTest(bytes[] memory orderUids) private { - if (freeFn == FreeFunctionVariant.FreeFilledAmountStorage) { + if (FREE_FN == FreeFunctionVariant.FreeFilledAmountStorage) { settlement.freeFilledAmountStorageTest(orderUids); - } else if (freeFn == FreeFunctionVariant.FreePreSignatureStorage) { + } else if (FREE_FN == FreeFunctionVariant.FreePreSignatureStorage) { settlement.freePreSignatureStorageTest(orderUids); } else { revert("Invalid free function"); diff --git a/test/GPv2Settlement/Settle/Reentrancy.t.sol b/test/GPv2Settlement/Settle/Reentrancy.t.sol index 7d94c7be..36f1aac4 100644 --- a/test/GPv2Settlement/Settle/Reentrancy.t.sol +++ b/test/GPv2Settlement/Settle/Reentrancy.t.sol @@ -12,29 +12,28 @@ import {Trade} from "test/libraries/Trade.sol"; import {SettlementEncoder} from "test/libraries/encoders/SettlementEncoder.sol"; import {SwapEncoder} from "test/libraries/encoders/SwapEncoder.sol"; -// solhint-disable func-name-mixedcase contract Reentrancy is Helper { using Trade for GPv2Order.Data; using SettlementEncoder for SettlementEncoder.State; using SwapEncoder for SwapEncoder.State; function test_settle_rejects_reentrancy_attempts_via_interactions() public { - reject_reentrancy_attempts_via_interactions(settle_reentrancy_calldata(), false); + rejectReentrancyAttemptsViaInteractions(settleReentrancyCalldata(), false); } function test_settle_rejects_reentrancy_attempts_via_interactions_as_a_registered_solver() public { - reject_reentrancy_attempts_via_interactions(settle_reentrancy_calldata(), true); + rejectReentrancyAttemptsViaInteractions(settleReentrancyCalldata(), true); } function test_swap_rejects_reentrancy_attempts_via_interactions() public { - reject_reentrancy_attempts_via_interactions(swap_reentrancy_calldata(), false); + rejectReentrancyAttemptsViaInteractions(swapReentrancyCalldata(), false); } function test_swap_rejects_reentrancy_attempts_via_interactions_as_a_registered_solver() public { - reject_reentrancy_attempts_via_interactions(swap_reentrancy_calldata(), true); + rejectReentrancyAttemptsViaInteractions(swapReentrancyCalldata(), true); } - function reject_reentrancy_attempts_via_interactions(bytes memory data, bool settlementContractIsSolver) internal { + function rejectReentrancyAttemptsViaInteractions(bytes memory data, bool settlementContractIsSolver) internal { if (settlementContractIsSolver) { vm.prank(owner); allowList.addSolver(address(settlement)); @@ -48,7 +47,7 @@ contract Reentrancy is Helper { settle(encoder.encode(interactions)); } - function settle_reentrancy_calldata() internal pure returns (bytes memory) { + function settleReentrancyCalldata() internal pure returns (bytes memory) { SettlementEncoder.EncodedSettlement memory empty; return abi.encodeCall( @@ -56,7 +55,7 @@ contract Reentrancy is Helper { ); } - function swap_reentrancy_calldata() internal returns (bytes memory) { + function swapReentrancyCalldata() internal returns (bytes memory) { swapEncoder.addToken(IERC20(address(0))); Sign.Signature memory empty = Sign.Signature({scheme: GPv2Signing.Scheme.Eip712, data: new bytes(65)}); diff --git a/test/GPv2Settlement/Swap/Balances.t.sol b/test/GPv2Settlement/Swap/Balances.t.sol index f3378763..aec4c3b9 100644 --- a/test/GPv2Settlement/Swap/Balances.t.sol +++ b/test/GPv2Settlement/Swap/Balances.t.sol @@ -10,30 +10,30 @@ contract Balances is Helper { using SwapEncoder for SwapEncoder.State; function test_performs_a_swap_to_sell_erc20_from_buy_erc20_when_specified() public { - performs_a_swap_with_the_specified_balances(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_ERC20); + performsASwapWithTheSpecifiedBalances(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_ERC20); } function test_performs_a_swap_to_sell_erc20_from_buy_internal_when_specified() public { - performs_a_swap_with_the_specified_balances(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_INTERNAL); + performsASwapWithTheSpecifiedBalances(GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_INTERNAL); } function test_performs_a_swap_to_sell_external_from_buy_erc20_when_specified() public { - performs_a_swap_with_the_specified_balances(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_ERC20); + performsASwapWithTheSpecifiedBalances(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_ERC20); } function test_performs_a_swap_to_sell_external_from_buy_internal_when_specified() public { - performs_a_swap_with_the_specified_balances(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_INTERNAL); + performsASwapWithTheSpecifiedBalances(GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_INTERNAL); } function test_performs_a_swap_to_sell_internal_from_buy_erc20_when_specified() public { - performs_a_swap_with_the_specified_balances(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_ERC20); + performsASwapWithTheSpecifiedBalances(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_ERC20); } function test_performs_a_swap_to_sell_internal_from_buy_internal_when_specified() public { - performs_a_swap_with_the_specified_balances(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_INTERNAL); + performsASwapWithTheSpecifiedBalances(GPv2Order.BALANCE_INTERNAL, GPv2Order.BALANCE_INTERNAL); } - function performs_a_swap_with_the_specified_balances(bytes32 sellTokenBalance, bytes32 buyTokenBalance) private { + function performsASwapWithTheSpecifiedBalances(bytes32 sellTokenBalance, bytes32 buyTokenBalance) private { address payable receiver = payable(makeAddr("receiver")); IERC20 sellToken = IERC20(makeAddr("sell token")); IERC20 buyToken = IERC20(makeAddr("buy token")); diff --git a/test/GPv2Settlement/Swap/Variants.t.sol b/test/GPv2Settlement/Swap/Variants.t.sol index 565cff03..d9e4da83 100644 --- a/test/GPv2Settlement/Swap/Variants.t.sol +++ b/test/GPv2Settlement/Swap/Variants.t.sol @@ -15,21 +15,21 @@ abstract contract Variant is Helper { IERC20 private sellToken = IERC20(makeAddr("GPv2Settlement.Swap.Variants sell token")); IERC20 private buyToken = IERC20(makeAddr("GPv2Settlement.Swap.Variants buy token")); - uint256 constant sellAmount = 4.2 ether; - uint256 constant buyAmount = 13.37 ether; + uint256 constant SELL_AMOUNT = 4.2 ether; + uint256 constant BUY_AMOUNT = 13.37 ether; - bytes32 immutable kind; + bytes32 immutable KIND; constructor(bytes32 _kind) { - kind = _kind; + KIND = _kind; } function defaultOrder() private view returns (GPv2Order.Data memory) { return GPv2Order.Data({ sellToken: sellToken, buyToken: buyToken, - sellAmount: sellAmount, - buyAmount: buyAmount, + sellAmount: SELL_AMOUNT, + buyAmount: BUY_AMOUNT, receiver: address(0), validTo: 0x01020304, appData: keccak256("GPv2Settlement.Swap.Variants default app data"), @@ -37,7 +37,7 @@ abstract contract Variant is Helper { sellTokenBalance: GPv2Order.BALANCE_INTERNAL, buyTokenBalance: GPv2Order.BALANCE_ERC20, partiallyFillable: true, - kind: kind + kind: KIND }); } @@ -76,7 +76,7 @@ abstract contract Variant is Helper { function test_executes_order_against_swap() public { SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(); - mockBalancerVaultCallsReturn(SafeCast.toInt256(sellAmount), -SafeCast.toInt256(buyAmount)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(SELL_AMOUNT), -SafeCast.toInt256(BUY_AMOUNT)); vm.prank(solver); swap(encodedSwap); @@ -85,12 +85,12 @@ abstract contract Variant is Helper { function test_updates_the_filled_amount_to_be_the_full_sell_or_buy_amount() public { SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(); - mockBalancerVaultCallsReturn(SafeCast.toInt256(sellAmount), -SafeCast.toInt256(buyAmount)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(SELL_AMOUNT), -SafeCast.toInt256(BUY_AMOUNT)); vm.prank(solver); swap(encodedSwap); - uint256 expectedFilledAmount = (kind == GPv2Order.KIND_SELL) ? sellAmount : buyAmount; + uint256 expectedFilledAmount = (KIND == GPv2Order.KIND_SELL) ? SELL_AMOUNT : BUY_AMOUNT; assertEq(settlement.filledAmount(defaultOrderUid()), expectedFilledAmount); } @@ -123,33 +123,33 @@ abstract contract Variant is Helper { function test_reverts_when_not_exactly_trading_expected_amount() public { SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(); - mockBalancerVaultCallsReturn(SafeCast.toInt256(sellAmount) - 1, -(SafeCast.toInt256(buyAmount) + 1)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(SELL_AMOUNT) - 1, -(SafeCast.toInt256(BUY_AMOUNT) + 1)); - string memory kindString = (kind == GPv2Order.KIND_SELL) ? "sell" : "buy"; + string memory kindString = (KIND == GPv2Order.KIND_SELL) ? "sell" : "buy"; vm.prank(solver); vm.expectRevert(bytes(string.concat("GPv2: ", kindString, " amount not respected"))); swap(encodedSwap); } function test_reverts_when_specified_limit_amount_does_not_satisfy_expected_price() public { - uint256 limitAmount = kind == GPv2Order.KIND_SELL - ? buyAmount - 1 // receive slightly less buy token - : sellAmount + 1; // pay slightly more sell token; + uint256 limitAmount = KIND == GPv2Order.KIND_SELL + ? BUY_AMOUNT - 1 // receive slightly less buy token + : SELL_AMOUNT + 1; // pay slightly more sell token; SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(limitAmount); - mockBalancerVaultCallsReturn(SafeCast.toInt256(sellAmount), -SafeCast.toInt256(buyAmount)); + mockBalancerVaultCallsReturn(SafeCast.toInt256(SELL_AMOUNT), -SafeCast.toInt256(BUY_AMOUNT)); vm.prank(solver); - vm.expectRevert(bytes((kind == GPv2Order.KIND_SELL) ? "GPv2: limit too low" : "GPv2: limit too high")); + vm.expectRevert(bytes((KIND == GPv2Order.KIND_SELL) ? "GPv2: limit too low" : "GPv2: limit too high")); swap(encodedSwap); } function test_emits_a_trade_event() public { SwapEncoder.EncodedSwap memory encodedSwap = encodedDefaultSwap(); - uint256 executedSellAmount = sellAmount; - uint256 executedBuyAmount = buyAmount; - if (kind == GPv2Order.KIND_SELL) { + uint256 executedSellAmount = SELL_AMOUNT; + uint256 executedBuyAmount = BUY_AMOUNT; + if (KIND == GPv2Order.KIND_SELL) { executedBuyAmount = executedBuyAmount * 2; } else { executedSellAmount = executedSellAmount / 2; diff --git a/test/GPv2Signing/DomainSeparator.t.sol b/test/GPv2Signing/DomainSeparator.t.sol index 498bb228..60615bd4 100644 --- a/test/GPv2Signing/DomainSeparator.t.sol +++ b/test/GPv2Signing/DomainSeparator.t.sol @@ -11,11 +11,11 @@ contract DomainSeparator is Helper { name: "Gnosis Protocol", version: "v2", chainId: block.chainid, verifyingContract: address(executor) }) ); - assertEq(executor.domainSeparator(), expectedDomainSeparator); + assertEq(executor.DOMAIN_SEPARATOR(), expectedDomainSeparator); } function test_should_have_a_different_replay_protection_for_each_deployment() public { Harness signing = new Harness(); - assertNotEq(executor.domainSeparator(), signing.domainSeparator()); + assertNotEq(executor.DOMAIN_SEPARATOR(), signing.DOMAIN_SEPARATOR()); } } diff --git a/test/GPv2Signing/Helper.sol b/test/GPv2Signing/Helper.sol index 9c9a5723..07f27f89 100644 --- a/test/GPv2Signing/Helper.sol +++ b/test/GPv2Signing/Helper.sol @@ -35,6 +35,6 @@ contract Helper is Test { function setUp() public { executor = new Harness(); - domainSeparator = executor.domainSeparator(); + domainSeparator = executor.DOMAIN_SEPARATOR(); } } diff --git a/test/GPv2Signing/SetPreSignature.t.sol b/test/GPv2Signing/SetPreSignature.t.sol index e653a1dd..a4087e3e 100644 --- a/test/GPv2Signing/SetPreSignature.t.sol +++ b/test/GPv2Signing/SetPreSignature.t.sol @@ -8,42 +8,42 @@ import {Order} from "test/libraries/Order.sol"; import {Sign} from "test/libraries/Sign.sol"; contract SetPreSignature is Helper { - address private immutable owner = makeAddr("GPv2Signing.SetPreSignature owner"); + address private immutable OWNER = makeAddr("GPv2Signing.SetPreSignature owner"); bytes private orderUid = - Order.computeOrderUid(keccak256("GPv2Signing.SetPreSignature order hash"), owner, type(uint32).max); + Order.computeOrderUid(keccak256("GPv2Signing.SetPreSignature order hash"), OWNER, type(uint32).max); function test_should_set_the_pre_signature() public { - vm.prank(owner); + vm.prank(OWNER); executor.setPreSignature(orderUid, true); assertEq(executor.preSignature(orderUid), Sign.PRE_SIGNED); } function test_should_unset_the_pre_signature() public { - vm.prank(owner); + vm.prank(OWNER); executor.setPreSignature(orderUid, true); - vm.prank(owner); + vm.prank(OWNER); executor.setPreSignature(orderUid, false); assertEq(executor.preSignature(orderUid), 0); } function test_should_emit_a_pre_signature_event() public { - vm.prank(owner); + vm.prank(OWNER); vm.expectEmit(address(executor)); - emit GPv2Signing.PreSignature(owner, orderUid, true); + emit GPv2Signing.PreSignature(OWNER, orderUid, true); executor.setPreSignature(orderUid, true); - vm.prank(owner); + vm.prank(OWNER); vm.expectEmit(address(executor)); - emit GPv2Signing.PreSignature(owner, orderUid, false); + emit GPv2Signing.PreSignature(OWNER, orderUid, false); executor.setPreSignature(orderUid, false); } function test_should_emit_a_PreSignature_event_even_if_storage_does_not_change() public { - vm.prank(owner); + vm.prank(OWNER); executor.setPreSignature(orderUid, true); - vm.prank(owner); + vm.prank(OWNER); vm.expectEmit(address(executor)); - emit GPv2Signing.PreSignature(owner, orderUid, true); + emit GPv2Signing.PreSignature(OWNER, orderUid, true); executor.setPreSignature(orderUid, true); } diff --git a/test/GPv2Trade/ExtractFlags.t.sol b/test/GPv2Trade/ExtractFlags.t.sol index 55096343..0c3ee455 100644 --- a/test/GPv2Trade/ExtractFlags.t.sol +++ b/test/GPv2Trade/ExtractFlags.t.sol @@ -13,7 +13,7 @@ contract ExtractOrder is Helper { using SettlementEncoder for SettlementEncoder.State; function test_should_extract_all_supported_order_flags() public view { - OrderLib.Flags[] memory flags = OrderLib.ALL_FLAGS(); + OrderLib.Flags[] memory flags = OrderLib.allFlags(); for (uint256 i = 0; i < flags.length; i++) { OrderLib.Flags memory extractedFlags = @@ -38,7 +38,7 @@ contract ExtractOrder is Helper { } function test_should_extract_all_supported_signing_schemes() public view { - GPv2Signing.Scheme[4] memory schemes = SignLib.ALL_SIGNING_SCHEMES(); + GPv2Signing.Scheme[4] memory schemes = SignLib.allSigningSchemes(); for (uint256 i = 0; i < schemes.length; i++) { TradeLib.Flags memory flags = TradeLib.Flags({ signingScheme: schemes[i], diff --git a/test/GPv2Trade/ExtractOrder.t.sol b/test/GPv2Trade/ExtractOrder.t.sol index a6360d76..eb6476a7 100644 --- a/test/GPv2Trade/ExtractOrder.t.sol +++ b/test/GPv2Trade/ExtractOrder.t.sol @@ -46,7 +46,7 @@ contract ExtractOrder is Helper { } function testFuzz_should_round_trip_encode_order_data(Fuzzed memory fuzzed) public { - OrderLib.Flags[] memory flags = OrderLib.ALL_FLAGS(); + OrderLib.Flags[] memory flags = OrderLib.allFlags(); for (uint256 i = 0; i < flags.length; i++) { GPv2Order.Data memory order = GPv2Order.Data({ @@ -73,7 +73,7 @@ contract ExtractOrder is Helper { } } - function should_revert_for_invalid_token_indices(GPv2Order.Data memory order, IERC20[] memory tokens) internal { + function shouldRevertForInvalidTokenIndices(GPv2Order.Data memory order, IERC20[] memory tokens) internal { SettlementEncoder.State storage encoder = SettlementEncoder.makeSettlementEncoder(); encoder.signEncodeTrade(vm, trader, order, domainSeparator, GPv2Signing.Scheme.Eip712, 0); // TODO: once Foundry supports catching EVM errors, require that this @@ -87,13 +87,13 @@ contract ExtractOrder is Helper { GPv2Order.Data memory order = sampleOrder(); IERC20[] memory tokens = new IERC20[](1); tokens[0] = order.buyToken; - should_revert_for_invalid_token_indices(order, tokens); + shouldRevertForInvalidTokenIndices(order, tokens); } function test_should_revert_for_invalid_buy_token_indices() public { GPv2Order.Data memory order = sampleOrder(); IERC20[] memory tokens = new IERC20[](1); tokens[0] = order.sellToken; - should_revert_for_invalid_token_indices(order, tokens); + shouldRevertForInvalidTokenIndices(order, tokens); } } diff --git a/test/GPv2Transfer/FastTransferFromAccount.t.sol b/test/GPv2Transfer/FastTransferFromAccount.t.sol index 6c08f0d2..0980ff8a 100644 --- a/test/GPv2Transfer/FastTransferFromAccount.t.sol +++ b/test/GPv2Transfer/FastTransferFromAccount.t.sol @@ -49,7 +49,7 @@ contract FastTransferFromAccount is Helper { executor.fastTransferFromAccountTest(vault, transfer, recipient); } - function reverts_when_mistakenly_trying_to_transfer_Ether(bytes32 balanceLocation) private { + function revertsWhenMistakenlyTryingToTransferEther(bytes32 balanceLocation) private { GPv2Transfer.Data memory transfer = GPv2Transfer.Data({ account: trader, token: IERC20(GPv2Transfer.BUY_ETH_ADDRESS), amount: amount, balance: balanceLocation }); @@ -57,16 +57,16 @@ contract FastTransferFromAccount is Helper { executor.fastTransferFromAccountTest(vault, transfer, recipient); } - function test_reverts_when_mistakenly_trying_to_transfer_Ether_erc20() public { - reverts_when_mistakenly_trying_to_transfer_Ether(GPv2Order.BALANCE_ERC20); + function test_revertsWhenMistakenlyTryingToTransferEther_erc20() public { + revertsWhenMistakenlyTryingToTransferEther(GPv2Order.BALANCE_ERC20); } - function test_reverts_when_mistakenly_trying_to_transfer_Ether_internal() public { - reverts_when_mistakenly_trying_to_transfer_Ether(GPv2Order.BALANCE_INTERNAL); + function test_revertsWhenMistakenlyTryingToTransferEther_internal() public { + revertsWhenMistakenlyTryingToTransferEther(GPv2Order.BALANCE_INTERNAL); } - function test_reverts_when_mistakenly_trying_to_transfer_Ether_external() public { - reverts_when_mistakenly_trying_to_transfer_Ether(GPv2Order.BALANCE_EXTERNAL); + function test_revertsWhenMistakenlyTryingToTransferEther_external() public { + revertsWhenMistakenlyTryingToTransferEther(GPv2Order.BALANCE_EXTERNAL); } function test_reverts_on_failed_ERC20_transfer() public { diff --git a/test/GPv2Transfer/TransferFromAccounts.t.sol b/test/GPv2Transfer/TransferFromAccounts.t.sol index a86022f1..b00a98cf 100644 --- a/test/GPv2Transfer/TransferFromAccounts.t.sol +++ b/test/GPv2Transfer/TransferFromAccounts.t.sol @@ -109,7 +109,7 @@ contract TransferFromAccounts is Helper { executor.transferFromAccountsTest(vault, transfers, recipient); } - function reverts_when_mistakenly_trying_to_transfer_Ether(bytes32 balanceLocation) private { + function revertsWhenMistakenlyTryingToTransferEther(bytes32 balanceLocation) private { GPv2Transfer.Data[] memory transfers = new GPv2Transfer.Data[](1); transfers[0] = GPv2Transfer.Data({ account: trader, token: IERC20(GPv2Transfer.BUY_ETH_ADDRESS), amount: amount, balance: balanceLocation @@ -118,16 +118,16 @@ contract TransferFromAccounts is Helper { executor.transferFromAccountsTest(vault, transfers, recipient); } - function test_reverts_when_mistakenly_trying_to_transfer_Ether_erc20() public { - reverts_when_mistakenly_trying_to_transfer_Ether(GPv2Order.BALANCE_ERC20); + function test_revertsWhenMistakenlyTryingToTransferEther_erc20() public { + revertsWhenMistakenlyTryingToTransferEther(GPv2Order.BALANCE_ERC20); } - function test_reverts_when_mistakenly_trying_to_transfer_Ether_internal() public { - reverts_when_mistakenly_trying_to_transfer_Ether(GPv2Order.BALANCE_INTERNAL); + function test_revertsWhenMistakenlyTryingToTransferEther_internal() public { + revertsWhenMistakenlyTryingToTransferEther(GPv2Order.BALANCE_INTERNAL); } - function test_reverts_when_mistakenly_trying_to_transfer_Ether_external() public { - reverts_when_mistakenly_trying_to_transfer_Ether(GPv2Order.BALANCE_EXTERNAL); + function test_revertsWhenMistakenlyTryingToTransferEther_external() public { + revertsWhenMistakenlyTryingToTransferEther(GPv2Order.BALANCE_EXTERNAL); } function test_reverts_on_failed_ERC20_transfer() public { diff --git a/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol b/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol index ca3ee11e..52837593 100644 --- a/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol +++ b/test/GPv2VaultRelayer/BatchSwapWithFee.t.sol @@ -16,14 +16,14 @@ contract BatchSwapWithFee is BatchSwapWithFeeHelper { } function test_performs_swaps_given_in() public { - performs_swaps_for_swap_kind(IVault.SwapKind.GIVEN_IN); + performsSwapsForSwapKind(IVault.SwapKind.GIVEN_IN); } function test_performs_swaps_given_out() public { - performs_swaps_for_swap_kind(IVault.SwapKind.GIVEN_OUT); + performsSwapsForSwapKind(IVault.SwapKind.GIVEN_OUT); } - function performs_swaps_for_swap_kind(IVault.SwapKind kind) private { + function performsSwapsForSwapKind(IVault.SwapKind kind) private { address trader0 = makeAddr("trader 0"); address trader1 = makeAddr("trader 1"); diff --git a/test/e2e/ContractOrdersWithGnosisSafe.t.sol b/test/e2e/ContractOrdersWithGnosisSafe.t.sol index 17a09184..82a43d22 100644 --- a/test/e2e/ContractOrdersWithGnosisSafe.t.sol +++ b/test/e2e/ContractOrdersWithGnosisSafe.t.sol @@ -80,7 +80,7 @@ bytes4 constant EIP1271_MAGICVALUE = bytes4(keccak256("isValidSignature(bytes32, contract ContractOrdersWithGnosisSafeTest is Helper(true) { IERC20Mintable dai; - IERC20Mintable wETH; + IERC20Mintable wethToken; ISafe safe; @@ -94,7 +94,7 @@ contract ContractOrdersWithGnosisSafeTest is Helper(true) { super.setUp(); dai = deployMintableErc20("dai", "dai"); - wETH = deployMintableErc20("wETH", "wETH"); + wethToken = deployMintableErc20("wethToken", "wethToken"); signer1 = vm.createWallet("signer1"); signer2 = vm.createWallet("signer2"); @@ -117,24 +117,24 @@ contract ContractOrdersWithGnosisSafeTest is Helper(true) { } function test_should_settle_matching_orders() external { - // EOA trader: sell 1 wETH for 900 dai - // Safe: buy 1 wETH for 1100 dai - // Settlement price at 1000 dai for 1 wETH. + // EOA trader: sell 1 wethToken for 900 dai + // Safe: buy 1 wethToken for 1100 dai + // Settlement price at 1000 dai for 1 wethToken. // mint some tokens to trader - wETH.mint(trader.addr, 1.001 ether); + wethToken.mint(trader.addr, 1.001 ether); // approve the tokens for trading on settlement contract vm.prank(trader.addr); - wETH.approve(vaultRelayer, type(uint256).max); + wethToken.approve(vaultRelayer, type(uint256).max); - // place order to sell 1 wETH for min 900 dai + // place order to sell 1 wethToken for min 900 dai encoder.signEncodeTrade( vm, trader, GPv2Order.Data({ kind: GPv2Order.KIND_SELL, partiallyFillable: false, - sellToken: wETH, + sellToken: wethToken, buyToken: dai, sellAmount: 1 ether, buyAmount: 900 ether, @@ -158,12 +158,12 @@ contract ContractOrdersWithGnosisSafeTest is Helper(true) { ); assertEq(dai.allowance(address(safe), vaultRelayer), type(uint256).max, "allowance not as expected"); - // place order to buy 1 wETH with max 1100 dai + // place order to buy 1 wethToken with max 1100 dai GPv2Order.Data memory order = GPv2Order.Data({ kind: GPv2Order.KIND_BUY, partiallyFillable: false, sellToken: dai, - buyToken: wETH, + buyToken: wethToken, sellAmount: 1100 ether, buyAmount: 1 ether, feeAmount: 10 ether, @@ -189,7 +189,7 @@ contract ContractOrdersWithGnosisSafeTest is Helper(true) { // set token prices IERC20[] memory tokens = new IERC20[](2); tokens[0] = dai; - tokens[1] = wETH; + tokens[1] = wethToken; uint256[] memory prices = new uint256[](2); prices[0] = 1 ether; prices[1] = 1000 ether; @@ -200,13 +200,13 @@ contract ContractOrdersWithGnosisSafeTest is Helper(true) { vm.prank(solver); settle(encodedSettlement); - assertEq(wETH.balanceOf(trader.addr), 0, "trader weth balance not as expected"); + assertEq(wethToken.balanceOf(trader.addr), 0, "trader wethToken balance not as expected"); assertEq(dai.balanceOf(trader.addr), 1000 ether, "trader dai balance not as expected"); - assertEq(wETH.balanceOf(address(safe)), 1 ether, "safe weth balance not as expected"); + assertEq(wethToken.balanceOf(address(safe)), 1 ether, "safe wethToken balance not as expected"); assertEq(dai.balanceOf(address(safe)), 100 ether, "safe dai balance not as expected"); - assertEq(wETH.balanceOf(address(settlement)), 0.001 ether, "settlement weth fee not as expected"); + assertEq(wethToken.balanceOf(address(settlement)), 0.001 ether, "settlement wethToken fee not as expected"); assertEq(dai.balanceOf(address(settlement)), 10 ether, "settlement dai fee not as expected"); } diff --git a/test/e2e/Helper.sol b/test/e2e/Helper.sol index b327f924..c8dacf75 100644 --- a/test/e2e/Helper.sol +++ b/test/e2e/Helper.sol @@ -63,7 +63,7 @@ abstract contract Helper is Test { address internal solver; Vm.Wallet internal trader; - bool immutable isForked; + bool immutable IS_FORKED; uint256 forkId; WETH9 weth; @@ -71,11 +71,11 @@ abstract contract Helper is Test { bytes32 constant SALT = "Mattresses in Berlin!"; constructor(bool _isForked) { - isForked = _isForked; + IS_FORKED = _isForked; } function setUp() public virtual { - if (isForked) { + if (IS_FORKED) { string memory forkUrl; try vm.envString("FORK_URL") returns (string memory url) { @@ -121,7 +121,7 @@ abstract contract Helper is Test { swapEncoder = SwapEncoder.makeSwapEncoder(); // Set the domain separator - domainSeparator = settlement.domainSeparator(); + domainSeparator = settlement.DOMAIN_SEPARATOR(); // Create wallets trader = vm.createWallet("E2E.Helper: trader"); @@ -172,6 +172,7 @@ abstract contract Helper is Test { } function _getBalancerBytecode(string memory artifactName) internal view returns (bytes memory) { + /// forge-lint: disable-next-line(unsafe-cheatcode) string memory data = vm.readFile(string(abi.encodePacked("balancer/", artifactName, ".json"))); return vm.parseJsonBytes(data, ".bytecode"); } diff --git a/test/e2e/OffchainAllowances.t.sol b/test/e2e/OffchainAllowances.t.sol index ffcc7fed..a1f4815e 100644 --- a/test/e2e/OffchainAllowances.t.sol +++ b/test/e2e/OffchainAllowances.t.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8; import {Vm} from "forge-std/Vm.sol"; import {IERC20} from "src/contracts/interfaces/IERC20.sol"; -import {IVault} from "src/contracts/interfaces/IVault.sol"; import {GPv2Interaction} from "src/contracts/libraries/GPv2Interaction.sol"; import {GPv2Order} from "src/contracts/libraries/GPv2Order.sol"; import {GPv2Signing} from "src/contracts/mixins/GPv2Signing.sol"; diff --git a/test/e2e/SmartOrder.t.sol b/test/e2e/SmartOrder.t.sol index 4c612a0f..b3400e6f 100644 --- a/test/e2e/SmartOrder.t.sol +++ b/test/e2e/SmartOrder.t.sol @@ -25,13 +25,13 @@ contract SmartSellOrder is EIP1271Verifier { bytes32 public constant APPDATA = keccak256("SmartSellOrder"); - address public immutable owner; - bytes32 public immutable domainSeparator; - IERC20 public immutable sellToken; - IERC20 public immutable buyToken; - uint256 public immutable totalSellAmount; - uint256 public immutable totalFeeAmount; - uint32 public immutable validTo; + address public immutable OWNER; + bytes32 public immutable DOMAIN_SEPARATOR; + IERC20 public immutable SELL_TOKEN; + IERC20 public immutable BUY_TOKEN; + uint256 public immutable TOTAL_SELL_AMOUNT; + uint256 public immutable TOTAL_FEE_AMOUNT; + uint32 public immutable VALID_TO; constructor( GPv2Settlement settlement, @@ -41,30 +41,30 @@ contract SmartSellOrder is EIP1271Verifier { uint256 totalSellAmount_, uint256 totalFeeAmount_ ) { - owner = msg.sender; - domainSeparator = settlement.domainSeparator(); - sellToken = sellToken_; - buyToken = buyToken_; - validTo = validTo_; - totalSellAmount = totalSellAmount_; - totalFeeAmount = totalFeeAmount_; + OWNER = msg.sender; + DOMAIN_SEPARATOR = settlement.DOMAIN_SEPARATOR(); + SELL_TOKEN = sellToken_; + BUY_TOKEN = buyToken_; + VALID_TO = validTo_; + TOTAL_SELL_AMOUNT = totalSellAmount_; + TOTAL_FEE_AMOUNT = totalFeeAmount_; sellToken_.approve(address(settlement.vaultRelayer()), type(uint256).max); } modifier onlyOwner() { - require(msg.sender == owner, "not owner"); + require(msg.sender == OWNER, "not owner"); _; } function withdraw(uint256 amount) external onlyOwner { - sellToken.safeTransfer(owner, amount); + SELL_TOKEN.safeTransfer(OWNER, amount); } function close() external onlyOwner { - uint256 balance = sellToken.balanceOf(address(this)); + uint256 balance = SELL_TOKEN.balanceOf(address(this)); if (balance != 0) { - sellToken.safeTransfer(owner, balance); + SELL_TOKEN.safeTransfer(OWNER, balance); } } @@ -72,20 +72,20 @@ contract SmartSellOrder is EIP1271Verifier { uint256 sellAmount = abi.decode(signature, (uint256)); GPv2Order.Data memory order = orderForSellAmount(sellAmount); - if (order.hash(domainSeparator) == hash) { + if (order.hash(DOMAIN_SEPARATOR) == hash) { magicValue = GPv2EIP1271.MAGICVALUE; } } function orderForSellAmount(uint256 sellAmount) public view returns (GPv2Order.Data memory order) { - order.sellToken = sellToken; - order.buyToken = buyToken; - order.receiver = owner; + order.sellToken = SELL_TOKEN; + order.buyToken = BUY_TOKEN; + order.receiver = OWNER; order.sellAmount = sellAmount; order.buyAmount = buyAmountForSellAmount(sellAmount); - order.validTo = validTo; + order.validTo = VALID_TO; order.appData = APPDATA; - order.feeAmount = totalFeeAmount.mul(sellAmount).div(totalSellAmount); + order.feeAmount = TOTAL_FEE_AMOUNT.mul(sellAmount).div(TOTAL_SELL_AMOUNT); order.kind = GPv2Order.KIND_SELL; // NOTE: We counter-intuitively set `partiallyFillable` to `false`, even // if the smart order as a whole acts like a partially fillable order. @@ -98,13 +98,13 @@ contract SmartSellOrder is EIP1271Verifier { function buyAmountForSellAmount(uint256 sellAmount) private view returns (uint256 buyAmount) { uint256 feeAdjustedBalance = - sellToken.balanceOf(address(this)).mul(totalSellAmount).div(totalSellAmount.add(totalFeeAmount)); - uint256 soldAmount = totalSellAmount > feeAdjustedBalance ? totalSellAmount - feeAdjustedBalance : 0; + SELL_TOKEN.balanceOf(address(this)).mul(TOTAL_SELL_AMOUNT).div(TOTAL_SELL_AMOUNT.add(TOTAL_FEE_AMOUNT)); + uint256 soldAmount = TOTAL_SELL_AMOUNT > feeAdjustedBalance ? TOTAL_SELL_AMOUNT - feeAdjustedBalance : 0; // NOTE: This is currently a silly price strategy where the xrate // increases linearly from 1:1 to 1:2 as the smart order gets filled. // This can be extended to more complex "price curves". - buyAmount = sellAmount.mul(totalSellAmount.add(sellAmount).add(soldAmount)).div(totalSellAmount); + buyAmount = sellAmount.mul(TOTAL_SELL_AMOUNT.add(sellAmount).add(soldAmount)).div(TOTAL_SELL_AMOUNT); } } diff --git a/test/e2e/UniswapTrade.t.sol b/test/e2e/UniswapTrade.t.sol index ab71c6f2..09fc952b 100644 --- a/test/e2e/UniswapTrade.t.sol +++ b/test/e2e/UniswapTrade.t.sol @@ -30,7 +30,7 @@ interface IUniswapV2Pair { contract UniswapTradeTest is Helper(true) { IERC20Mintable dai; - IERC20Mintable wETH; + IERC20Mintable wethToken; IUniswapV2Factory factory; IUniswapV2Pair uniswapPair; @@ -41,39 +41,39 @@ contract UniswapTradeTest is Helper(true) { super.setUp(); dai = deployMintableErc20("dai", "dai"); - wETH = deployMintableErc20("wETH", "wETH"); + wethToken = deployMintableErc20("wethToken", "wethToken"); factory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); - uniswapPair = IUniswapV2Pair(factory.createPair(address(wETH), address(dai))); + uniswapPair = IUniswapV2Pair(factory.createPair(address(wethToken), address(dai))); - isWethToken0 = uniswapPair.token0() == address(wETH); + isWethToken0 = uniswapPair.token0() == address(wethToken); } // Settles the following batch: // - // /----(1. SELL 1 wETH for dai if p(wETH) >= 500)-----\ + // /----(1. SELL 1 wethToken for dai if p(wethToken) >= 500)-----\ // | | // | v - // [dai]<---(Uniswap Pair 1000 wETH / 600.000 dai)--->[wETH] + // [dai]<---(Uniswap Pair 1000 wethToken / 600.000 dai)--->[wethToken] // ^ | // | | - // \----(2. BUY 0.5 wETH for dai if p(wETH) <= 600)----/ + // \----(2. BUY 0.5 wethToken for dai if p(wethToken) <= 600)----/ function test_should_two_overlapping_orders_and_trade_surplus_with_uniswap() external { uint256 wethReserve = 1000 ether; uint256 daiReserve = 600000 ether; - wETH.mint(address(uniswapPair), wethReserve); + wethToken.mint(address(uniswapPair), wethReserve); dai.mint(address(uniswapPair), daiReserve); uniswapPair.mint(address(this)); - // The current batch has a sell order selling 1 wETH and a buy order buying - // 0.5 wETH. This means there is exactly a surplus 0.5 wETH that needs to be + // The current batch has a sell order selling 1 wethToken and a buy order buying + // 0.5 wethToken. This means there is exactly a surplus 0.5 wethToken that needs to be // sold to Uniswap. Uniswap is governed by a balancing equation which can be - // used to compute the exact buy amount for selling the 0.5 wETH and we can + // used to compute the exact buy amount for selling the 0.5 wethToken and we can // use to build our the settlement with a smart contract interaction. // ``` - // (reservewETH + inwETH * 0.997) * (reservedai - outdai) = reservewETH * reservedai - // outdai = (reservedai * inwETH * 0.997) / (reservewETH + inwETH * 0.997) - // = (reservedai * inwETH * 997) / (reservewETH * 1000 + inwETH * 997) + // (reserveweth + inweth * 0.997) * (reservedai - outdai) = reserveweth * reservedai + // outdai = (reservedai * inweth * 0.997) / (reserveweth + inweth * 0.997) + // = (reservedai * inweth * 997) / (reserveweth * 1000 + inweth * 997) // ``` uint256 uniswapWethInAmount = 0.5 ether; uint256 uniswapDaiOutAmount = @@ -82,19 +82,19 @@ contract UniswapTradeTest is Helper(true) { Vm.Wallet memory trader1 = vm.createWallet("trader1"); Vm.Wallet memory trader2 = vm.createWallet("trader2"); - // mint some weth - wETH.mint(trader1.addr, 1.001 ether); + // mint some wethToken + wethToken.mint(trader1.addr, 1.001 ether); vm.prank(trader1.addr); - wETH.approve(vaultRelayer, type(uint256).max); + wethToken.approve(vaultRelayer, type(uint256).max); - // place order to sell 1 wETH for min 500 dai + // place order to sell 1 wethToken for min 500 dai encoder.signEncodeTrade( vm, trader1, GPv2Order.Data({ kind: GPv2Order.KIND_SELL, partiallyFillable: false, - sellToken: wETH, + sellToken: wethToken, buyToken: dai, sellAmount: 1 ether, buyAmount: 500 ether, @@ -115,7 +115,7 @@ contract UniswapTradeTest is Helper(true) { vm.prank(trader2.addr); dai.approve(vaultRelayer, type(uint256).max); - // place order to buy 0.5 wETH for max 300 dai + // place order to buy 0.5 wethToken for max 300 dai encoder.signEncodeTrade( vm, trader2, @@ -123,7 +123,7 @@ contract UniswapTradeTest is Helper(true) { kind: GPv2Order.KIND_BUY, partiallyFillable: false, sellToken: dai, - buyToken: wETH, + buyToken: wethToken, sellAmount: 300 ether, buyAmount: 0.5 ether, feeAmount: 0.3 ether, @@ -141,7 +141,7 @@ contract UniswapTradeTest is Helper(true) { // interaction to swap the remainder on uniswap encoder.addInteraction( GPv2Interaction.Data({ - target: address(wETH), + target: address(wethToken), value: 0, callData: abi.encodeCall(IERC20.transfer, (address(uniswapPair), uniswapWethInAmount)) }), @@ -160,7 +160,7 @@ contract UniswapTradeTest is Helper(true) { // set token prices IERC20[] memory tokens = new IERC20[](2); - tokens[0] = wETH; + tokens[0] = wethToken; tokens[1] = dai; uint256[] memory prices = new uint256[](2); prices[0] = uniswapDaiOutAmount; @@ -172,13 +172,13 @@ contract UniswapTradeTest is Helper(true) { vm.prank(solver); settle(encodedSettlement); - assertEq(wETH.balanceOf(address(settlement)), 0.001 ether, "weth fees not as expected"); + assertEq(wethToken.balanceOf(address(settlement)), 0.001 ether, "wethToken fees not as expected"); assertEq(dai.balanceOf(address(settlement)), 0.3 ether, "dai fees not as expected"); - assertEq(wETH.balanceOf(trader1.addr), 0, "not all weth sold"); + assertEq(wethToken.balanceOf(trader1.addr), 0, "not all wethToken sold"); assertEq(dai.balanceOf(trader1.addr), uniswapDaiOutAmount * 2, "dai received not as expected"); - assertEq(wETH.balanceOf(trader2.addr), 0.5 ether, "weth bought not correct amount"); + assertEq(wethToken.balanceOf(trader2.addr), 0.5 ether, "wethToken bought not correct amount"); assertEq(dai.balanceOf(trader2.addr), 300.3 ether - (uniswapDaiOutAmount + 0.3 ether)); uint256 finalWethReserve; @@ -189,7 +189,7 @@ contract UniswapTradeTest is Helper(true) { (finalWethReserve, finalDaiReserve) = isWethToken0 ? (token0Reserve, token1Reserve) : (token1Reserve, token0Reserve); } - assertEq(finalWethReserve, wethReserve + uniswapWethInAmount, "weth reserve not as expected"); + assertEq(finalWethReserve, wethReserve + uniswapWethInAmount, "wethToken reserve not as expected"); assertEq(finalDaiReserve, daiReserve - uniswapDaiOutAmount, "dai reserve not as expected"); } } diff --git a/test/libraries/Bytecode.sol b/test/libraries/Bytecode.sol index 00002996..0933f5bb 100644 --- a/test/libraries/Bytecode.sol +++ b/test/libraries/Bytecode.sol @@ -19,6 +19,7 @@ library Bytecode { returns (bytes[] memory immutables) { string memory json = + /// forge-lint: disable-next-line(unsafe-cheatcode) vm.readFile(string.concat(vm.projectRoot(), "/out/", contractName, ".sol/", contractName, ".json")); string memory jsonPath = ".deployedBytecode.immutableReferences"; diff --git a/test/libraries/Eip712.sol b/test/libraries/Eip712.sol index 82e3f11a..ce54e9a7 100644 --- a/test/libraries/Eip712.sol +++ b/test/libraries/Eip712.sol @@ -8,6 +8,7 @@ library Eip712 { /// parameter is intentionally omitted as there is no need to disambiguate /// with the available information. More details can be found at: /// https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator + /// forge-lint: disable-next-line(pascal-case-struct) struct EIP712Domain { string name; string version; @@ -35,7 +36,7 @@ library Eip712 { // Ideally, this would be replaced by type(EIP712Domain).typehash. // Progress tracking for this Solidity feature is here: // https://github.com/ethereum/solidity/issues/14157 - function EIP712DOMAIN_TYPE_HASH() internal pure returns (bytes32) { + function eip712DomainTypeHash() internal pure returns (bytes32) { return keccak256( bytes( string.concat( @@ -54,7 +55,7 @@ library Eip712 { // Ideally, this would be replaced by type(Order).typehash. // Progress tracking for this Solidity feature is here: // https://github.com/ethereum/solidity/issues/14157 - function ORDER_TYPE_HASH() internal pure returns (bytes32) { + function orderTypeHash() internal pure returns (bytes32) { return keccak256( bytes( string.concat( @@ -133,7 +134,7 @@ library Eip712 { return keccak256( // Note: dynamic types are hashed. abi.encode( - EIP712DOMAIN_TYPE_HASH(), + eip712DomainTypeHash(), keccak256(bytes(domain.name)), keccak256(bytes(domain.version)), domain.chainId, @@ -149,7 +150,7 @@ library Eip712 { return keccak256( // Note: dynamic types are hashed. abi.encode( - ORDER_TYPE_HASH(), + orderTypeHash(), order.sellToken, order.buyToken, order.receiver, diff --git a/test/libraries/Order.sol b/test/libraries/Order.sol index d6dea48b..34123805 100644 --- a/test/libraries/Order.sol +++ b/test/libraries/Order.sol @@ -32,42 +32,42 @@ library Order { // type are implemented." and "Library cannot have non-constant state // variables". So I'm left with defining them as functions. - function ALL_KINDS() internal pure returns (bytes32[2] memory) { + function allKinds() internal pure returns (bytes32[2] memory) { return [GPv2Order.KIND_SELL, GPv2Order.KIND_BUY]; } - function ALL_SELL_TOKEN_BALANCES() internal pure returns (bytes32[3] memory) { + function allSellTokenBalances() internal pure returns (bytes32[3] memory) { return [GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_EXTERNAL, GPv2Order.BALANCE_INTERNAL]; } - function ALL_BUY_TOKEN_BALANCES() internal pure returns (bytes32[2] memory) { + function allBuyTokenBalances() internal pure returns (bytes32[2] memory) { return [GPv2Order.BALANCE_ERC20, GPv2Order.BALANCE_INTERNAL]; } - function ALL_FLAGS() internal pure returns (Flags[] memory out) { + function allFlags() internal pure returns (Flags[] memory out) { uint256 numBools = 1; uint256 boolLength = 2; // "out" has as many entries as there are distinct options to fill the // `Flags` struct. out = new Flags[]( - ALL_KINDS().length * ALL_SELL_TOKEN_BALANCES().length * ALL_BUY_TOKEN_BALANCES().length + allKinds().length * allSellTokenBalances().length * allBuyTokenBalances().length * (boolLength * numBools) ); uint256 offset = 0; - for (uint256 kindI = 0; kindI < ALL_KINDS().length; kindI++) { + for (uint256 kindI = 0; kindI < allKinds().length; kindI++) { for ( uint256 sellTokenBalanceI = 0; - sellTokenBalanceI < ALL_SELL_TOKEN_BALANCES().length; + sellTokenBalanceI < allSellTokenBalances().length; sellTokenBalanceI++ ) { for ( uint256 buyTokenBalanceI = 0; - buyTokenBalanceI < ALL_BUY_TOKEN_BALANCES().length; + buyTokenBalanceI < allBuyTokenBalances().length; buyTokenBalanceI++ ) { - bytes32 kind = ALL_KINDS()[kindI]; - bytes32 sellTokenBalance = ALL_SELL_TOKEN_BALANCES()[sellTokenBalanceI]; - bytes32 buyTokenBalance = ALL_BUY_TOKEN_BALANCES()[buyTokenBalanceI]; + bytes32 kind = allKinds()[kindI]; + bytes32 sellTokenBalance = allSellTokenBalances()[sellTokenBalanceI]; + bytes32 buyTokenBalance = allBuyTokenBalances()[buyTokenBalanceI]; out[offset] = Flags({ kind: kind, sellTokenBalance: sellTokenBalance, @@ -174,10 +174,10 @@ library Order { } function fuzz(Fuzzed memory params) internal pure returns (GPv2Order.Data memory) { - Order.Flags[] memory allFlags = Order.ALL_FLAGS(); + Order.Flags[] memory allFlagsArray = Order.allFlags(); // `flags` isn't exactly random, but for fuzzing purposes it should be // more than enough. - Order.Flags memory flags = allFlags[uint256(params.flagsPick) % allFlags.length]; + Order.Flags memory flags = allFlagsArray[uint256(params.flagsPick) % allFlagsArray.length]; return GPv2Order.Data({ sellToken: IERC20(params.sellToken), buyToken: IERC20(params.buyToken), diff --git a/test/libraries/Sign.sol b/test/libraries/Sign.sol index 096baa88..1bac26f8 100644 --- a/test/libraries/Sign.sol +++ b/test/libraries/Sign.sol @@ -32,7 +32,7 @@ library Sign { bytes signature; } - function ALL_SIGNING_SCHEMES() internal pure returns (GPv2Signing.Scheme[4] memory) { + function allSigningSchemes() internal pure returns (GPv2Signing.Scheme[4] memory) { return [ GPv2Signing.Scheme.Eip712, @@ -70,7 +70,7 @@ library Sign { /// @dev Encode the data used to verify a pre-signed signature function preSign(address owner) internal pure returns (Signature memory) { - return Signature(GPv2Signing.Scheme.PreSign, abi.encodePacked(owner)); + return Signature({scheme: GPv2Signing.Scheme.PreSign, data: abi.encodePacked(owner)}); } /// @dev Decode the data used to verify a pre-signed signature @@ -89,7 +89,7 @@ library Sign { /// @dev Encodes the necessary data required to verify an EIP-1271 signature function sign(EIP1271Verifier verifier, bytes memory signature) internal pure returns (Signature memory) { - return Signature(GPv2Signing.Scheme.Eip1271, abi.encodePacked(verifier, signature)); + return Signature({scheme: GPv2Signing.Scheme.Eip1271, data: abi.encodePacked(verifier, signature)}); } /// @dev Decodes the data used to verify an EIP-1271 signature @@ -108,7 +108,7 @@ library Sign { verifier := shr(96, mload(add(signatureData, 0x20))) } - return Eip1271Signature(verifier, signature); + return Eip1271Signature({verifier: verifier, signature: signature}); } /// @dev Given a `scheme`, encode it into a uint256 for a GPv2Trade. This makes use of solidity's diff --git a/test/libraries/encoders/SettlementEncoder.sol b/test/libraries/encoders/SettlementEncoder.sol index 9b5ccbfb..a850be11 100644 --- a/test/libraries/encoders/SettlementEncoder.sol +++ b/test/libraries/encoders/SettlementEncoder.sol @@ -105,17 +105,16 @@ library SettlementEncoder { // All the order refunds are encoded in the POST interactions so we take some liberty and // use a short variable to represent the POST stage. - // solhint-disable-next-line var-name-mixedcase - uint256 POST = uint256(InteractionStage.POST); + uint256 post = uint256(InteractionStage.POST); GPv2Interaction.Data[] memory postInteractions = - new GPv2Interaction.Data[](state.interactions[POST].length + r.length); + new GPv2Interaction.Data[](state.interactions[post].length + r.length); - for (uint256 i = 0; i < state.interactions[POST].length; i++) { - postInteractions[i] = state.interactions[POST][i]; + for (uint256 i = 0; i < state.interactions[post].length; i++) { + postInteractions[i] = state.interactions[post][i]; } for (uint256 i = 0; i < r.length; i++) { - postInteractions[state.interactions[POST].length + i] = r[i]; + postInteractions[state.interactions[post].length + i] = r[i]; } return [ diff --git a/test/libraries/encoders/SwapEncoder.sol b/test/libraries/encoders/SwapEncoder.sol index e51771b6..0533eb82 100644 --- a/test/libraries/encoders/SwapEncoder.sol +++ b/test/libraries/encoders/SwapEncoder.sol @@ -117,7 +117,7 @@ library SwapEncoder { /// @dev Encode the state into an EncodedSwap struct function encode(State storage state) internal returns (EncodedSwap memory) { - return EncodedSwap(state.steps, tokens(state), state.trade); + return EncodedSwap({swaps: state.steps, tokens: tokens(state), trade: state.trade}); } /// @dev Convert a Swap struct into a BatchSwapStep struct diff --git a/test/reader/StorageAccessibleWrapper.sol b/test/reader/StorageAccessibleWrapper.sol index 4b0fab33..09deab17 100644 --- a/test/reader/StorageAccessibleWrapper.sol +++ b/test/reader/StorageAccessibleWrapper.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-only pragma solidity ^0.8; -import "src/contracts/mixins/StorageAccessible.sol"; +import {StorageAccessible, ViewStorageAccessible} from "src/contracts/mixins/StorageAccessible.sol"; contract StorageAccessibleWrapper is StorageAccessible { struct FooBar { diff --git a/test/src/EIP173Proxy.sol b/test/src/EIP173Proxy.sol index 90c3bea8..081b01b3 100644 --- a/test/src/EIP173Proxy.sol +++ b/test/src/EIP173Proxy.sol @@ -16,10 +16,14 @@ contract EIP173Proxy is Proxy { event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); modifier onlyOwner() { - require(msg.sender == _owner(), "NOT_AUTHORIZED"); + _onlyOwner(); _; } + function _onlyOwner() internal view { + require(msg.sender == _owner(), "NOT_AUTHORIZED"); + } + constructor(address implAddress, address ownerAddress, bytes memory data) { _setOwner(ownerAddress); _setImplementation(implAddress, data); diff --git a/test/src/ERC20PresetPermit.sol b/test/src/ERC20PresetPermit.sol index cec1efb8..b4dfd280 100644 --- a/test/src/ERC20PresetPermit.sol +++ b/test/src/ERC20PresetPermit.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-later pragma solidity ^0.8; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; contract ERC20PresetPermit is ERC20Permit { constructor(string memory symbol) ERC20(symbol, symbol) ERC20Permit(symbol) diff --git a/test/src/vendor/ChiToken.sol b/test/src/vendor/ChiToken.sol index a44b0eb4..fa6858f0 100644 --- a/test/src/vendor/ChiToken.sol +++ b/test/src/vendor/ChiToken.sol @@ -2,8 +2,8 @@ // Vendored from: pragma solidity ^0.8.0; -import "@openzeppelin/contracts/utils/math/Math.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; abstract contract ERC20WithoutTotalSupply is IERC20 { @@ -71,9 +71,13 @@ abstract contract ERC20WithoutTotalSupply is IERC20 { } } -contract ChiToken is IERC20, ERC20WithoutTotalSupply { +contract ChiToken is ERC20WithoutTotalSupply { + // we disable forge lints here because these view functions are sort of defined by standard and creating the actual functions is tedious and unnecessary + /// forge-lint: disable-next-line(screaming-snake-case-const) string public constant name = "Chi Token by 1inch"; + /// forge-lint: disable-next-line(screaming-snake-case-const) string public constant symbol = "CHI"; + /// forge-lint: disable-next-line(screaming-snake-case-const) uint8 public constant decimals = 0; uint256 public totalMinted;