From d8cc2b4e93397fdb55a5cc10806266952fa90d89 Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:08:55 -0300 Subject: [PATCH 01/10] Add inital integration reversioning --- contracts | 2 +- core/lib/types/src/event/mod.rs | 9 +- .../src/updates/l2_block_updates.rs | 16 +- .../evm-contracts/ConstructorRevert.sol | 13 + .../evm-contracts/CounterFallback.sol | 15 + .../evm-contracts/CounterWithParam.sol | 40 + .../ts-integration/evm-contracts/Creator.sol | 19 + .../evm-contracts/CreatorFallback.sol | 19 + .../ts-integration/evm-contracts/ERC20.sol | 76 ++ .../evm-contracts/GasCaller.sol | 16 + .../evm-contracts/OpcodeTest.sol | 125 +++ .../evm-contracts/OpcodeTestFallback.sol | 141 +++ .../evm-contracts/ProxyCaller.sol | 25 + .../evm-contracts/SelfDestruct.sol | 15 + .../evm-contracts/UniswapFallback.sol | 124 +++ core/tests/ts-integration/package.json | 5 +- core/tests/ts-integration/src/helpers.ts | 47 + .../tests/evm-contracts.test.ts | 894 ++++++++++++++++++ yarn.lock | 485 +++++++++- 19 files changed, 2042 insertions(+), 44 deletions(-) create mode 100644 core/tests/ts-integration/evm-contracts/ConstructorRevert.sol create mode 100644 core/tests/ts-integration/evm-contracts/CounterFallback.sol create mode 100644 core/tests/ts-integration/evm-contracts/CounterWithParam.sol create mode 100644 core/tests/ts-integration/evm-contracts/Creator.sol create mode 100644 core/tests/ts-integration/evm-contracts/CreatorFallback.sol create mode 100644 core/tests/ts-integration/evm-contracts/ERC20.sol create mode 100644 core/tests/ts-integration/evm-contracts/GasCaller.sol create mode 100644 core/tests/ts-integration/evm-contracts/OpcodeTest.sol create mode 100644 core/tests/ts-integration/evm-contracts/OpcodeTestFallback.sol create mode 100644 core/tests/ts-integration/evm-contracts/ProxyCaller.sol create mode 100644 core/tests/ts-integration/evm-contracts/SelfDestruct.sol create mode 100644 core/tests/ts-integration/evm-contracts/UniswapFallback.sol create mode 100644 core/tests/ts-integration/tests/evm-contracts.test.ts diff --git a/contracts b/contracts index 8eeffbad0a0..b6a629f9b55 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 8eeffbad0a03e0f51f99d82be7e3e7cb08c36132 +Subproject commit b6a629f9b55b2b59409e086469c8cdff0c4bd56c diff --git a/core/lib/types/src/event/mod.rs b/core/lib/types/src/event/mod.rs index 6e86b88b2ff..6fc4c4444ed 100644 --- a/core/lib/types/src/event/mod.rs +++ b/core/lib/types/src/event/mod.rs @@ -10,7 +10,14 @@ use zksync_utils::{ }; use crate::{ - api::Log, ethabi, l2_to_l1_log::L2ToL1Log, tokens::{TokenInfo, TokenMetadata}, web3::{Bytes, Index}, zk_evm_types::{LogQuery, Timestamp}, Address, L1BatchNumber, StorageLogQuery, CONTRACT_DEPLOYER_ADDRESS, H256, KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, U256, U64 + api::Log, + ethabi, + l2_to_l1_log::L2ToL1Log, + tokens::{TokenInfo, TokenMetadata}, + web3::{Bytes, Index}, + zk_evm_types::{LogQuery, Timestamp}, + Address, L1BatchNumber, StorageLogQuery, CONTRACT_DEPLOYER_ADDRESS, H256, + KNOWN_CODES_STORAGE_ADDRESS, L1_MESSENGER_ADDRESS, U256, U64, }; #[cfg(test)] diff --git a/core/node/state_keeper/src/updates/l2_block_updates.rs b/core/node/state_keeper/src/updates/l2_block_updates.rs index 7db7f110e44..f3ea4804792 100644 --- a/core/node/state_keeper/src/updates/l2_block_updates.rs +++ b/core/node/state_keeper/src/updates/l2_block_updates.rs @@ -5,7 +5,13 @@ use zksync_multivm::{ vm_latest::TransactionVmExt, }; use zksync_types::{ - block::{BlockGasCount, L2BlockHasher}, event::{convert_vm_events_to_log_queries, extract_bytecodes_marked_as_known}, l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, tx::{tx_execution_info::TxExecutionStatus, ExecutionMetrics, TransactionExecutionResult}, vm_trace::Call, L2BlockNumber, ProtocolVersionId, StorageLogKind, StorageLogQuery, StorageLogWithPreviousValue, Transaction, VmEvent, H256 + block::{BlockGasCount, L2BlockHasher}, + event::{convert_vm_events_to_log_queries, extract_bytecodes_marked_as_known}, + l2_to_l1_log::{SystemL2ToL1Log, UserL2ToL1Log}, + tx::{tx_execution_info::TxExecutionStatus, ExecutionMetrics, TransactionExecutionResult}, + vm_trace::Call, + L2BlockNumber, ProtocolVersionId, StorageLogKind, StorageLogQuery, StorageLogWithPreviousValue, + Transaction, VmEvent, H256, }; use zksync_utils::bytecode::{hash_bytecode, CompressedBytecodeInfo}; @@ -122,9 +128,11 @@ impl L2BlockUpdates { .iter() .map(|bytecode| (hash_bytecode(bytecode), bytecode.clone())) .collect(); - new_known_factory_deps.into_iter().for_each(|(hash, bytecode)| { - tx_factory_deps.insert(hash, bytecode); - }); + new_known_factory_deps + .into_iter() + .for_each(|(hash, bytecode)| { + tx_factory_deps.insert(hash, bytecode); + }); // Save all bytecodes that were marked as known on the bootloader let known_bytecodes = saved_factory_deps.into_iter().map(|bytecode_hash| { diff --git a/core/tests/ts-integration/evm-contracts/ConstructorRevert.sol b/core/tests/ts-integration/evm-contracts/ConstructorRevert.sol new file mode 100644 index 00000000000..468b3395ce5 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/ConstructorRevert.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0; + +contract ConstructorRevert { + uint256 value; + + + constructor() { + revert('Failure string'); + } + +} diff --git a/core/tests/ts-integration/evm-contracts/CounterFallback.sol b/core/tests/ts-integration/evm-contracts/CounterFallback.sol new file mode 100644 index 00000000000..2535a05262b --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/CounterFallback.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract CounterFallback { + + function performCall() external { + uint256 value = 0; + value += 1; + } + + fallback() external { + this.performCall(); + } +} diff --git a/core/tests/ts-integration/evm-contracts/CounterWithParam.sol b/core/tests/ts-integration/evm-contracts/CounterWithParam.sol new file mode 100644 index 00000000000..40211a74654 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/CounterWithParam.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0; + +contract CounterWithParam { + uint256 value; + + + constructor(uint256 _startingValue) { + value = _startingValue; + } + + function increment(uint256 x) public { + value += x; + } + + function incrementWithRevertPayable(uint256 x, bool shouldRevert) payable public returns (uint256) { + return incrementWithRevert(x, shouldRevert); + } + + function incrementWithRevert(uint256 x, bool shouldRevert) public returns (uint256) { + value += x; + if(shouldRevert) { + revert("This method always reverts"); + } + return value; + } + + function set(uint256 x) public { + value = x; + } + + function get() public view returns (uint256) { + return value; + } + + function getBytes() public returns (bytes memory) { + return "Testing"; + } +} \ No newline at end of file diff --git a/core/tests/ts-integration/evm-contracts/Creator.sol b/core/tests/ts-integration/evm-contracts/Creator.sol new file mode 100644 index 00000000000..516bce4cbe3 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/Creator.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0; + +contract Creation { + function blockNumber() external view returns (uint256) { + return block.number; + } +} + +contract Creator { + function create() external { + new Creation(); + } + + function getCreationRuntimeCode() external pure returns(bytes memory){ + return type(Creation).runtimeCode; + } +} diff --git a/core/tests/ts-integration/evm-contracts/CreatorFallback.sol b/core/tests/ts-integration/evm-contracts/CreatorFallback.sol new file mode 100644 index 00000000000..ce532aa69b7 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/CreatorFallback.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0; + +contract Creation { + function blockNumber() external view returns (uint256) { + return block.number; + } +} + +contract CreatorFallback { + function performCall() external { + new Creation(); + type(Creation).runtimeCode; + } + fallback() external { + this.performCall(); + } +} diff --git a/core/tests/ts-integration/evm-contracts/ERC20.sol b/core/tests/ts-integration/evm-contracts/ERC20.sol new file mode 100644 index 00000000000..5c1c89faf7a --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/ERC20.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0; + +contract ERC20{ + string public symbol; + string public name; + uint8 public decimals; + uint public totalSupply; + + mapping(address => uint) balances; + mapping(address => mapping(address => uint)) allowed; + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + + constructor() { + symbol = "TEST"; + name = "Test Coin"; + decimals = 18; + totalSupply = 1000000; + balances[msg.sender] = totalSupply; + emit Transfer(address(0), msg.sender, totalSupply); + } + + + function balanceOf(address tokenOwner) public view returns (uint balance) { + return balances[tokenOwner]; + } + + function transfer(address to, uint tokens) public returns (bool success) { + balances[msg.sender] = safeSub(balances[msg.sender], tokens); + balances[to] = safeAdd(balances[to], tokens); + emit Transfer(msg.sender, to, tokens); + return true; + } + + function approve(address spender, uint tokens) public returns (bool success) { + allowed[msg.sender][spender] = tokens; + emit Approval(msg.sender, spender, tokens); + return true; + } + + function transferFrom(address from, address to, uint tokens) public returns (bool success) { + balances[from] = safeSub(balances[from], tokens); + allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens); + balances[to] = safeAdd(balances[to], tokens); + emit Transfer(from, to, tokens); + return true; + } + + function allowance(address tokenOwner, address spender) public view returns (uint remaining) { + return allowed[tokenOwner][spender]; + } + + function safeAdd(uint a, uint b) internal pure returns (uint c) { + c = a + b; + require(c >= a); + } + + function safeSub(uint a, uint b) internal pure returns (uint c) { + require(b <= a); + c = a - b; + } + + function safeMul(uint a, uint b) internal pure returns (uint c) { + c = a * b; + require(a == 0 || c / a == b); + } + + function safeDiv(uint a, uint b) internal pure returns (uint c) { + require(b > 0); + c = a / b; + } + +} diff --git a/core/tests/ts-integration/evm-contracts/GasCaller.sol b/core/tests/ts-integration/evm-contracts/GasCaller.sol new file mode 100644 index 00000000000..4fd43a235b5 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/GasCaller.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract GasCaller { + uint256 _resultGas; + + function callAndGetGas(address _to) external returns (uint256){ + uint256 startGas = gasleft(); + // Just doing a call to an address + (bool success, ) = _to.call(""); + require(success); + _resultGas = startGas - gasleft(); + return _resultGas; + } +} diff --git a/core/tests/ts-integration/evm-contracts/OpcodeTest.sol b/core/tests/ts-integration/evm-contracts/OpcodeTest.sol new file mode 100644 index 00000000000..9c739846f33 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/OpcodeTest.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract OpcodeTest { + + function execute() external { + uint256 loaded = 1; + uint256 tmp; + uint256 prevBlock = block.number - 1; + assembly { + loaded := add(loaded, 1) + loaded := mul(loaded, 2) + loaded := sub(loaded, 1) + loaded := div(loaded, 2) + loaded := sdiv(loaded, 2) + loaded := mod(loaded, 2) + // ADDMOD + // MULMOD + loaded := exp(loaded, 2) + loaded := signextend(loaded, 2) + tmp := lt(loaded, 2) + tmp := gt(loaded, 2) + tmp := slt(loaded, 2) + tmp := sgt(loaded, 2) + tmp := eq(loaded, 2) + tmp := iszero(tmp) + tmp := and(1,1) + tmp := or(1,1) + tmp := xor(1,1) + tmp := not(tmp) + tmp := byte(tmp,1) + tmp := shl(tmp,1) + tmp := shr(tmp,1) + tmp := sar(tmp,1) + tmp := keccak256(0, 0x40) + tmp := address() + tmp := balance(0x00) + tmp := origin() + tmp := caller() + tmp := callvalue() + // CALLDATALOAD + tmp := calldatasize() + // CALLDATACOPY + tmp := codesize() + // CODECOPY + tmp := gasprice() + // EXTCODESIZE + // EXTCODECOPY + tmp := returndatasize() + // RETURNDATACOPY + // EXTCODEHASH + tmp := blockhash(prevBlock) + tmp := coinbase() + tmp := timestamp() + tmp := number() + tmp := prevrandao() + tmp := gaslimit() + tmp := chainid() + tmp := selfbalance() + tmp := basefee() + // POP + tmp := mload(1) + mstore(1024,1) + mstore8(10242,1) + tmp := sload(0) + sstore(0,1) + // JUMP + // JUMPI + // PC + tmp := msize() + tmp := gas() + // JUMPDEST + // PUSH0...PUSH32 + // DUP1...DUP16 + // SWAP1...SWAP16 + // LOG0...LOG4 + // CREATE + // CALL + // CALLCODE + // RETURN + // DELEGATECALL + // CREATE2 + // STATICCALL + // REVERT + // INVALID + // selfdestruct(sender) + } + + // tmp = 0; + // tmp = 0x11; + // tmp = 0x2211; + // tmp = 0x332211; + // tmp = 0x44332211; + // tmp = 0x5544332211; + // tmp = 0x665544332211; + // tmp = 0x77665544332211; + // tmp = 0x8877665544332211; + // tmp = 0x998877665544332211; + // tmp = 0xaa998877665544332211; + // tmp = 0xbbaa998877665544332211; + // tmp = 0xccbbaa998877665544332211; + // tmp = 0xddccbbaa998877665544332211; + // tmp = 0xeeddccbbaa998877665544332211; + // tmp = 0xffeeddccbbaa998877665544332211; + // tmp = 0x11ffeeddccbbaa998877665544332211; + // tmp = 0x2211ffeeddccbbaa998877665544332211; + // tmp = 0x332211ffeeddccbbaa998877665544332211; + // tmp = 0x44332211ffeeddccbbaa998877665544332211; + // tmp = uint256(uint160(0x5544332211FFeeDDCcbbAa998877665544332211)); + // tmp = 0x665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x77665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x8877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0xff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x11ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x2211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x44332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x5544332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x665544332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x77665544332211ff998877665544332211ffeeddccbbaa998877665544332211; + } + +} diff --git a/core/tests/ts-integration/evm-contracts/OpcodeTestFallback.sol b/core/tests/ts-integration/evm-contracts/OpcodeTestFallback.sol new file mode 100644 index 00000000000..68d64fc074d --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/OpcodeTestFallback.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract OpcodeTestFallback { + + function performCall() external { + uint256 loaded = 1; + uint256 tmp; + uint256 prevBlock = block.number - 1; + assembly { + loaded := add(loaded, 1) + loaded := mul(loaded, 2) + loaded := sub(loaded, 1) + loaded := div(loaded, 2) + loaded := sdiv(loaded, 2) + loaded := mod(loaded, 2) + // ADDMOD + // MULMOD + loaded := exp(loaded, 2) + loaded := signextend(loaded, 2) + tmp := lt(loaded, 2) + tmp := gt(loaded, 2) + tmp := slt(loaded, 2) + tmp := sgt(loaded, 2) + tmp := eq(loaded, 2) + tmp := iszero(tmp) + tmp := and(1,1) + tmp := or(1,1) + tmp := xor(1,1) + tmp := not(tmp) + tmp := byte(tmp,1) + tmp := shl(tmp,1) + tmp := shr(tmp,1) + tmp := sar(tmp,1) + tmp := keccak256(0, 0x40) + tmp := address() + tmp := balance(0x00) + tmp := origin() + tmp := caller() + tmp := callvalue() + // CALLDATALOAD + tmp := calldatasize() + // CALLDATACOPY + tmp := codesize() + // CODECOPY + tmp := gasprice() + // EXTCODESIZE + // EXTCODECOPY + tmp := returndatasize() + // RETURNDATACOPY + // EXTCODEHASH + tmp := blockhash(prevBlock) + tmp := coinbase() + tmp := timestamp() + tmp := number() + tmp := prevrandao() + tmp := gaslimit() + tmp := chainid() + tmp := selfbalance() + tmp := basefee() + // POP + tmp := mload(1) + mstore(1024,1) + mstore8(10242,1) + tmp := sload(0) + sstore(0,1) + // JUMP + // JUMPI + // PC + tmp := msize() + tmp := gas() + // JUMPDEST + // PUSH0...PUSH32 + // DUP1...DUP16 + // SWAP1...SWAP16 + // LOG0...LOG4 + // CREATE + // CALL + // CALLCODE + // RETURN + // DELEGATECALL + // CREATE2 + // STATICCALL + // REVERT + // INVALID + // selfdestruct(sender) + tmp := calldataload(0) + calldatacopy(10,0,1) + codecopy(10,0,1) + tmp := extcodesize(0) + extcodecopy(address(),10,0,1) + returndatacopy(10,0,1) + pop(extcodehash(0)) + log0(0,30) + log1(0,30,30) + log2(0,30,30,30) + log3(0,30,30,30,30) + log4(0,30,30,30,30,30) + } + + // tmp = 0; + // tmp = 0x11; + // tmp = 0x2211; + // tmp = 0x332211; + // tmp = 0x44332211; + // tmp = 0x5544332211; + // tmp = 0x665544332211; + // tmp = 0x77665544332211; + // tmp = 0x8877665544332211; + // tmp = 0x998877665544332211; + // tmp = 0xaa998877665544332211; + // tmp = 0xbbaa998877665544332211; + // tmp = 0xccbbaa998877665544332211; + // tmp = 0xddccbbaa998877665544332211; + // tmp = 0xeeddccbbaa998877665544332211; + // tmp = 0xffeeddccbbaa998877665544332211; + // tmp = 0x11ffeeddccbbaa998877665544332211; + // tmp = 0x2211ffeeddccbbaa998877665544332211; + // tmp = 0x332211ffeeddccbbaa998877665544332211; + // tmp = 0x44332211ffeeddccbbaa998877665544332211; + // tmp = uint256(uint160(0x5544332211FFeeDDCcbbAa998877665544332211)); + // tmp = 0x665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x77665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x8877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0xff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x11ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x2211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x44332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x5544332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x665544332211ff998877665544332211ffeeddccbbaa998877665544332211; + // tmp = 0x77665544332211ff998877665544332211ffeeddccbbaa998877665544332211; + } + + fallback() external { + this.performCall(); + } + +} diff --git a/core/tests/ts-integration/evm-contracts/ProxyCaller.sol b/core/tests/ts-integration/evm-contracts/ProxyCaller.sol new file mode 100644 index 00000000000..05200d06a31 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/ProxyCaller.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0; + +interface ICounterWithParam { + function increment(uint256 x) external; + + function get() external view returns (uint256); + + function getBytes() external returns (bytes memory); +} + +contract ProxyCaller { + function executeIncrememt(address dest, uint256 x) external{ + ICounterWithParam(dest).increment(x); + } + + function proxyGet(address dest) external view returns (uint256){ + return ICounterWithParam(dest).get(); + } + + function proxyGetBytes(address dest) external returns(bytes memory returnData){ + return ICounterWithParam(dest).getBytes(); + } +} \ No newline at end of file diff --git a/core/tests/ts-integration/evm-contracts/SelfDestruct.sol b/core/tests/ts-integration/evm-contracts/SelfDestruct.sol new file mode 100644 index 00000000000..3e3923de336 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/SelfDestruct.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0; + +contract SelfDestruct { + + constructor() payable { + } + + function destroy(address recipient) external{ + assembly { + selfdestruct(recipient) + } + } +} \ No newline at end of file diff --git a/core/tests/ts-integration/evm-contracts/UniswapFallback.sol b/core/tests/ts-integration/evm-contracts/UniswapFallback.sol new file mode 100644 index 00000000000..b2d631d3551 --- /dev/null +++ b/core/tests/ts-integration/evm-contracts/UniswapFallback.sol @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +interface IUniswapV2ERC20 { + event Approval(address indexed owner, address indexed spender, uint value); + event Transfer(address indexed from, address indexed to, uint value); + + function name() external pure returns (string memory); + + function symbol() external pure returns (string memory); + + function decimals() external pure returns (uint8); + + function totalSupply() external returns (uint); + + function balanceOf(address owner) external returns (uint); + + function allowance(address owner, address spender) external returns (uint); + + function approve(address spender, uint value) external returns (bool); + + function transfer(address to, uint value) external returns (bool); + + function transferFrom( + address from, + address to, + uint value + ) external returns (bool); + + function DOMAIN_SEPARATOR() external returns (bytes32); + + function PERMIT_TYPEHASH() external pure returns (bytes32); + + function nonces(address owner) external returns (uint); + + function permit( + address owner, + address spender, + uint value, + uint deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} + +interface IUniswapV2Pair { + event Mint(address indexed sender, uint amount0, uint amount1); + event Burn( + address indexed sender, + uint amount0, + uint amount1, + address indexed to + ); + event Swap( + address indexed sender, + uint amount0In, + uint amount1In, + uint amount0Out, + uint amount1Out, + address indexed to + ); + event Sync(uint112 reserve0, uint112 reserve1); + + function MINIMUM_LIQUIDITY() external pure returns (uint); + + function factory() external returns (address); + + function token0() external returns (address); + + function token1() external returns (address); + + function getReserves() + external + returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); + + function price0CumulativeLast() external returns (uint); + + function price1CumulativeLast() external returns (uint); + + function kLast() external returns (uint); + + function mint(address to) external returns (uint liquidity); + + function burn(address to) external returns (uint amount0, uint amount1); + + function swap( + uint amount0Out, + uint amount1Out, + address to, + bytes calldata data + ) external; + + function skim(address to) external; + + function sync() external; + + function initialize(address, address) external; +} + +contract UniswapFallback { + IUniswapV2Pair public uniswapPair; + IUniswapV2ERC20 public uniswapPair2; + address public alice_address; + + function setUniswapAddress(address _uniswap_address) public { + uniswapPair = IUniswapV2Pair(_uniswap_address); + uniswapPair2 = IUniswapV2ERC20(_uniswap_address); + } + function setAliceAddress(address _alice_address) public { + alice_address = _alice_address; + } + // Fallback function + fallback() external { + // Implement any logic you want the contract to perform when it receives Ether + // This function will be called when the contract receives Ether and no other function matches the call data + uniswapPair.mint(alice_address); + uniswapPair.swap(0,5000,alice_address,"0x"); + uint balance = uniswapPair2.balanceOf(alice_address); + //uniswapPair2.transfer(address(uniswapPair),balance); + //uniswapPair.burn(alice_address); + } +} diff --git a/core/tests/ts-integration/package.json b/core/tests/ts-integration/package.json index 03bd84bb3f4..e6c414bbaaf 100644 --- a/core/tests/ts-integration/package.json +++ b/core/tests/ts-integration/package.json @@ -32,6 +32,9 @@ "typescript": "^4.3.5", "zksync-ethers": "^6.9.0", "elliptic": "^6.5.5", - "yaml": "^2.4.2" + "yaml": "^2.4.2", + "zksync-web3": "^0.15.5", + "csv-parser": "^3.0.0", + "csv-writer": "^1.6.0" } } diff --git a/core/tests/ts-integration/src/helpers.ts b/core/tests/ts-integration/src/helpers.ts index 8e31c1a691f..327a4912a6c 100644 --- a/core/tests/ts-integration/src/helpers.ts +++ b/core/tests/ts-integration/src/helpers.ts @@ -4,6 +4,8 @@ import * as ethers from 'ethers'; import * as hre from 'hardhat'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-solc/dist/src/types'; +const solc = require('solc'); + export const SYSTEM_CONTEXT_ADDRESS = '0x000000000000000000000000000000000000800b'; /** @@ -141,3 +143,48 @@ export function bigIntMax(...args: bigint[]) { return args.reduce((max, current) => (current > max ? current : max), args[0]); } + +/** Compiles and returns artifacts for a Solidity (EVM) contract + * + * @param contractPath The path of the contract relative to the contracts directory + * @param args Constructor arguments for the contract + * @returns The transaction data for the contract deployment + */ +export function getEVMArtifact(contractPath: string, contractName: string | undefined = undefined): any { + const compilerParams = { + language: 'Solidity', + sources: { + contract: { + content: getContractSource(contractPath) + } + }, + settings: { + outputSelection: { + '*': { + '*': ['*'] + } + } + } + } as any; + if (contractName === undefined) { + const splitPath = contractPath.split('/'); + contractName = splitPath[splitPath.length - 1]; + } + + const artifact = JSON.parse(solc.compile(JSON.stringify(compilerParams))).contracts['contract'][ + contractName.split('.')[0] + ]; + + return artifact; +} + +/** Gets the deployment transaction data for a given contract path and parameters + * + * @param initiator Wallet that should be used + * @param contractPath The path of the contract relative to the contracts directory + * @param args Constructor arguments for the contract + * @returns The transaction data for the contract deployment + */ +export function getEVMContractFactory(initiator: zksync.Wallet, artifact: any): ethers.ContractFactory { + return new ethers.ContractFactory(artifact.abi, '0x' + artifact.evm.bytecode.object, initiator); +} diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts new file mode 100644 index 00000000000..57e2a61a2f0 --- /dev/null +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -0,0 +1,894 @@ +/** + * Generic tests checking evm equivalence smart contract behavior. + * + * Note: if you are going to write multiple tests checking specific topic (e.g. `CREATE2` behavior or something like this), + * consider creating a separate suite. + * Let's try to keep only relatively simple and self-contained tests here. + */ + +import { TestMaster } from '../src'; +import { deployContract, getEVMArtifact, getEVMContractFactory, getTestContract } from '../src/helpers'; + +import * as ethers from 'ethers'; +import * as zksync from 'zksync-ethers'; + +import fs, { PathLike } from 'fs'; +import csv from 'csv-parser'; +import { createObjectCsvWriter } from 'csv-writer'; + +const contracts = { + tester: getTestContract('TestEVMCreate'), + erc20: getTestContract('ERC20'), + uniswapV2Pair: getTestContract('UniswapV2Pair'), + uniswapV2Factory: getTestContract('UniswapV2Factory') +}; + +const artifacts = { + counter: getEVMArtifact('../evm-contracts/CounterWithParam.sol'), + proxyCaller: getEVMArtifact('../evm-contracts/ProxyCaller.sol'), + creator: getEVMArtifact('../evm-contracts/Creator.sol'), + erc20: getEVMArtifact('../evm-contracts/ERC20.sol'), + constructorRevert: getEVMArtifact('../evm-contracts/ConstructorRevert.sol'), + uniswapV2Pair: getEVMArtifact('../contracts/uniswap-v2/UniswapV2Factory.sol', 'UniswapV2Pair.sol'), + uniswapV2Factory: getEVMArtifact('../contracts/uniswap-v2/UniswapV2Factory.sol', 'UniswapV2Factory.sol'), + opcodeTest: getEVMArtifact('../evm-contracts/OpcodeTest.sol'), + selfDestruct: getEVMArtifact('../evm-contracts/SelfDestruct.sol'), + gasCaller: getEVMArtifact('../evm-contracts/GasCaller.sol'), + counterFallback: getEVMArtifact('../evm-contracts/CounterFallback.sol'), + uniswapFallback: getEVMArtifact('../evm-contracts/UniswapFallback.sol'), + creatorFallback: getEVMArtifact('../evm-contracts/CreatorFallback.sol'), + opcodeTestFallback: getEVMArtifact('../evm-contracts/OpcodeTestFallback.sol') +}; + +const initBytecode = '0x69602a60005260206000f3600052600a6016f3'; +const runtimeBytecode = '0x602a60005260206000f3'; + +let gasLimit = '0x01ffffff'; + +const logGasCosts = false; +describe('EVM equivalence contract', () => { + let testMaster: TestMaster; + let alice: zksync.Wallet; + + // Contracts shared in several tests. + let evmCreateTester: zksync.Contract; + let deployer: zksync.Contract; + + beforeAll(async () => { + testMaster = TestMaster.getInstance(__filename); + alice = testMaster.mainAccount(); + + evmCreateTester = await deployContract(alice, contracts.tester, []); + deployer = new zksync.Contract(zksync.utils.CONTRACT_DEPLOYER_ADDRESS, zksync.utils.CONTRACT_DEPLOYER, alice); + }); + + describe('Gas consumption', () => { + test("Should compare gas against counter fallback contract's call", async () => { + const gasCallerContract = await deploygasCallerContract(alice, artifacts.gasCaller); + + const counterContract = await deploygasCallerContract(alice, artifacts.counterFallback); + + let result = (await gasCallerContract.callAndGetGas.staticCall(counterContract.address)).toString(); + + const expected_gas = '3617'; // Gas cost when run with solidity interpreter + expect(result).toEqual(expected_gas); + }); + + test("Should compare gas against creator fallback contract's call", async () => { + const gasCallerContract = await deploygasCallerContract(alice, artifacts.gasCaller); + + const creatorContract = await deploygasCallerContract(alice, artifacts.creatorFallback); + + let result = (await gasCallerContract.callStatic.callAndGetGas(creatorContract.address)).toString(); + + const expected_gas = '70601'; // Gas cost when run with solidity interpreter + expect(result).toEqual(expected_gas); + }); + + xtest("Should compare gas against opcode test fallback contract's call", async () => { + const gasCallerContract = await deploygasCallerContract(alice, artifacts.gasCaller); + + const counterContract = await deploygasCallerContract(alice, artifacts.opcodeTestFallback); + + let result = (await gasCallerContract.callStatic.callAndGetGas(counterContract.address)).toString(); + + const expected_gas = '34763'; // Gas cost when run with solidity interpreter + expect(result).toEqual(expected_gas); + }); + }); + + describe('Contract creation', () => { + describe('Create from EOA', () => { + test('Should create evm contract from EOA and allow view and non-view calls', async () => { + const args = [1]; + const factory = getEVMContractFactory(alice, artifacts.counter); + const contract = await factory.deploy(args); + await contract.deployTransaction.wait(); + const receipt = await alice.provider.getTransactionReceipt(contract.deployTransaction.hash); + + await assertCreatedCorrectly( + deployer, + contract.address, + '0x' + artifacts.counter.evm.deployedBytecode.object, + receipt.logs + ); + + expect((await contract.callStatic.get()).toString()).toEqual('1'); + await (await contract.increment(1)).wait(); + expect((await contract.callStatic.get()).toString()).toEqual('2'); + }); + + test('Should create2 evm contract from ZKEVM contract', async () => { + const salt = ethers.utils.randomBytes(32); + + const expectedAddress = ethers.utils.getCreate2Address( + evmCreateTester.address, + salt, + ethers.utils.keccak256(initBytecode) + ); + + const receipt = await (await evmCreateTester.create2(salt, initBytecode)).wait(); + + await assertCreatedCorrectly(deployer, expectedAddress, runtimeBytecode, receipt.logs); + + try { + await (await evmCreateTester.create2(salt, initBytecode, { gasLimit })).wait(); + } catch (e) { + // Should fail + return; + } + throw 'Should fail to create2 the same contract with same salt twice'; + }); + + test('Should propegate revert in constructor', async () => { + const factory = getEVMContractFactory(alice, artifacts.constructorRevert); + const contract = await factory.deploy({ gasLimit }); + + let failReason; + + try { + await contract.deployTransaction.wait(); + } catch (e: any) { + failReason = e.reason; + } + + expect(failReason).toBe('transaction failed'); + }); + + test('Should NOT create evm contract from EOA when `to` is address(0x0)', async () => { + const args = [1]; + + const factory = getEVMContractFactory(alice, artifacts.counter); + const transaction = await factory.getDeployTransaction(args); + transaction.to = '0x0000000000000000000000000000000000000000'; + + const result = await (await alice.sendTransaction(transaction)).wait(); + const expectedAddressCreate = ethers.utils.getContractAddress({ + from: alice.address, + nonce: await alice.getNonce() + }); + + await assertContractNotCreated(deployer, expectedAddressCreate); + }); + + // test('Should SENDALL', async () => { + // const salt = ethers.utils.randomBytes(32); + // const selfDestructBytecode = '0x' + artifacts.selfDestruct.evm.bytecode.object; + // const hash = ethers.utils.keccak256(selfDestructBytecode); + + // const selfDestructFactory = getEVMContractFactory(alice, artifacts.selfDestruct); + // const selfDestructAddress = ethers.utils.getCreate2Address(evmCreateTester.address, salt, hash); + // const selfDestruct = selfDestructFactory.attach(selfDestructAddress); + // const beneficiary = testMaster.newEmptyAccount(); + + // await (await evmCreateTester.create2(salt, selfDestructBytecode, { value: 1000 })).wait(); + // expect((await alice.provider.getBalance(selfDestructAddress)).toNumber()).toBe(1000); + + // await (await selfDestruct.destroy(beneficiary.address)).wait(); + // expect((await alice.provider.getBalance(beneficiary.address)).toNumber()).toBe(1000); + + // let failReason; + + // try { + // await (await evmCreateTester.create2(salt, selfDestructBytecode)).wait(); + // } catch (e: any) { + // failReason = e.error.reason; + // } + + // expect(failReason).toBe("execution reverted: Can't create on existing contract address"); + // }); + }); + }); + + describe('Inter-contract calls', () => { + test('Calls (read/write) between EVM contracts should work correctly', async () => { + const args = [1]; + + const counterFactory = getEVMContractFactory(alice, artifacts.counter); + const counterContract = await counterFactory.deploy(args); + await counterContract.deployTransaction.wait(); + await alice.provider.getTransactionReceipt(counterContract.deployTransaction.hash); + + const proxyCallerFactory = getEVMContractFactory(alice, artifacts.proxyCaller); + const proxyCallerContract = await proxyCallerFactory.deploy(); + await proxyCallerContract.deployTransaction.wait(); + await alice.provider.getTransactionReceipt(proxyCallerContract.deployTransaction.hash); + + expect((await proxyCallerContract.proxyGet(counterContract.address)).toString()).toEqual('1'); + + await (await proxyCallerContract.executeIncrememt(counterContract.address, 1)).wait(); + + expect((await proxyCallerContract.proxyGet(counterContract.address)).toString()).toEqual('2'); + + expect((await proxyCallerContract.callStatic.proxyGetBytes(counterContract.address)).toString()).toEqual( + '0x54657374696e67' + ); + }); + + test('Create opcode works correctly', async () => { + const creatorFactory = getEVMContractFactory(alice, artifacts.creator); + const creatorContract = await creatorFactory.deploy(); + await creatorContract.deployTransaction.wait(); + + dumpOpcodeLogs(creatorContract.deployTransaction.hash, alice.provider); + + const nonce = 1; + + const runtimeBytecode = await creatorContract.getCreationRuntimeCode(); + + const expectedAddress = ethers.utils.getContractAddress({ + from: creatorContract.address, + nonce + }); + + const result = await (await creatorContract.create()).wait(); + dumpOpcodeLogs(result.transactionHash, alice.provider); + + await assertCreatedCorrectly(deployer, expectedAddress, runtimeBytecode, result.logs); + }); + + test('Should revert correctly', async () => { + const args = [1]; + + const counterFactory = getEVMContractFactory(alice, artifacts.counter); + const counterContract = await counterFactory.deploy(args); + await counterContract.deployTransaction.wait(); + + dumpOpcodeLogs(counterContract.deployTransaction.hash, alice.provider); + + let errorString; + + try { + await counterContract.callStatic.incrementWithRevert(1, true); + } catch (e: any) { + errorString = e.reason; + } + expect(errorString).toEqual('This method always reverts'); + }); + }); + + // NOTE: Gas cost comparisons should be done on a *fresh* chain that doesn't have e.g. bytecodes already published + describe('ERC20', () => { + let evmToken: ethers.Contract; + let nativeToken: zksync.Contract; + let userAccount: zksync.Wallet; + let deployLogged: boolean = false; + + beforeEach(async () => { + const erc20Factory = getEVMContractFactory(alice, artifacts.erc20); + evmToken = await erc20Factory.deploy(); + await evmToken.deployTransaction.wait(); + nativeToken = await deployContract(alice, contracts.erc20, []); + + dumpOpcodeLogs(evmToken.deployTransaction.hash, alice.provider); + userAccount = testMaster.newEmptyAccount(); + // Only log the first deployment + if (logGasCosts && !deployLogged) { + console.log( + 'ERC20 native deploy gas: ' + + (await alice.provider.getTransactionReceipt(nativeToken.deployTransaction.hash)).gasUsed + ); + console.log( + 'ERC20 evm deploy gas: ' + + (await alice.provider.getTransactionReceipt(evmToken.deployTransaction.hash)).gasUsed + ); + deployLogged = true; + } + await ( + await alice.sendTransaction({ + to: userAccount.address, + value: ethers.BigNumber.from('0xffffffffffffff') + }) + ).wait(); + }); + + test('view functions should work', async () => { + const evmBalanceOfCost = await evmToken.estimateGas.balanceOf(alice.address); + const nativeBalanceOfCost = await nativeToken.estimateGas.balanceOf(alice.address); + if (logGasCosts) { + console.log('ERC20 native balanceOf gas: ' + nativeBalanceOfCost.toString()); + console.log('ERC20 evm balanceOf gas: ' + evmBalanceOfCost.toString()); + } + expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('1000000'); + expect((await evmToken.totalSupply()).toString()).toEqual('1000000'); + expect((await evmToken.balanceOf(userAccount.address)).toString()).toEqual('0'); + }); + + test('transfer should work', async () => { + expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('1000000'); + const evmTransferTx = await (await evmToken.transfer(userAccount.address, 100000)).wait(); + const nativeTransferTx = await (await nativeToken.transfer(userAccount.address, 100000)).wait(); + if (logGasCosts) { + console.log('ERC20 native transfer gas: ' + nativeTransferTx.gasUsed.toString()); + console.log('ERC20 evm transfer gas: ' + evmTransferTx.gasUsed.toString()); + } + dumpOpcodeLogs(evmTransferTx.transactionHash, alice.provider); + + expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('900000'); + expect((await evmToken.balanceOf(userAccount.address)).toString()).toEqual('100000'); + }); + + test('approve & transferFrom should work', async () => { + expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('1000000'); + const evmApproveTx = await (await evmToken.connect(alice).approve(userAccount.address, 100000)).wait(); + const nativeApproveTx = await ( + await nativeToken.connect(alice).approve(userAccount.address, 100000) + ).wait(); + if (logGasCosts) { + console.log('ERC20 native approve gas: ' + nativeApproveTx.gasUsed.toString()); + console.log('ERC20 evm approve gas: ' + evmApproveTx.gasUsed.toString()); + } + dumpOpcodeLogs(evmApproveTx.transactionHash, alice.provider); + + const evmTransferFromTx = await ( + await evmToken.connect(userAccount).transferFrom(alice.address, userAccount.address, 100000) + ).wait(); + const nativeTransferFromTx = await ( + await nativeToken.connect(userAccount).transferFrom(alice.address, userAccount.address, 100000) + ).wait(); + if (logGasCosts) { + console.log('ERC20 native transferFrom gas: ' + nativeTransferFromTx.gasUsed.toString()); + console.log('ERC20 evm transferFrom gas: ' + evmTransferFromTx.gasUsed.toString()); + } + dumpOpcodeLogs(evmTransferFromTx.transactionHash, alice.provider); + + expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('900000'); + expect((await evmToken.balanceOf(userAccount.address)).toString()).toEqual('100000'); + }); + }); + + // NOTE: Gas cost comparisons should be done on a *fresh* chain that doesn't have e.g. bytecodes already published + describe('Uniswap-v2', () => { + let evmToken1: ethers.Contract; + let evmToken2: ethers.Contract; + let evmUniswapFactory: ethers.Contract; + let nativeUniswapFactory: ethers.Contract; + let evmUniswapPair: ethers.Contract; + let nativeUniswapPair: ethers.Contract; + + let deployLogged: boolean = false; + const NEW_PAIR_TOPIC = '0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9'; + + beforeEach(async () => { + const erc20Factory = getEVMContractFactory(alice, artifacts.erc20); + evmToken1 = await erc20Factory.deploy({ gasLimit }); + await evmToken1.deployTransaction.wait(); + evmToken2 = await erc20Factory.deploy(); + await evmToken2.deployTransaction.wait(); + + const evmUniswapFactoryFactory = getEVMContractFactory(alice, artifacts.uniswapV2Factory); + evmUniswapFactory = await evmUniswapFactoryFactory.deploy('0x0000000000000000000000000000000000000000'); + await evmUniswapFactory.deployTransaction.wait(); + + nativeUniswapFactory = await deployContract( + alice, + contracts.uniswapV2Factory, + ['0x0000000000000000000000000000000000000000'], + undefined, + { + customData: { + factoryDeps: [contracts.uniswapV2Pair.bytecode] + } + } + ); + + const evmPairReceipt = await ( + await evmUniswapFactory.createPair(evmToken1.address, evmToken2.address) + ).wait(); + + const nativePairReceipt = await ( + await nativeUniswapFactory.createPair(evmToken1.address, evmToken2.address) + ).wait(); + dumpOpcodeLogs(evmUniswapFactory.deployTransaction.hash, alice.provider); + dumpOpcodeLogs(evmPairReceipt.transactionHash, alice.provider); + + const evmUniswapPairFactory = getEVMContractFactory(alice, artifacts.uniswapV2Pair); + const nativeUniswapPairFactory = new zksync.ContractFactory( + contracts.uniswapV2Pair.abi, + contracts.uniswapV2Pair.bytecode, + alice + ); + evmUniswapPair = evmUniswapPairFactory.attach( + ethers.utils.defaultAbiCoder.decode( + ['address', 'uint256'], + evmPairReceipt.logs.find((log: any) => log.topics[0] === NEW_PAIR_TOPIC).data + )[0] + ); + nativeUniswapPair = nativeUniswapPairFactory.attach( + ethers.utils.defaultAbiCoder.decode( + ['address', 'uint256'], + nativePairReceipt.logs.find((log: any) => log.topics[0] === NEW_PAIR_TOPIC).data + )[0] + ); + const token1IsFirst = (await evmUniswapPair.token0()).toString() === evmToken1.address; + if (!token1IsFirst) { + [evmToken1, evmToken2] = [evmToken2, evmToken1]; + } + await (await evmToken1.transfer(evmUniswapPair.address, 100000)).wait(); + await (await evmToken1.transfer(nativeUniswapPair.address, 100000)).wait(); + await (await evmToken2.transfer(evmUniswapPair.address, 100000)).wait(); + await (await evmToken2.transfer(nativeUniswapPair.address, 100000)).wait(); + + // Only log the first deployment + if (logGasCosts && !deployLogged) { + console.log( + 'Uniswap Factory native deploy gas: ' + + (await alice.provider.getTransactionReceipt(nativeUniswapFactory.deployTransaction.hash)) + .gasUsed + ); + console.log( + 'Uniswap Factory evm deploy gas: ' + + (await alice.provider.getTransactionReceipt(evmUniswapFactory.deployTransaction.hash)).gasUsed + ); + console.log('Uniswap Pair native create gas: ' + nativePairReceipt.gasUsed); + console.log('Uniswap Pair evm create gas: ' + evmPairReceipt.gasUsed); + deployLogged = true; + } + }); + + test('mint, swap, and burn should work', async () => { + const evmMintReceipt = await (await evmUniswapPair.mint(alice.address)).wait(); + const nativeMintReceipt = await (await nativeUniswapPair.mint(alice.address)).wait(); + dumpOpcodeLogs(evmMintReceipt.transactionHash, alice.provider); + + await (await evmToken1.transfer(evmUniswapPair.address, 10000)).wait(); + await (await evmToken1.transfer(nativeUniswapPair.address, 10000)).wait(); + const evmSwapReceipt = await (await evmUniswapPair.swap(0, 5000, alice.address, '0x')).wait(); + const nativeSwapReceipt = await (await nativeUniswapPair.swap(0, 5000, alice.address, '0x')).wait(); + dumpOpcodeLogs(evmSwapReceipt.transactionHash, alice.provider); + + const evmLiquidityTransfer = await ( + await evmUniswapPair.transfer( + evmUniswapPair.address, + (await evmUniswapPair.balanceOf(alice.address)).toString() + ) + ).wait(); + await ( + await nativeUniswapPair.transfer( + nativeUniswapPair.address, + (await nativeUniswapPair.balanceOf(alice.address)).toString() + ) + ).wait(); + const evmBurnReceipt = await (await evmUniswapPair.burn(alice.address)).wait(); + const nativeBurnReceipt = await (await nativeUniswapPair.burn(alice.address)).wait(); + expect(Number((await evmToken1.balanceOf(alice.address)).toString())).toBeGreaterThanOrEqual(990000); + expect(Number((await evmToken2.balanceOf(alice.address)).toString())).toBeGreaterThanOrEqual(990000); + + if (logGasCosts) { + console.log('UniswapV2Pair native mint gas: ' + nativeMintReceipt.gasUsed); + console.log('UniswapV2Pair evm mint gas: ' + evmMintReceipt.gasUsed); + console.log('UniswapV2Pair native swap gas: ' + nativeSwapReceipt.gasUsed); + console.log('UniswapV2Pair evm swap gas: ' + evmSwapReceipt.gasUsed); + console.log('UniswapV2Pair native burn gas: ' + nativeBurnReceipt.gasUsed); + console.log('UniswapV2Pair evm burn gas: ' + evmBurnReceipt.gasUsed); + } + dumpOpcodeLogs(evmLiquidityTransfer.transactionHash, alice.provider); + dumpOpcodeLogs(evmBurnReceipt.transactionHash, alice.provider); + }); + + test("Should compare gas against uniswap fallback contract's call", async () => { + const gasCallerFactory = getEVMContractFactory(alice, artifacts.gasCaller); + const gasCallerContract = await gasCallerFactory.deploy(); + await gasCallerContract.deployTransaction.wait(); + await alice.provider.getTransactionReceipt(gasCallerContract.deployTransaction.hash); + + const uniswapContract = await deploygasCallerContract(alice, artifacts.uniswapFallback); + await (await uniswapContract.setUniswapAddress(evmUniswapPair.address)).wait(); + await (await uniswapContract.setAliceAddress(alice.address)).wait(); + + await (await evmToken1.transfer(evmUniswapPair.address, 10000)).wait(); + await (await evmToken1.transfer(uniswapContract.address, 10000)).wait(); + + let result = (await gasCallerContract.callStatic.callAndGetGas(uniswapContract.address)).toString(); + + const expected_gas = '165939'; // Gas cost when run with solidity interpreter + expect(result).toEqual(expected_gas); + }); + }); + + // NOTE: Gas cost comparisons should be done on a *fresh* chain that doesn't have e.g. bytecodes already published + // describe('Bulk opcode tests', () => { + // let opcodeTest: ethers.Contract; + // beforeEach(async () => { + // const opcodeTestFactory = getEVMContractFactory(alice, artifacts.opcodeTest); + // console.log(opcodeTestFactory.bytecode) + // opcodeTest = await opcodeTestFactory.deploy() + // }); + + // test('should successfully execute bulk opcode test', async () => { + // console.log(await deployer.evmCode(opcodeTest.address)) + // // const receipt = await (await opcodeTest.execute()).wait() + // // dumpOpcodeLogs(receipt.transactionHash, alice.provider); + // }); + // }); + + afterAll(async () => { + await testMaster.deinitialize(); + if (logGasCosts) { + printCostData(); + } + }); +}); + +type BenchmarkResult = { + name: string; + used_zkevm_ergs: string; + used_evm_gas: string; + used_circuits: string; +}; + +async function saveBenchmark(name: string, filename: string, result: string) { + try { + const resultWithName = { + name: name, + used_zkevm_ergs: result, + used_evm_gas: '0', + used_circuits: '0' + }; + + let results: BenchmarkResult[] = []; + + // Read existing CSV file + if (fs.existsSync(filename)) { + const existingResults: BenchmarkResult[] = await new Promise((resolve, reject) => { + const results: BenchmarkResult[] = []; + fs.createReadStream(filename) + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', () => resolve(results)) + .on('error', reject); + }); + results = existingResults.map((result) => ({ + name: result.name, + used_zkevm_ergs: result.used_zkevm_ergs, + used_evm_gas: result.used_evm_gas, + used_circuits: result.used_circuits + })); + } + + // Push the new result + results.push(resultWithName); + + // Write results back to CSV + const csvWriter = createObjectCsvWriter({ + path: filename, + header: [ + { id: 'name', title: 'name' }, + { id: 'used_zkevm_ergs', title: 'used_zkevm_ergs' }, + { id: 'used_evm_gas', title: 'used_evm_gas' }, + { id: 'used_circuits', title: 'used_circuits' } + ] + }); + await csvWriter.writeRecords(results); + + console.log('Benchmark saved successfully.'); + } catch (error) { + console.error('Error saving benchmark:', error); + } +} +function zeroPad(num: number, places: number): string { + return String(num).padStart(places, '0'); +} + +async function startBenchmark(): Promise { + try { + const now = new Date(); + const year = now.getUTCFullYear(); + const month = zeroPad(now.getUTCMonth() + 1, 2); // Months are zero-based, so add 1 + const day = zeroPad(now.getUTCDate(), 2); + const hour = zeroPad(now.getUTCHours(), 2); + const minute = zeroPad(now.getUTCMinutes(), 2); + const second = zeroPad(now.getUTCSeconds(), 2); + const formattedTime = `${year}-${month}-${day}-${hour}-${minute}-${second}`; + const directoryPath = 'benchmarks'; + + if (!fs.existsSync(directoryPath)) { + // If it doesn't exist, create it + fs.mkdirSync(directoryPath); + } + + const filename = `benchmarks/benchmark_integration_${formattedTime}.csv`; + return filename; + } catch (error) { + console.error('Error creating benchmark:', error); + return ''; + } +} + +async function deploygasCallerContract(alice: zksync.Wallet, contract: any, ...args: Array) { + const counterFactory = getEVMContractFactory(alice, contract); + const counterContract = await counterFactory.deploy(...args); + await counterContract.waitForDeployment(); + await counterContract.deploymentTransaction()?.wait(); + let hash = counterContract.deploymentTransaction()?.hash; + if (hash == undefined) { + throw new Error('Deployment transaction has failed'); + } + await alice.provider.getTransactionReceipt(hash); + + return counterContract; +} + +async function assertStoredBytecodeHash( + deployer: zksync.Contract, + deployedAddress: string, + expectedStoredHash: string +): Promise { + const ACCOUNT_CODE_STORAGE_ADDRESS = '0x0000000000000000000000000000000000008002'; + const storedCodeHash = await deployer.provider.getStorageAt( + ACCOUNT_CODE_STORAGE_ADDRESS, + ethers.utils.hexZeroPad(deployedAddress, 32) + ); + + expect(storedCodeHash).toEqual(expectedStoredHash); +} + +async function assertCreatedCorrectly( + deployer: zksync.Contract, + deployedAddress: string, + expectedEVMBytecode: string, + logs: Array +): Promise { + const expectedStoredHash = getSha256BlobHash(expectedEVMBytecode); + await assertStoredBytecodeHash(deployer, deployedAddress, expectedStoredHash); +} + +function getPaddedBytecode(bytes: ethers.BytesLike) { + const length = ethers.utils.arrayify(bytes).length; + + const encodedLength = ethers.utils.defaultAbiCoder.encode(['uint256'], [length]); + + let paddedBytecode = encodedLength + ethers.utils.hexlify(bytes).slice(2); + + // The length needs to be 32 mod 64. We use 64 mod 128, since + // we are dealing with a hexlified string + while ((paddedBytecode.length - 2) % 128 != 64) { + paddedBytecode += '0'; + } + + return paddedBytecode; +} + +// Returns the canonical code hash of +function getSha256BlobHash(bytes: ethers.BytesLike): string { + const paddedBytes = getPaddedBytecode(bytes); + + const hash = ethers.utils.arrayify(ethers.utils.sha256(paddedBytes)); + hash[0] = 2; + hash[1] = 0; + + // Length of the bytecode + const lengthInBytes = ethers.utils.arrayify(paddedBytes).length; + hash[2] = Math.floor(lengthInBytes / 256); + hash[3] = lengthInBytes % 256; + + return ethers.utils.hexlify(hash); +} + +async function assertContractNotCreated(deployer: zksync.Contract, deployedAddress: string): Promise { + assertStoredBytecodeHash(deployer, deployedAddress, ethers.constants.HashZero); +} + +function printCostData() { + let costsDataString = ''; + + const averageOverhead = + overheadDataDump.length === 0 + ? undefined + : Math.floor(overheadDataDump.reduce((a: number, c: number) => a + c) / overheadDataDump.length); + const minOverhead = overheadDataDump.length === 0 ? undefined : Math.min(...overheadDataDump); + const maxOverhead = overheadDataDump.length === 0 ? undefined : Math.max(...overheadDataDump); + + costsDataString += 'Overhead\t' + averageOverhead + '\t' + minOverhead + '\t' + maxOverhead + '\n'; + + Object.keys(opcodeDataDump).forEach((opcode) => { + const opcodeString = '0x' + Number(opcode).toString(16).padStart(2, '0'); + const values = opcodeDataDump[opcode.toString()]; + if (values.length === 0) { + costsDataString += opcodeString + '\n'; + return; + } + const average = Math.floor(values.reduce((a: number, c: number) => a + c) / values.length); + const min = Math.min(...values); + const max = Math.max(...values); + + costsDataString += + opcodeString + + '\t' + + average + + '\t' + + (min === average ? '' : min) + + '\t' + + (max === average ? '' : max) + + '\n'; + }); + console.log(costsDataString); +} + +const overheadDataDump: Array = []; +const opcodeDataDump: any = {}; +[ + '0x0', + '0x1', + '0x2', + '0x3', + '0x4', + '0x5', + '0x6', + '0x7', + '0x8', + '0x9', + '0x0A', + '0x0B', + '0x10', + '0x11', + '0x12', + '0x13', + '0x14', + '0x15', + '0x16', + '0x17', + '0x18', + '0x19', + '0x1A', + '0x1B', + '0x1C', + '0x1D', + '0x20', + '0x30', + '0x31', + '0x32', + '0x33', + '0x34', + '0x35', + '0x36', + '0x37', + '0x38', + '0x39', + '0x3A', + '0x3B', + '0x3C', + '0x3D', + '0x3E', + '0x3F', + '0x40', + '0x41', + '0x42', + '0x43', + '0x44', + '0x45', + '0x46', + '0x47', + '0x48', + '0x50', + '0x51', + '0x52', + '0x53', + '0x54', + '0x55', + '0x56', + '0x57', + '0x58', + '0x59', + '0x5A', + '0x5B', + '0x5F', + '0x60', + '0x61', + '0x62', + '0x63', + '0x64', + '0x65', + '0x66', + '0x67', + '0x68', + '0x69', + '0x6A', + '0x6B', + '0x6C', + '0x6D', + '0x6E', + '0x6F', + '0x70', + '0x71', + '0x72', + '0x73', + '0x74', + '0x75', + '0x76', + '0x77', + '0x78', + '0x79', + '0x7A', + '0x7B', + '0x7C', + '0x7D', + '0x7E', + '0x7F', + '0x80', + '0x81', + '0x82', + '0x83', + '0x84', + '0x85', + '0x86', + '0x87', + '0x88', + '0x89', + '0x8A', + '0x8B', + '0x8C', + '0x8D', + '0x8E', + '0x8F', + '0x90', + '0x91', + '0x92', + '0x93', + '0x94', + '0x95', + '0x96', + '0x97', + '0x98', + '0x99', + '0x9A', + '0x9B', + '0x9C', + '0x9D', + '0x9E', + '0x9F', + '0xA0', + '0xA1', + '0xA2', + '0xA3', + '0xA4', + '0xF0', + '0xF1', + '0xF2', + '0xF3', + '0xF4', + '0xF5', + '0xFA', + '0xFD', + '0xFE', + '0xFF' +].forEach((key) => { + opcodeDataDump[Number(key).toString()] = []; +}); + +async function dumpOpcodeLogs(hash: string, provider: zksync.Provider): Promise { + const logs = (await provider.getTransactionReceipt(hash)).logs; + logs.forEach((log) => { + if (log.topics[0] === '0x63307236653da06aaa7e128a306b128c594b4cf3b938ef212975ed10dad17515') { + //Overhead + overheadDataDump.push(Number(ethers.utils.defaultAbiCoder.decode(['uint256'], log.data).toString())); + } else if (log.topics[0] === '0xca5a69edf1b934943a56c00605317596b03e2f61c3f633e8657b150f102a3dfa') { + // Opcode + const parsed = ethers.utils.defaultAbiCoder.decode(['uint256', 'uint256'], log.data); + const opcode = Number(parsed[0].toString()); + const cost = Number(parsed[1].toString()); + + opcodeDataDump[opcode.toString()].push(cost); + } + }); +} diff --git a/yarn.lock b/yarn.lock index 173a06e631f..be6687ce6ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1689,12 +1689,22 @@ resolved "https://registry.yarnpkg.com/@matterlabs/eslint-config-typescript/-/eslint-config-typescript-1.1.2.tgz#a9be4e56aedf298800f247c5049fc412f8b301a7" integrity sha512-AhiWJQr+MSE3RVfgp5XwGoMK7kNSKh6a18+T7hkNJtyycP0306I6IGmuFA5ZVbcakGb+K32fQWzepSkrNCTAGg== -"@matterlabs/hardhat-zksync-chai-matchers@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-chai-matchers/-/hardhat-zksync-chai-matchers-0.1.4.tgz#105cb0ec1367c8fcd3ce7e3773f747c71fff675b" - integrity sha512-eGQWiImg51fmayoQ7smIK/T6QZkSu38PK7xjp1RIrewGzw2ZgqFWGp40jb5oomkf8yOQPk52Hu4TwE3Ntp8CtA== +"@matterlabs/hardhat-zksync-chai-matchers@^0.2.0": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-chai-matchers/-/hardhat-zksync-chai-matchers-0.2.1.tgz#d05136d6cf9a53c30f5e7ee9bae95abb72c1000d" + integrity sha512-LXm5r53DLTQC/KXRXzSRmVp5mEJ4tsoKAKyGck2YLHQ9CBdPoC0paVjbyB2MaEuK/k8o4lZu4uaYKgWQNUXeyQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@matterlabs/hardhat-zksync-deploy" "^0.7.0" + "@matterlabs/hardhat-zksync-solc" "1.0.6" + chai "^4.3.7" + chai-as-promised "^7.1.1" + ethers "~5.7.2" + hardhat "^2.14.0" + ordinal "1.0.3" + zksync-ethers "^5.0.0" -"@matterlabs/hardhat-zksync-deploy@^0.6.1", "@matterlabs/hardhat-zksync-deploy@^0.6.5": +"@matterlabs/hardhat-zksync-deploy@^0.6.1": version "0.6.6" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-deploy/-/hardhat-zksync-deploy-0.6.6.tgz#5c86cf7da859844167d62300528c3e6013ee0286" integrity sha512-QpkxK2wnyQCgaTVLdFATpfiySfr7073yPre9eq5LfKA8VxXWD4WZAuBMq700GL5UyfW9yyHkCdkSzaGigmZ4/Q== @@ -1703,6 +1713,15 @@ chalk "4.1.2" ts-morph "^19.0.0" +"@matterlabs/hardhat-zksync-deploy@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-deploy/-/hardhat-zksync-deploy-0.7.0.tgz#e56b73d8f8fbd0f809a779d0028418ea7d914017" + integrity sha512-PGZcuhKsVzZ2IWPt931pK2gA+HDxnCtye+7CwvoOnM6diHeO9tB1QHFX/ywR9ErOW9wpezhPYkVDx9myFrdoqQ== + dependencies: + "@matterlabs/hardhat-zksync-solc" "^1.0.5" + chalk "4.1.2" + ts-morph "^19.0.0" + "@matterlabs/hardhat-zksync-deploy@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-deploy/-/hardhat-zksync-deploy-1.3.0.tgz#5c2b723318ddf6c4d3929ec225401864ff54557a" @@ -1728,25 +1747,26 @@ chalk "4.1.2" fs-extra "^11.1.1" -"@matterlabs/hardhat-zksync-solc@0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.4.1.tgz#e8e67d947098d7bb8925f968544d34e522af5a9c" - integrity sha512-fdlGf/2yZR5ihVNc2ubea1R/nNFXRONL29Fgz5FwB3azB13rPb76fkQgcFIg9zSufHsEy6zUUT029NkxLNA9Sw== +"@matterlabs/hardhat-zksync-solc@0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.4.2.tgz#64121082e88c5ab22eb4e9594d120e504f6af499" + integrity sha512-6NFWPSZiOAoo7wNuhMg4ztj7mMEH+tLrx09WuCbcURrHPijj/KxYNsJD6Uw5lapKr7G8H7SQISGid1/MTXVmXQ== dependencies: "@nomiclabs/hardhat-docker" "^2.0.0" chalk "4.1.2" dockerode "^3.3.4" fs-extra "^11.1.1" + proper-lockfile "^4.1.2" semver "^7.5.1" -"@matterlabs/hardhat-zksync-solc@0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.4.2.tgz#64121082e88c5ab22eb4e9594d120e504f6af499" - integrity sha512-6NFWPSZiOAoo7wNuhMg4ztj7mMEH+tLrx09WuCbcURrHPijj/KxYNsJD6Uw5lapKr7G8H7SQISGid1/MTXVmXQ== +"@matterlabs/hardhat-zksync-solc@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.0.6.tgz#7ef8438e6bb15244691600e2afa77aaff7dff9f0" + integrity sha512-0icYSufXba/Bbb7v2iXuZJ+IbYsiNpR4Wy6UizHnGuFw3OMHgh+saebQphuaN9yyRL2UPGZbPkQFHWBLZj5/xQ== dependencies: "@nomiclabs/hardhat-docker" "^2.0.0" chalk "4.1.2" - dockerode "^3.3.4" + dockerode "^4.0.0" fs-extra "^11.1.1" proper-lockfile "^4.1.2" semver "^7.5.1" @@ -1794,16 +1814,17 @@ sinon-chai "^3.7.0" undici "^6.18.2" -"@matterlabs/hardhat-zksync-verify@^0.2.0": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-verify/-/hardhat-zksync-verify-0.2.2.tgz#daa34bc4404096ed0f44461ee366c1cb0e5a4f2f" - integrity sha512-WgcItoZGY702oJ708uCP5uLvmwzDLBfhMqq2D0Kh1U/3fCTlPza9zMGUFHxKMQYsITKTeQ5zKOjKoi8MXOeUdQ== +"@matterlabs/hardhat-zksync-verify@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-verify/-/hardhat-zksync-verify-0.4.0.tgz#f812c19950022fc36728f3796f6bdae5633e2fcd" + integrity sha512-GPZmAumFl3ZMPKbECX7Qw8CriwZKWd1DlCRhoG/6YYc6mFy4+MXkF1XsHLMs5r34N+GDOfbVZVMeftIlJC96Kg== dependencies: - "@matterlabs/hardhat-zksync-solc" "0.4.1" + "@matterlabs/hardhat-zksync-solc" "^1.0.5" "@nomicfoundation/hardhat-verify" "^1.0.2" axios "^1.4.0" chalk "4.1.2" dockerode "^3.3.4" + zksync-ethers "^5.0.0" "@matterlabs/hardhat-zksync-verify@^1.4.3": version "1.5.0" @@ -1925,6 +1946,11 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.4.0.tgz#bbb43f0e01f40839b0bd38c2c443cb6910ae955f" integrity sha512-7+rraFk9tCqvfemv9Ita5vTlSBAeO/S5aDKOgGRgYt0JEKZlrX161nDW6UfzMPxWl9GOLEDUzCEaYuNmXseUlg== +"@nomicfoundation/edr-darwin-arm64@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.5.2.tgz#72f7a826c9f0f2c91308edca562de3b9484ac079" + integrity sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A== + "@nomicfoundation/edr-darwin-x64@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.4.tgz#cbcc0a2dcda0a7c0a900a74efc6918cff134dc23" @@ -1935,6 +1961,11 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.4.0.tgz#b1ffcd9142418fd8498de34a7336b3f977907c86" integrity sha512-+Hrc0mP9L6vhICJSfyGo/2taOToy1AIzVZawO3lU8Lf7oDQXfhQ4UkZnkWAs9SVu1eUwHUGGGE0qB8644piYgg== +"@nomicfoundation/edr-darwin-x64@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.5.2.tgz#6d0fedb219d664631c6feddc596ab8c3bbc36fa8" + integrity sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg== + "@nomicfoundation/edr-linux-arm64-gnu@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.4.tgz#12073f97d310176bb24ad7d48c25128ea8eff093" @@ -1945,6 +1976,11 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.4.0.tgz#8173d16d4f6f2b3e82ba7096d2a1ea3619d8bfa7" integrity sha512-4HUDMchNClQrVRfVTqBeSX92hM/3khCgpZkXP52qrnJPqgbdCxosOehlQYZ65wu0b/kaaZSyvACgvCLSQ5oSzQ== +"@nomicfoundation/edr-linux-arm64-gnu@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.5.2.tgz#60e4d52d963141bc2bb4a02639dc590a7fbdda2f" + integrity sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA== + "@nomicfoundation/edr-linux-arm64-musl@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.4.tgz#c9bc685d4d14bf21d9c3e326edd44e009e24492d" @@ -1955,6 +1991,11 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.4.0.tgz#b1ce293a7c3e0d9f70391e1aef1a82b83b997567" integrity sha512-D4J935ZRL8xfnP3zIFlCI9jXInJ0loDUkCTLeCEbOf2uuDumWDghKNQlF1itUS+EHaR1pFVBbuwqq8hVK0dASg== +"@nomicfoundation/edr-linux-arm64-musl@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.5.2.tgz#6676a09eab57c435a16ffc144658c896acca9baa" + integrity sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg== + "@nomicfoundation/edr-linux-x64-gnu@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.4.tgz#37486cbe317b8caf7961e500fc0150c45c895a56" @@ -1965,6 +2006,11 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.4.0.tgz#4c12c4e4bfd3d837f5663ad7cbf7cb6d5634ef83" integrity sha512-6x7HPy+uN5Cb9N77e2XMmT6+QSJ+7mRbHnhkGJ8jm4cZvWuj2Io7npOaeHQ3YHK+TiQpTnlbkjoOIpEwpY3XZA== +"@nomicfoundation/edr-linux-x64-gnu@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.5.2.tgz#f558d9697ce961410e7a7468f9ab8c8a601b9df6" + integrity sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A== + "@nomicfoundation/edr-linux-x64-musl@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.4.tgz#399278807100a1833f6c8a39c17d5beaaf7a9223" @@ -1975,6 +2021,11 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.4.0.tgz#8842004aa1a47c504f10863687da28b65dca7baa" integrity sha512-3HFIJSXgyubOiaN4MWGXx2xhTnhwlJk0PiSYNf9+L/fjBtcRkb2nM910ZJHTvqCb6OT98cUnaKuAYdXIW2amgw== +"@nomicfoundation/edr-linux-x64-musl@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.5.2.tgz#c9c9cbb2997499f75c1d022be724b0551d44569f" + integrity sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA== + "@nomicfoundation/edr-win32-arm64-msvc@0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-arm64-msvc/-/edr-win32-arm64-msvc-0.3.4.tgz#879028e2708538fd54efc349c1a4de107a15abb4" @@ -1995,6 +2046,11 @@ resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.4.0.tgz#29d8bbb2edf9912a95f5453855cf17cdcb269957" integrity sha512-CP4GsllEfXEz+lidcGYxKe5rDJ60TM5/blB5z/04ELVvw6/CK9eLcYeku7HV0jvV7VE6dADYKSdQyUkvd0El+A== +"@nomicfoundation/edr-win32-x64-msvc@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.5.2.tgz#f16db88bf4fe09a996af0a25096e09deecb72bfa" + integrity sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w== + "@nomicfoundation/edr@^0.3.1": version "0.3.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.3.4.tgz#e8eaf41963460139c47b0785f1a6a2a1c1b24ae0" @@ -2023,6 +2079,19 @@ "@nomicfoundation/edr-linux-x64-musl" "0.4.0" "@nomicfoundation/edr-win32-x64-msvc" "0.4.0" +"@nomicfoundation/edr@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.5.2.tgz#e8c7b3d3dd4a312432ab3930dec60f76dc5c4926" + integrity sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.5.2" + "@nomicfoundation/edr-darwin-x64" "0.5.2" + "@nomicfoundation/edr-linux-arm64-gnu" "0.5.2" + "@nomicfoundation/edr-linux-arm64-musl" "0.5.2" + "@nomicfoundation/edr-linux-x64-gnu" "0.5.2" + "@nomicfoundation/edr-linux-x64-musl" "0.5.2" + "@nomicfoundation/edr-win32-x64-msvc" "0.5.2" + "@nomicfoundation/ethereumjs-common@4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz#9901f513af2d4802da87c66d6f255b510bef5acb" @@ -2239,6 +2308,27 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@pnpm/config.env-replace@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz#ab29da53df41e8948a00f2433f085f54de8b3a4c" + integrity sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w== + +"@pnpm/network.ca-file@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz#2ab05e09c1af0cdf2fcf5035bea1484e222f7983" + integrity sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA== + dependencies: + graceful-fs "4.2.10" + +"@pnpm/npm-conf@^2.1.0": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz#bb375a571a0bd63ab0a23bece33033c683e9b6b0" + integrity sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw== + dependencies: + "@pnpm/config.env-replace" "^1.1.0" + "@pnpm/network.ca-file" "^1.0.1" + config-chain "^1.1.11" + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -2441,6 +2531,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== +"@sindresorhus/is@^5.2.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== + "@sinonjs/commons@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" @@ -2507,6 +2602,13 @@ resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" integrity sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA== +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + "@trufflesuite/bigint-buffer@1.1.10": version "1.1.10" resolved "https://registry.yarnpkg.com/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.10.tgz#a1d9ca22d3cad1a138b78baaf15543637a3e1692" @@ -2686,6 +2788,11 @@ dependencies: "@types/node" "*" +"@types/http-cache-semantics@^4.0.2": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" @@ -3242,6 +3349,11 @@ antlr4@^4.11.0: resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.13.1.tgz#1e0a1830a08faeb86217cb2e6c34716004e4253d" integrity sha512-kiXTspaRYvnIArgE97z5YVVf/cDVQABr3abFRR6mE7yesLMkgu4ujuyV/sgxafQ8wgve0DJQUJ38Z8tkgA2izA== +antlr4@^4.13.1-patch-1: + version "4.13.2" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.13.2.tgz#0d084ad0e32620482a9c3a0e2470c02e72e4006d" + integrity sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg== + antlr4ts@^0.5.0-alpha.4: version "0.5.0-alpha.4" resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" @@ -3812,6 +3924,24 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.14" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.14.tgz#eb915b665fda41b79652782df3f553449c406b9d" + integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ== + dependencies: + "@types/http-cache-semantics" "^4.0.2" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.3" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" + call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" @@ -3887,6 +4017,19 @@ chai@^4.3.10, chai@^4.3.4, chai@^4.3.6: pathval "^1.1.1" type-detect "^4.0.8" +chai@^4.3.7: + version "4.5.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8" + integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.1.0" + chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -4192,6 +4335,14 @@ concat-stream@^1.6.0, concat-stream@^1.6.2, concat-stream@~1.6.2: readable-stream "^2.2.2" typedarray "^0.0.6" +config-chain@^1.1.11: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -4316,6 +4467,18 @@ crypto-js@^4.2.0: resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== +csv-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/csv-parser/-/csv-parser-3.0.0.tgz#b88a6256d79e090a97a1b56451f9327b01d710e7" + integrity sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ== + dependencies: + minimist "^1.2.0" + +csv-writer@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/csv-writer/-/csv-writer-1.6.0.tgz#d0cea44b6b4d7d3baa2ecc6f3f7209233514bcf9" + integrity sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -4395,6 +4558,13 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + dedent@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" @@ -4427,6 +4597,11 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +defer-to-connect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + deferred-leveldown@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" @@ -4555,7 +4730,7 @@ dockerode@^3.3.4: docker-modem "^3.0.0" tar-fs "~2.0.1" -dockerode@^4.0.2: +dockerode@^4.0.0, dockerode@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-4.0.2.tgz#dedc8529a1db3ac46d186f5912389899bc309f7d" integrity sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w== @@ -5236,7 +5411,7 @@ ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.3, ethereumjs-util@^7.1.4, ethereum ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@^5.0.2, ethers@^5.7.0, ethers@^5.7.2, ethers@~5.7.0: +ethers@^5.0.2, ethers@^5.7.0, ethers@^5.7.2, ethers@~5.7.0, ethers@~5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -5647,6 +5822,11 @@ 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@^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: version "2.5.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" @@ -5840,7 +6020,7 @@ get-stdin@~9.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575" integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -6038,6 +6218,28 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +got@^12.1.0: + version "12.6.1" + resolved "https://registry.yarnpkg.com/got/-/got-12.6.1.tgz#8869560d1383353204b5a9435f782df9c091f549" + integrity sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + +graceful-fs@4.2.10: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -6155,6 +6357,55 @@ hardhat@=2.22.2: uuid "^8.3.2" ws "^7.4.6" +hardhat@^2.14.0: + version "2.22.8" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.8.tgz#348dcdb48c44648ae7723f6efb511785e2b220c5" + integrity sha512-hPh2feBGRswkXkoXUFW6NbxgiYtEzp/3uvVFjYROy6fA9LH8BobUyxStlyhSKj4+v1Y23ZoUBOVWL84IcLACrA== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@nomicfoundation/edr" "^0.5.2" + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-tx" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" + "@nomicfoundation/solidity-analyzer" "^0.1.0" + "@sentry/node" "^5.18.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + boxen "^5.1.2" + chalk "^2.4.2" + chokidar "^3.4.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" + 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" + keccak "^3.0.2" + lodash "^4.17.11" + mnemonist "^0.38.0" + mocha "^10.0.0" + p-map "^4.0.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + solc "0.8.26" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + tsort "0.0.1" + undici "^5.14.0" + uuid "^8.3.2" + ws "^7.4.6" + hardhat@^2.22.5: version "2.22.5" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.5.tgz#7e1a4311fa9e34a1cfe337784eae06706f6469a5" @@ -6311,6 +6562,11 @@ http-basic@^8.1.1: http-response-object "^3.0.1" parse-cache-control "^1.0.1" +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -6338,6 +6594,14 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -6442,7 +6706,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== -ini@^1.3.5, ini@~1.3.0: +ini@^1.3.4, ini@^1.3.5, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -7389,6 +7653,13 @@ kleur@^3.0.3: dependencies: dotenv "^16.0.3" +latest-version@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-7.0.0.tgz#843201591ea81a4d404932eeb61240fe04e9e5da" + integrity sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg== + dependencies: + package-json "^8.1.0" + level-codec@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" @@ -7658,6 +7929,11 @@ loupe@^2.3.6: dependencies: get-func-name "^2.0.1" +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + lru-cache@^10.2.0: version "10.2.2" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" @@ -7899,6 +8175,16 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -8228,6 +8514,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-url@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.1.tgz#9b7d96af9836577c58f5883e939365fa15623a4a" + integrity sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w== + npm-run-all@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" @@ -8371,7 +8662,7 @@ optionator@^0.9.1, optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" -ordinal@^1.0.3: +ordinal@1.0.3, ordinal@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/ordinal/-/ordinal-1.0.3.tgz#1a3c7726a61728112f50944ad7c35c06ae3a0d4d" integrity sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ== @@ -8381,6 +8672,11 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== +p-cancelable@^3.0.0: + version "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" @@ -8447,6 +8743,16 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json@^8.1.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-8.1.1.tgz#3e9948e43df40d1e8e78a85485f1070bf8f03dc8" + integrity sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA== + dependencies: + got "^12.1.0" + registry-auth-token "^5.0.1" + registry-url "^6.0.0" + semver "^7.3.7" + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -8852,6 +9158,11 @@ proper-lockfile@^4.1.2: retry "^0.12.0" signal-exit "^3.0.2" +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + protobufjs@^7.2.5: version "7.2.6" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215" @@ -8938,6 +9249,11 @@ queue-microtask@^1.2.2, queue-microtask@^1.2.3: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + railroad-diagrams@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" @@ -8968,7 +9284,7 @@ raw-body@^2.4.1: iconv-lite "0.4.24" unpipe "1.0.0" -rc@~1.2.7: +rc@1.2.8, rc@~1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -9070,6 +9386,20 @@ regexpp@^3.1.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +registry-auth-token@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.0.2.tgz#8b026cc507c8552ebbe06724136267e63302f756" + integrity sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ== + dependencies: + "@pnpm/npm-conf" "^2.1.0" + +registry-url@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-6.0.1.tgz#056d9343680f2f64400032b1e199faa692286c58" + integrity sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q== + dependencies: + rc "1.2.8" + req-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-2.0.0.tgz#d4082b4d44598036640fb73ddea01ed53db49ebc" @@ -9120,6 +9450,11 @@ require-from-string@^2.0.0, require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +resolve-alpn@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -9173,6 +9508,13 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.20.0, resolve@^1.22 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== + dependencies: + lowercase-keys "^3.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -9573,6 +9915,19 @@ solc@0.8.17: semver "^5.5.0" tmp "0.0.33" +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 "^8.1.0" + follow-redirects "^1.12.1" + js-sha3 "0.8.0" + memorystream "^0.3.1" + semver "^5.5.0" + tmp "0.0.33" + solhint-plugin-prettier@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" @@ -9580,6 +9935,32 @@ solhint-plugin-prettier@^0.0.5: dependencies: prettier-linter-helpers "^1.0.0" +solhint@4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-4.5.4.tgz#171cf33f46c36b8499efe60c0e425f6883a54e50" + integrity sha512-Cu1XiJXub2q1eCr9kkJ9VPv1sGcmj3V7Zb76B0CoezDOB9bu3DxKIFFH7ggCl9fWpEPD6xBmRLfZrYijkVmujQ== + dependencies: + "@solidity-parser/parser" "^0.18.0" + ajv "^6.12.6" + antlr4 "^4.13.1-patch-1" + ast-parents "^0.0.1" + chalk "^4.1.2" + commander "^10.0.0" + cosmiconfig "^8.0.0" + fast-diff "^1.2.0" + glob "^8.0.3" + ignore "^5.2.4" + js-yaml "^4.1.0" + latest-version "^7.0.0" + lodash "^4.17.21" + pluralize "^8.0.0" + semver "^7.5.2" + strip-ansi "^6.0.1" + table "^6.8.1" + text-table "^0.2.0" + optionalDependencies: + prettier "^2.8.3" + solhint@^3.3.2, solhint@^3.6.2: version "3.6.2" resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.6.2.tgz#2b2acbec8fdc37b2c68206a71ba89c7f519943fe" @@ -9776,7 +10157,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", 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: +"string-width-cjs@npm:string-width@^4.2.0": 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== @@ -9793,6 +10174,15 @@ string-width@^2.1.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +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== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -9859,7 +10249,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9880,6 +10270,13 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -9979,7 +10376,7 @@ synckit@^0.8.6: "system-contracts@link:contracts/system-contracts": version "0.1.0" dependencies: - "@matterlabs/hardhat-zksync-deploy" "^0.6.5" + "@matterlabs/hardhat-zksync-deploy" "^0.7.0" "@matterlabs/hardhat-zksync-solc" "^1.1.4" "@matterlabs/hardhat-zksync-verify" "^1.4.3" commander "^9.4.1" @@ -9990,7 +10387,6 @@ synckit@^0.8.6: fast-glob "^3.3.2" hardhat "=2.22.2" preprocess "^3.2.0" - zksync-ethers "https://github.com/zksync-sdk/zksync-ethers#ethers-v5-feat/bridgehub" table-layout@^1.0.2: version "1.0.2" @@ -10347,6 +10743,11 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-detect@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" + integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -10725,7 +11126,16 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10879,18 +11289,19 @@ zksync-ethers@5.8.0-beta.5: dependencies: ethers "~5.7.0" +zksync-ethers@^5.0.0, zksync-ethers@^5.9.0: + version "5.9.2" + resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-5.9.2.tgz#1c5f34cb25ac0b040fd1a6118f2ba1c2c3bda090" + integrity sha512-Y2Mx6ovvxO6UdC2dePLguVzvNToOY8iLWeq5ne+jgGSJxAi/f4He/NF6FNsf6x1aWX0o8dy4Df8RcOQXAkj5qw== + dependencies: + ethers "~5.7.0" + zksync-ethers@^6.9.0: version "6.9.0" resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-6.9.0.tgz#efaff1d59e2cff837eeda84c4ba59fdca4972a91" integrity sha512-2CppwvLHtz689L7E9EhevbFtsqVukKC/lVicwdeUS2yqV46ET4iBR11rYdEfGW2oEo1h6yJuuwIBDFm2SybkIA== -"zksync-ethers@https://github.com/zksync-sdk/zksync-ethers#ethers-v5-feat/bridgehub": - version "5.1.0" - resolved "https://github.com/zksync-sdk/zksync-ethers#28ccbe7d67b170c202b17475e06a82002e6e3acc" - dependencies: - ethers "~5.7.0" - -zksync-web3@^0.15.4: +zksync-web3@^0.15.5: version "0.15.5" resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.15.5.tgz#aabe379464963ab573e15948660a709f409b5316" integrity sha512-97gB7OKJL4spegl8fGO54g6cvTd/75G6yFWZWEa2J09zhjTrfqabbwE/GwiUJkFQ5BbzoH4JaTlVz1hoYZI+DQ== From e0940193e5b77d0ea418646d317db5a368a24b2c Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 19 Aug 2024 16:37:04 -0300 Subject: [PATCH 02/10] Make evm tests compile --- .../contracts/create/TestEVMCreate.sol | 30 + .../ts-integration/contracts/token/ERC20.sol | 76 ++ .../contracts/uniswap-v2/UniswapV2Factory.sol | 683 ++++++++++++++++++ .../tests/evm-contracts.test.ts | 242 ++++--- 4 files changed, 915 insertions(+), 116 deletions(-) create mode 100644 core/tests/ts-integration/contracts/create/TestEVMCreate.sol create mode 100644 core/tests/ts-integration/contracts/token/ERC20.sol create mode 100644 core/tests/ts-integration/contracts/uniswap-v2/UniswapV2Factory.sol diff --git a/core/tests/ts-integration/contracts/create/TestEVMCreate.sol b/core/tests/ts-integration/contracts/create/TestEVMCreate.sol new file mode 100644 index 00000000000..176790d1402 --- /dev/null +++ b/core/tests/ts-integration/contracts/create/TestEVMCreate.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IContractDeployer { + function evmCodeHash(address key) external returns (bytes32); + + function createEVM( + bytes calldata _initCode + ) external payable returns (address newAddress); + + function create2EVM( + bytes32 _salt, + bytes calldata _initCode + ) external payable returns (address); +} + +/// @notice An example of a system contract that be used for local testing. +/// @dev It is not used anywhere except for testing +contract TestEVMCreate { + IContractDeployer deployer = IContractDeployer(address(0x8006)); + + function create(bytes calldata _code) external { + deployer.createEVM(_code); + } + + function create2(bytes32 _salt, bytes calldata _code) external payable { + deployer.create2EVM{value:msg.value}(_salt, _code); + } +} diff --git a/core/tests/ts-integration/contracts/token/ERC20.sol b/core/tests/ts-integration/contracts/token/ERC20.sol new file mode 100644 index 00000000000..ca3269c4ebd --- /dev/null +++ b/core/tests/ts-integration/contracts/token/ERC20.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity ^0.8.0; + +contract ERC20{ + string public symbol; + string public name; + uint8 public decimals; + uint public totalSupply; + + mapping(address => uint) balances; + mapping(address => mapping(address => uint)) allowed; + + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); + + constructor() { + symbol = "TEST"; + name = "Test Coin"; + decimals = 18; + totalSupply = 1000000; + balances[msg.sender] = totalSupply; + emit Transfer(address(0), msg.sender, totalSupply); + } + + + function balanceOf(address tokenOwner) public view returns (uint balance) { + return balances[tokenOwner]; + } + + function transfer(address to, uint tokens) public returns (bool success) { + balances[msg.sender] = safeSub(balances[msg.sender], tokens); + balances[to] = safeAdd(balances[to], tokens); + emit Transfer(msg.sender, to, tokens); + return true; + } + + function approve(address spender, uint tokens) public returns (bool success) { + allowed[msg.sender][spender] = tokens; + emit Approval(msg.sender, spender, tokens); + return true; + } + + function transferFrom(address from, address to, uint tokens) public returns (bool success) { + balances[from] = safeSub(balances[from], tokens); + allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens); + balances[to] = safeAdd(balances[to], tokens); + emit Transfer(from, to, tokens); + return true; + } + + function allowance(address tokenOwner, address spender) public view returns (uint remaining) { + return allowed[tokenOwner][spender]; + } + + function safeAdd(uint a, uint b) internal pure returns (uint c) { + c = a + b; + require(c >= a); + } + + function safeSub(uint a, uint b) internal pure returns (uint c) { + require(b <= a); + c = a - b; + } + + function safeMul(uint a, uint b) internal pure returns (uint c) { + c = a * b; + require(a == 0 || c / a == b); + } + + function safeDiv(uint a, uint b) internal pure returns (uint c) { + require(b > 0); + c = a / b; + } + +} \ No newline at end of file diff --git a/core/tests/ts-integration/contracts/uniswap-v2/UniswapV2Factory.sol b/core/tests/ts-integration/contracts/uniswap-v2/UniswapV2Factory.sol new file mode 100644 index 00000000000..90b9429dc19 --- /dev/null +++ b/core/tests/ts-integration/contracts/uniswap-v2/UniswapV2Factory.sol @@ -0,0 +1,683 @@ +// NOTE: Flattened to make easier to deploy to both native/evm + +// File contracts/uniswap-v2/interfaces/IUniswapV2Factory.sol + +pragma solidity ^0.8.0; + +interface IUniswapV2Factory { + function feeTo() external returns (address); + + function feeToSetter() external returns (address); + + function getPair( + address tokenA, + address tokenB + ) external returns (address pair); + + function allPairs(uint) external returns (address pair); + + function allPairsLength() external returns (uint); + + function createPair( + address tokenA, + address tokenB + ) external returns (address pair); + + function setFeeTo(address) external; + + function setFeeToSetter(address) external; +} + +// File contracts/uniswap-v2/libraries/SafeMath.sol + +pragma solidity ^0.8.0; + +// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math) + +library SafeMath { + function add(uint x, uint y) internal pure returns (uint z) { + require((z = x + y) >= x, "ds-math-add-overflow"); + } + + function sub(uint x, uint y) internal pure returns (uint z) { + require((z = x - y) <= x, "ds-math-sub-underflow"); + } + + function mul(uint x, uint y) internal pure returns (uint z) { + require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow"); + } +} + +// File contracts/uniswap-v2/interfaces/IUniswapV2ERC20.sol + +pragma solidity ^0.8.0; + +interface IUniswapV2ERC20 { + event Approval(address indexed owner, address indexed spender, uint value); + event Transfer(address indexed from, address indexed to, uint value); + + function name() external pure returns (string memory); + + function symbol() external pure returns (string memory); + + function decimals() external pure returns (uint8); + + function totalSupply() external returns (uint); + + function balanceOf(address owner) external returns (uint); + + function allowance(address owner, address spender) external returns (uint); + + function approve(address spender, uint value) external returns (bool); + + function transfer(address to, uint value) external returns (bool); + + function transferFrom( + address from, + address to, + uint value + ) external returns (bool); + + function DOMAIN_SEPARATOR() external returns (bytes32); + + function PERMIT_TYPEHASH() external pure returns (bytes32); + + function nonces(address owner) external returns (uint); + + function permit( + address owner, + address spender, + uint value, + uint deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} + +// File contracts/uniswap-v2/UniswapV2ERC20.sol + +pragma solidity ^0.8.0; + +contract UniswapV2ERC20 is IUniswapV2ERC20 { + using SafeMath for uint; + + string public override constant name = "Uniswap V2"; + string public override constant symbol = "UNI-V2"; + uint8 public override constant decimals = 18; + uint public override totalSupply; + mapping(address => uint) public override balanceOf; + mapping(address => mapping(address => uint)) public override allowance; + + bytes32 public override DOMAIN_SEPARATOR; + // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + bytes32 public override constant PERMIT_TYPEHASH = + 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + mapping(address => uint) public override nonces; + + constructor() { + uint chainId; + assembly { + chainId := chainid() + } + DOMAIN_SEPARATOR = keccak256( + abi.encode( + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ), + keccak256(bytes(name)), + keccak256(bytes("1")), + chainId, + address(this) + ) + ); + } + + function _mint(address to, uint value) internal { + totalSupply = totalSupply.add(value); + balanceOf[to] = balanceOf[to].add(value); + emit Transfer(address(0), to, value); + } + + function _burn(address from, uint value) internal { + balanceOf[from] = balanceOf[from].sub(value); + totalSupply = totalSupply.sub(value); + emit Transfer(from, address(0), value); + } + + function _approve(address owner, address spender, uint value) private { + allowance[owner][spender] = value; + emit Approval(owner, spender, value); + } + + function _transfer(address from, address to, uint value) private { + balanceOf[from] = balanceOf[from].sub(value); + balanceOf[to] = balanceOf[to].add(value); + emit Transfer(from, to, value); + } + + function approve(address spender, uint value) external override returns (bool) { + _approve(msg.sender, spender, value); + return true; + } + + function transfer(address to, uint value) external override returns (bool) { + _transfer(msg.sender, to, value); + return true; + } + + function transferFrom( + address from, + address to, + uint value + ) external override returns (bool) { + if (allowance[from][msg.sender] != uint(int(-1))) { + allowance[from][msg.sender] = allowance[from][msg.sender].sub( + value + ); + } + _transfer(from, to, value); + return true; + } + + function permit( + address owner, + address spender, + uint value, + uint deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external override { + require(deadline >= block.timestamp, "UniswapV2: EXPIRED"); + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + keccak256( + abi.encode( + PERMIT_TYPEHASH, + owner, + spender, + value, + nonces[owner]++, + deadline + ) + ) + ) + ); + address recoveredAddress = ecrecover(digest, v, r, s); + require( + recoveredAddress != address(0) && recoveredAddress == owner, + "UniswapV2: INVALID_SIGNATURE" + ); + _approve(owner, spender, value); + } +} + +// File contracts/uniswap-v2/libraries/Math.sol + +pragma solidity ^0.8.0; + +// a library for performing various math operations + +library Math { + function min(uint x, uint y) internal pure returns (uint z) { + z = x < y ? x : y; + } + + // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) + function sqrt(uint y) internal pure returns (uint z) { + if (y > 3) { + z = y; + uint x = y / 2 + 1; + while (x < z) { + z = x; + x = (y / x + x) / 2; + } + } else if (y != 0) { + z = 1; + } + } +} + +// File contracts/uniswap-v2/interfaces/IERC20.sol + +pragma solidity ^0.8.0; + +interface IERC20 { + event Approval(address indexed owner, address indexed spender, uint value); + event Transfer(address indexed from, address indexed to, uint value); + + function name() external returns (string memory); + + function symbol() external returns (string memory); + + function decimals() external returns (uint8); + + function totalSupply() external returns (uint); + + function balanceOf(address owner) external returns (uint); + + function allowance(address owner, address spender) external returns (uint); + + function approve(address spender, uint value) external returns (bool); + + function transfer(address to, uint value) external returns (bool); + + function transferFrom( + address from, + address to, + uint value + ) external returns (bool); +} + +// File contracts/uniswap-v2/libraries/UQ112x112.sol + +pragma solidity ^0.8.0; + +// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format)) + +// range: [0, 2**112 - 1] +// resolution: 1 / 2**112 + +library UQ112x112 { + uint224 constant Q112 = 2 ** 112; + + // encode a uint112 as a UQ112x112 + function encode(uint112 y) internal pure returns (uint224 z) { + z = uint224(y) * Q112; // never overflows + } + + // divide a UQ112x112 by a uint112, returning a UQ112x112 + function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) { + z = x / uint224(y); + } +} + +// File contracts/uniswap-v2/interfaces/IUniswapV2Pair.sol + +pragma solidity ^0.8.0; + +interface IUniswapV2Pair { + event Mint(address indexed sender, uint amount0, uint amount1); + event Burn( + address indexed sender, + uint amount0, + uint amount1, + address indexed to + ); + event Swap( + address indexed sender, + uint amount0In, + uint amount1In, + uint amount0Out, + uint amount1Out, + address indexed to + ); + event Sync(uint112 reserve0, uint112 reserve1); + + function MINIMUM_LIQUIDITY() external pure returns (uint); + + function factory() external returns (address); + + function token0() external returns (address); + + function token1() external returns (address); + + function getReserves() + external + returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); + + function price0CumulativeLast() external returns (uint); + + function price1CumulativeLast() external returns (uint); + + function kLast() external returns (uint); + + function mint(address to) external returns (uint liquidity); + + function burn(address to) external returns (uint amount0, uint amount1); + + function swap( + uint amount0Out, + uint amount1Out, + address to, + bytes calldata data + ) external; + + function skim(address to) external; + + function sync() external; + + function initialize(address, address) external; +} + +// File contracts/uniswap-v2/interfaces/IUniswapV2Callee.sol + +pragma solidity ^0.8.0; + +interface IUniswapV2Callee { + function uniswapV2Call( + address sender, + uint amount0, + uint amount1, + bytes calldata data + ) external; +} + +// File contracts/uniswap-v2/UniswapV2Pair.sol + +pragma solidity ^0.8.0; + +contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 { + using SafeMath for uint; + using UQ112x112 for uint224; + + uint public override constant MINIMUM_LIQUIDITY = 10 ** 3; + bytes4 private constant SELECTOR = + bytes4(keccak256(bytes("transfer(address,uint256)"))); + + address public override factory; + address public override token0; + address public override token1; + + uint112 private reserve0; // uses single storage slot, accessible via getReserves + uint112 private reserve1; // uses single storage slot, accessible via getReserves + uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves + + uint public override price0CumulativeLast; + uint public override price1CumulativeLast; + uint public override kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event + + uint private unlocked = 1; + modifier lock() { + require(unlocked == 1, "UniswapV2: LOCKED"); + unlocked = 0; + _; + unlocked = 1; + } + + function getReserves() + public + override + returns ( + uint112 _reserve0, + uint112 _reserve1, + uint32 _blockTimestampLast + ) + { + _reserve0 = reserve0; + _reserve1 = reserve1; + _blockTimestampLast = blockTimestampLast; + } + + function _safeTransfer(address token, address to, uint value) private { + IERC20(token).transfer(to, value); + } + + constructor() public { + factory = msg.sender; + } + + // called once by the factory at time of deployment + function initialize(address _token0, address _token1) external override { + require(msg.sender == factory, "UniswapV2: FORBIDDEN"); // sufficient check + token0 = _token0; + token1 = _token1; + } + + // update reserves and, on the first call per block, price accumulators + function _update( + uint balance0, + uint balance1, + uint112 _reserve0, + uint112 _reserve1 + ) private { + require( + balance0 <= uint112(int112(-1)) && balance1 <= uint112(int112(-1)), + "UniswapV2: OVERFLOW" + ); + uint32 blockTimestamp = uint32(block.timestamp % 2 ** 32); + uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired + if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) { + // * never overflows, and + overflow is desired + price0CumulativeLast += + uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * + timeElapsed; + price1CumulativeLast += + uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * + timeElapsed; + } + reserve0 = uint112(balance0); + reserve1 = uint112(balance1); + blockTimestampLast = blockTimestamp; + emit Sync(reserve0, reserve1); + } + + // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k) + function _mintFee( + uint112 _reserve0, + uint112 _reserve1 + ) private returns (bool feeOn) { + address feeTo = IUniswapV2Factory(factory).feeTo(); + feeOn = feeTo != address(0); + uint _kLast = kLast; // gas savings + if (feeOn) { + if (_kLast != 0) { + uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1)); + uint rootKLast = Math.sqrt(_kLast); + if (rootK > rootKLast) { + uint numerator = totalSupply.mul(rootK.sub(rootKLast)); + uint denominator = rootK.mul(5).add(rootKLast); + uint liquidity = numerator / denominator; + if (liquidity > 0) _mint(feeTo, liquidity); + } + } + } else if (_kLast != 0) { + kLast = 0; + } + } + + // this low-level function should be called from a contract which performs important safety checks + function mint(address to) external override lock returns (uint liquidity) { + (uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings + uint balance0 = IERC20(token0).balanceOf(address(this)); + uint balance1 = IERC20(token1).balanceOf(address(this)); + uint amount0 = balance0.sub(_reserve0); + uint amount1 = balance1.sub(_reserve1); + + bool feeOn = _mintFee(_reserve0, _reserve1); + uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee + if (_totalSupply == 0) { + liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY); + _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens + } else { + liquidity = Math.min( + amount0.mul(_totalSupply) / _reserve0, + amount1.mul(_totalSupply) / _reserve1 + ); + } + require(liquidity > 0, "UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED"); + _mint(to, liquidity); + + _update(balance0, balance1, _reserve0, _reserve1); + if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date + emit Mint(msg.sender, amount0, amount1); + } + + // this low-level function should be called from a contract which performs important safety checks + function burn( + address to + ) external override lock returns (uint amount0, uint amount1) { + (uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings + address _token0 = token0; // gas savings + address _token1 = token1; // gas savings + uint balance0 = IERC20(_token0).balanceOf(address(this)); + uint balance1 = IERC20(_token1).balanceOf(address(this)); + uint liquidity = balanceOf[address(this)]; + + bool feeOn = _mintFee(_reserve0, _reserve1); + uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee + amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution + amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution + require( + amount0 > 0 && amount1 > 0, + "UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED" + ); + _burn(address(this), liquidity); + _safeTransfer(_token0, to, amount0); + _safeTransfer(_token1, to, amount1); + balance0 = IERC20(_token0).balanceOf(address(this)); + balance1 = IERC20(_token1).balanceOf(address(this)); + + _update(balance0, balance1, _reserve0, _reserve1); + if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date + emit Burn(msg.sender, amount0, amount1, to); + } + + // this low-level function should be called from a contract which performs important safety checks + function swap( + uint amount0Out, + uint amount1Out, + address to, + bytes calldata data + ) external override lock { + // require( + // amount0Out > 0 || amount1Out > 0, + // "UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT" + // ); + // (uint112 _reserve0, uint112 _reserve1, ) = getReserves(); // gas savings + // require( + // amount0Out < _reserve0 && amount1Out < _reserve1, + // "UniswapV2: INSUFFICIENT_LIQUIDITY" + // ); + + // uint balance0; + // uint balance1; + // { + // // scope for _token{0,1}, avoids stack too deep errors + // address _token0 = token0; + // address _token1 = token1; + // require(to != _token0 && to != _token1, "UniswapV2: INVALID_TO"); + // if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens + // if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens + // if (data.length > 0) + // IUniswapV2Callee(to).uniswapV2Call( + // msg.sender, + // amount0Out, + // amount1Out, + // data + // ); + // balance0 = IERC20(_token0).balanceOf(address(this)); + // balance1 = IERC20(_token1).balanceOf(address(this)); + // } + // uint amount0In = balance0 > _reserve0 - amount0Out + // ? balance0 - (_reserve0 - amount0Out) + // : 0; + // uint amount1In = balance1 > _reserve1 - amount1Out + // ? balance1 - (_reserve1 - amount1Out) + // : 0; + // require( + // amount0In > 0 || amount1In > 0, + // "UniswapV2: INSUFFICIENT_INPUT_AMOUNT" + // ); + // { + // // scope for reserve{0,1}Adjusted, avoids stack too deep errors + // uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3)); + // uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3)); + // require( + // balance0Adjusted.mul(balance1Adjusted) >= + // uint(_reserve0).mul(_reserve1).mul(1000 ** 2), + // "UniswapV2: K" + // ); + // } + + // _update(balance0, balance1, _reserve0, _reserve1); + // emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); + } + + // force balances to match reserves + function skim(address to) external override lock { + address _token0 = token0; // gas savings + address _token1 = token1; // gas savings + _safeTransfer( + _token0, + to, + IERC20(_token0).balanceOf(address(this)).sub(reserve0) + ); + _safeTransfer( + _token1, + to, + IERC20(_token1).balanceOf(address(this)).sub(reserve1) + ); + } + + // force reserves to match balances + function sync() external override lock { + _update( + IERC20(token0).balanceOf(address(this)), + IERC20(token1).balanceOf(address(this)), + reserve0, + reserve1 + ); + } +} + +// File contracts/uniswap-v2/UniswapV2Factory.sol + +pragma solidity ^0.8.0; + +contract UniswapV2Factory is IUniswapV2Factory { + address public override feeTo; + address public override feeToSetter; + + mapping(address => mapping(address => address)) public override getPair; + address[] public override allPairs; + + event PairCreated( + address indexed token0, + address indexed token1, + address pair, + uint + ); + + constructor(address _feeToSetter) public { + feeToSetter = _feeToSetter; + } + + function allPairsLength() external override returns (uint) { + return allPairs.length; + } + + function createPair( + address tokenA, + address tokenB + ) external override returns (address pair) { + require(tokenA != tokenB, "UniswapV2: IDENTICAL_ADDRESSES"); + (address token0, address token1) = tokenA < tokenB + ? (tokenA, tokenB) + : (tokenB, tokenA); + require(token0 != address(0), "UniswapV2: ZERO_ADDRESS"); + require( + getPair[token0][token1] == address(0), + "UniswapV2: PAIR_EXISTS" + ); // single check is sufficient + pair = address(new UniswapV2Pair()); + IUniswapV2Pair(pair).initialize(token0, token1); + getPair[token0][token1] = pair; + getPair[token1][token0] = pair; // populate mapping in the reverse direction + allPairs.push(pair); + emit PairCreated(token0, token1, pair, allPairs.length); + } + + function setFeeTo(address _feeTo) external override { + require(msg.sender == feeToSetter, "UniswapV2: FORBIDDEN"); + feeTo = _feeTo; + } + + function setFeeToSetter(address _feeToSetter) external override { + require(msg.sender == feeToSetter, "UniswapV2: FORBIDDEN"); + feeToSetter = _feeToSetter; + } +} \ No newline at end of file diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts index 57e2a61a2f0..316e65ce4bd 100644 --- a/core/tests/ts-integration/tests/evm-contracts.test.ts +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -68,7 +68,7 @@ describe('EVM equivalence contract', () => { const counterContract = await deploygasCallerContract(alice, artifacts.counterFallback); - let result = (await gasCallerContract.callAndGetGas.staticCall(counterContract.address)).toString(); + let result = (await gasCallerContract.getFunction("callAndGetGas").staticCall(counterContract.getAddress())).toString(); const expected_gas = '3617'; // Gas cost when run with solidity interpreter expect(result).toEqual(expected_gas); @@ -79,7 +79,7 @@ describe('EVM equivalence contract', () => { const creatorContract = await deploygasCallerContract(alice, artifacts.creatorFallback); - let result = (await gasCallerContract.callStatic.callAndGetGas(creatorContract.address)).toString(); + let result = (await gasCallerContract.getFunction("callAndGetGas").staticCall(creatorContract.getAddress())).toString(); const expected_gas = '70601'; // Gas cost when run with solidity interpreter expect(result).toEqual(expected_gas); @@ -90,7 +90,7 @@ describe('EVM equivalence contract', () => { const counterContract = await deploygasCallerContract(alice, artifacts.opcodeTestFallback); - let result = (await gasCallerContract.callStatic.callAndGetGas(counterContract.address)).toString(); + let result = (await gasCallerContract.getFunction("callAndGetGas").staticCall(counterContract.getAddress())).toString(); const expected_gas = '34763'; // Gas cost when run with solidity interpreter expect(result).toEqual(expected_gas); @@ -103,33 +103,32 @@ describe('EVM equivalence contract', () => { const args = [1]; const factory = getEVMContractFactory(alice, artifacts.counter); const contract = await factory.deploy(args); - await contract.deployTransaction.wait(); - const receipt = await alice.provider.getTransactionReceipt(contract.deployTransaction.hash); + await contract.deploymentTransaction()?.wait(); + const receipt = await alice.provider.getTransactionReceipt(contract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })()); await assertCreatedCorrectly( deployer, - contract.address, + await contract.getAddress(), '0x' + artifacts.counter.evm.deployedBytecode.object, - receipt.logs ); - expect((await contract.callStatic.get()).toString()).toEqual('1'); - await (await contract.increment(1)).wait(); - expect((await contract.callStatic.get()).toString()).toEqual('2'); + expect((await contract.getFunction("get").staticCall()).toString()).toEqual('1'); + await (await contract.getFunction("increment")(1)).wait(); + expect((await contract.getFunction("get").staticCall()).toString()).toEqual('2'); }); test('Should create2 evm contract from ZKEVM contract', async () => { - const salt = ethers.utils.randomBytes(32); + const salt = ethers.randomBytes(32); - const expectedAddress = ethers.utils.getCreate2Address( - evmCreateTester.address, + const expectedAddress = ethers.getCreate2Address( + await evmCreateTester.getAddress(), salt, - ethers.utils.keccak256(initBytecode) + ethers.keccak256(initBytecode) ); const receipt = await (await evmCreateTester.create2(salt, initBytecode)).wait(); - await assertCreatedCorrectly(deployer, expectedAddress, runtimeBytecode, receipt.logs); + await assertCreatedCorrectly(deployer, expectedAddress, runtimeBytecode); try { await (await evmCreateTester.create2(salt, initBytecode, { gasLimit })).wait(); @@ -147,7 +146,7 @@ describe('EVM equivalence contract', () => { let failReason; try { - await contract.deployTransaction.wait(); + await contract.deploymentTransaction()?.wait(); } catch (e: any) { failReason = e.reason; } @@ -160,10 +159,11 @@ describe('EVM equivalence contract', () => { const factory = getEVMContractFactory(alice, artifacts.counter); const transaction = await factory.getDeployTransaction(args); - transaction.to = '0x0000000000000000000000000000000000000000'; + let dep_transaction = ethers.Transaction.from(transaction); + dep_transaction.to = '0x0000000000000000000000000000000000000000'; - const result = await (await alice.sendTransaction(transaction)).wait(); - const expectedAddressCreate = ethers.utils.getContractAddress({ + const result = await (await alice.sendTransaction(dep_transaction)).wait(); + const expectedAddressCreate = ethers.getCreateAddress({ from: alice.address, nonce: await alice.getNonce() }); @@ -206,21 +206,21 @@ describe('EVM equivalence contract', () => { const counterFactory = getEVMContractFactory(alice, artifacts.counter); const counterContract = await counterFactory.deploy(args); - await counterContract.deployTransaction.wait(); - await alice.provider.getTransactionReceipt(counterContract.deployTransaction.hash); + await counterContract.deploymentTransaction()?.wait(); + await alice.provider.getTransactionReceipt(counterContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })()); const proxyCallerFactory = getEVMContractFactory(alice, artifacts.proxyCaller); const proxyCallerContract = await proxyCallerFactory.deploy(); - await proxyCallerContract.deployTransaction.wait(); - await alice.provider.getTransactionReceipt(proxyCallerContract.deployTransaction.hash); + await proxyCallerContract.deploymentTransaction()?.wait(); + await alice.provider.getTransactionReceipt(proxyCallerContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })()); - expect((await proxyCallerContract.proxyGet(counterContract.address)).toString()).toEqual('1'); + expect((await proxyCallerContract.getFunction("proxyGet")(counterContract.getAddress())).toString()).toEqual('1'); - await (await proxyCallerContract.executeIncrememt(counterContract.address, 1)).wait(); + await (await proxyCallerContract.getFunction("executeIncrememt")(counterContract.getAddress(), 1)).wait(); - expect((await proxyCallerContract.proxyGet(counterContract.address)).toString()).toEqual('2'); + expect((await proxyCallerContract.getFunction("proxyGet")(counterContract.getAddress())).toString()).toEqual('2'); - expect((await proxyCallerContract.callStatic.proxyGetBytes(counterContract.address)).toString()).toEqual( + expect((await proxyCallerContract.getFunction("proxyGetBytes").staticCall(counterContract.getAddress())).toString()).toEqual( '0x54657374696e67' ); }); @@ -228,23 +228,23 @@ describe('EVM equivalence contract', () => { test('Create opcode works correctly', async () => { const creatorFactory = getEVMContractFactory(alice, artifacts.creator); const creatorContract = await creatorFactory.deploy(); - await creatorContract.deployTransaction.wait(); + await creatorContract.deploymentTransaction()?.wait(); - dumpOpcodeLogs(creatorContract.deployTransaction.hash, alice.provider); + dumpOpcodeLogs(creatorContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(), alice.provider); const nonce = 1; - const runtimeBytecode = await creatorContract.getCreationRuntimeCode(); + const runtimeBytecode = await creatorContract.getFunction("getCreationRuntimeCode")(); - const expectedAddress = ethers.utils.getContractAddress({ - from: creatorContract.address, + const expectedAddress = ethers.getCreateAddress({ + from: await creatorContract.getAddress(), nonce }); - const result = await (await creatorContract.create()).wait(); + const result = await (await creatorContract.getFunction("create")()).wait(); dumpOpcodeLogs(result.transactionHash, alice.provider); - await assertCreatedCorrectly(deployer, expectedAddress, runtimeBytecode, result.logs); + await assertCreatedCorrectly(deployer, expectedAddress, runtimeBytecode); }); test('Should revert correctly', async () => { @@ -252,14 +252,14 @@ describe('EVM equivalence contract', () => { const counterFactory = getEVMContractFactory(alice, artifacts.counter); const counterContract = await counterFactory.deploy(args); - await counterContract.deployTransaction.wait(); + await counterContract.deploymentTransaction()?.wait(); - dumpOpcodeLogs(counterContract.deployTransaction.hash, alice.provider); + dumpOpcodeLogs(counterContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(), alice.provider); let errorString; try { - await counterContract.callStatic.incrementWithRevert(1, true); + await counterContract.getFunction("incrementWithRevert").staticCall(1, true); } catch (e: any) { errorString = e.reason; } @@ -269,7 +269,7 @@ describe('EVM equivalence contract', () => { // NOTE: Gas cost comparisons should be done on a *fresh* chain that doesn't have e.g. bytecodes already published describe('ERC20', () => { - let evmToken: ethers.Contract; + let evmToken: ethers.BaseContract; let nativeToken: zksync.Contract; let userAccount: zksync.Wallet; let deployLogged: boolean = false; @@ -277,46 +277,50 @@ describe('EVM equivalence contract', () => { beforeEach(async () => { const erc20Factory = getEVMContractFactory(alice, artifacts.erc20); evmToken = await erc20Factory.deploy(); - await evmToken.deployTransaction.wait(); + await evmToken.deploymentTransaction()?.wait(); nativeToken = await deployContract(alice, contracts.erc20, []); - dumpOpcodeLogs(evmToken.deployTransaction.hash, alice.provider); + dumpOpcodeLogs(evmToken.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(), alice.provider); userAccount = testMaster.newEmptyAccount(); // Only log the first deployment if (logGasCosts && !deployLogged) { + let native_hash = nativeToken.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(); + let native_transanction_receipt = (await alice.provider.getTransactionReceipt(native_hash)) ?? (() => { throw new Error('Deployment transaction has failed'); })(); + let evm_hash = evmToken.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(); + let evm_transanction_receipt = (await alice.provider.getTransactionReceipt(evm_hash)) ?? (() => { throw new Error('Deployment transaction has failed'); })(); console.log( 'ERC20 native deploy gas: ' + - (await alice.provider.getTransactionReceipt(nativeToken.deployTransaction.hash)).gasUsed + native_transanction_receipt.gasUsed ); console.log( 'ERC20 evm deploy gas: ' + - (await alice.provider.getTransactionReceipt(evmToken.deployTransaction.hash)).gasUsed + evm_transanction_receipt.gasUsed ); deployLogged = true; } await ( await alice.sendTransaction({ to: userAccount.address, - value: ethers.BigNumber.from('0xffffffffffffff') + value: BigInt('0xffffffffffffff') }) ).wait(); }); test('view functions should work', async () => { - const evmBalanceOfCost = await evmToken.estimateGas.balanceOf(alice.address); - const nativeBalanceOfCost = await nativeToken.estimateGas.balanceOf(alice.address); + const evmBalanceOfCost = await evmToken.getFunction("balanceOf").estimateGas(alice.address); + const nativeBalanceOfCost = await nativeToken.getFunction("balanceOf").estimateGas(alice.address); if (logGasCosts) { console.log('ERC20 native balanceOf gas: ' + nativeBalanceOfCost.toString()); console.log('ERC20 evm balanceOf gas: ' + evmBalanceOfCost.toString()); } - expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('1000000'); - expect((await evmToken.totalSupply()).toString()).toEqual('1000000'); - expect((await evmToken.balanceOf(userAccount.address)).toString()).toEqual('0'); + expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('1000000'); + expect((await evmToken.getFunction("totalSupply")()).toString()).toEqual('1000000'); + expect((await evmToken.getFunction("balanceOf")(userAccount.address)).toString()).toEqual('0'); }); test('transfer should work', async () => { - expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('1000000'); - const evmTransferTx = await (await evmToken.transfer(userAccount.address, 100000)).wait(); + expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('1000000'); + const evmTransferTx = await (await evmToken.getFunction("transfer")(userAccount.address, 100000)).wait(); const nativeTransferTx = await (await nativeToken.transfer(userAccount.address, 100000)).wait(); if (logGasCosts) { console.log('ERC20 native transfer gas: ' + nativeTransferTx.gasUsed.toString()); @@ -324,15 +328,15 @@ describe('EVM equivalence contract', () => { } dumpOpcodeLogs(evmTransferTx.transactionHash, alice.provider); - expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('900000'); - expect((await evmToken.balanceOf(userAccount.address)).toString()).toEqual('100000'); + expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('900000'); + expect((await evmToken.getFunction("balanceOf")(userAccount.address)).toString()).toEqual('100000'); }); test('approve & transferFrom should work', async () => { - expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('1000000'); - const evmApproveTx = await (await evmToken.connect(alice).approve(userAccount.address, 100000)).wait(); + expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('1000000'); + const evmApproveTx = await (await evmToken.connect(alice).getFunction("approve")(userAccount.getAddress(), 100000)).wait(); const nativeApproveTx = await ( - await nativeToken.connect(alice).approve(userAccount.address, 100000) + await nativeToken.connect(alice).getFunction("approve")(userAccount.address, 100000) ).wait(); if (logGasCosts) { console.log('ERC20 native approve gas: ' + nativeApproveTx.gasUsed.toString()); @@ -341,10 +345,10 @@ describe('EVM equivalence contract', () => { dumpOpcodeLogs(evmApproveTx.transactionHash, alice.provider); const evmTransferFromTx = await ( - await evmToken.connect(userAccount).transferFrom(alice.address, userAccount.address, 100000) + await evmToken.connect(userAccount).getFunction("transferFrom")(alice.address, userAccount.address, 100000) ).wait(); const nativeTransferFromTx = await ( - await nativeToken.connect(userAccount).transferFrom(alice.address, userAccount.address, 100000) + await nativeToken.connect(userAccount).getFunction("transferFrom")(alice.address, userAccount.address, 100000) ).wait(); if (logGasCosts) { console.log('ERC20 native transferFrom gas: ' + nativeTransferFromTx.gasUsed.toString()); @@ -352,19 +356,19 @@ describe('EVM equivalence contract', () => { } dumpOpcodeLogs(evmTransferFromTx.transactionHash, alice.provider); - expect((await evmToken.balanceOf(alice.address)).toString()).toEqual('900000'); - expect((await evmToken.balanceOf(userAccount.address)).toString()).toEqual('100000'); + expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('900000'); + expect((await evmToken.getFunction("balanceOf")(userAccount.address)).toString()).toEqual('100000'); }); }); // NOTE: Gas cost comparisons should be done on a *fresh* chain that doesn't have e.g. bytecodes already published describe('Uniswap-v2', () => { - let evmToken1: ethers.Contract; - let evmToken2: ethers.Contract; - let evmUniswapFactory: ethers.Contract; - let nativeUniswapFactory: ethers.Contract; - let evmUniswapPair: ethers.Contract; - let nativeUniswapPair: ethers.Contract; + let evmToken1: ethers.BaseContract; + let evmToken2: ethers.BaseContract; + let evmUniswapFactory: ethers.BaseContract; + let nativeUniswapFactory: ethers.BaseContract; + let evmUniswapPair: ethers.BaseContract; + let nativeUniswapPair: ethers.BaseContract; let deployLogged: boolean = false; const NEW_PAIR_TOPIC = '0x0d3648bd0f6ba80134a33ba9275ac585d9d315f0ad8355cddefde31afa28d0e9'; @@ -372,13 +376,13 @@ describe('EVM equivalence contract', () => { beforeEach(async () => { const erc20Factory = getEVMContractFactory(alice, artifacts.erc20); evmToken1 = await erc20Factory.deploy({ gasLimit }); - await evmToken1.deployTransaction.wait(); + await evmToken1.deploymentTransaction()?.wait(); evmToken2 = await erc20Factory.deploy(); - await evmToken2.deployTransaction.wait(); + await evmToken2.deploymentTransaction()?.wait(); const evmUniswapFactoryFactory = getEVMContractFactory(alice, artifacts.uniswapV2Factory); evmUniswapFactory = await evmUniswapFactoryFactory.deploy('0x0000000000000000000000000000000000000000'); - await evmUniswapFactory.deployTransaction.wait(); + await evmUniswapFactory.deploymentTransaction()?.wait(); nativeUniswapFactory = await deployContract( alice, @@ -393,13 +397,13 @@ describe('EVM equivalence contract', () => { ); const evmPairReceipt = await ( - await evmUniswapFactory.createPair(evmToken1.address, evmToken2.address) + await evmUniswapFactory.getFunction("createPair")(evmToken1.getAddress(), evmToken2.getAddress()) ).wait(); const nativePairReceipt = await ( - await nativeUniswapFactory.createPair(evmToken1.address, evmToken2.address) + await nativeUniswapFactory.getFunction("createPair")(evmToken1.getAddress(), evmToken2.getAddress()) ).wait(); - dumpOpcodeLogs(evmUniswapFactory.deployTransaction.hash, alice.provider); + dumpOpcodeLogs(evmUniswapFactory.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(), alice.provider); dumpOpcodeLogs(evmPairReceipt.transactionHash, alice.provider); const evmUniswapPairFactory = getEVMContractFactory(alice, artifacts.uniswapV2Pair); @@ -409,36 +413,40 @@ describe('EVM equivalence contract', () => { alice ); evmUniswapPair = evmUniswapPairFactory.attach( - ethers.utils.defaultAbiCoder.decode( + ethers.AbiCoder.defaultAbiCoder().decode( ['address', 'uint256'], evmPairReceipt.logs.find((log: any) => log.topics[0] === NEW_PAIR_TOPIC).data )[0] ); nativeUniswapPair = nativeUniswapPairFactory.attach( - ethers.utils.defaultAbiCoder.decode( + ethers.AbiCoder.defaultAbiCoder().decode( ['address', 'uint256'], nativePairReceipt.logs.find((log: any) => log.topics[0] === NEW_PAIR_TOPIC).data )[0] ); - const token1IsFirst = (await evmUniswapPair.token0()).toString() === evmToken1.address; + const token1IsFirst = (await evmUniswapPair.getFunction("token0")()).toString() === evmToken1.getAddress(); if (!token1IsFirst) { [evmToken1, evmToken2] = [evmToken2, evmToken1]; } - await (await evmToken1.transfer(evmUniswapPair.address, 100000)).wait(); - await (await evmToken1.transfer(nativeUniswapPair.address, 100000)).wait(); - await (await evmToken2.transfer(evmUniswapPair.address, 100000)).wait(); - await (await evmToken2.transfer(nativeUniswapPair.address, 100000)).wait(); + await (await evmToken1.getFunction("transfer")(evmUniswapPair.getAddress(), 100000)).wait(); + await (await evmToken1.getFunction("transfer")(nativeUniswapPair.getAddress(), 100000)).wait(); + await (await evmToken2.getFunction("transfer")(evmUniswapPair.getAddress(), 100000)).wait(); + await (await evmToken2.getFunction("transfer")(nativeUniswapPair.getAddress(), 100000)).wait(); // Only log the first deployment if (logGasCosts && !deployLogged) { + let native_hash = nativeUniswapFactory.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(); + let native_transanction_receipt = (await alice.provider.getTransactionReceipt(native_hash)) ?? (() => { throw new Error('Deployment transaction has failed'); })(); + let evm_hash = evmUniswapFactory.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(); + let evm_transanction_receipt = (await alice.provider.getTransactionReceipt(evm_hash)) ?? (() => { throw new Error('Deployment transaction has failed'); })(); console.log( 'Uniswap Factory native deploy gas: ' + - (await alice.provider.getTransactionReceipt(nativeUniswapFactory.deployTransaction.hash)) + native_transanction_receipt .gasUsed ); console.log( 'Uniswap Factory evm deploy gas: ' + - (await alice.provider.getTransactionReceipt(evmUniswapFactory.deployTransaction.hash)).gasUsed + evm_transanction_receipt.gasUsed ); console.log('Uniswap Pair native create gas: ' + nativePairReceipt.gasUsed); console.log('Uniswap Pair evm create gas: ' + evmPairReceipt.gasUsed); @@ -447,32 +455,32 @@ describe('EVM equivalence contract', () => { }); test('mint, swap, and burn should work', async () => { - const evmMintReceipt = await (await evmUniswapPair.mint(alice.address)).wait(); - const nativeMintReceipt = await (await nativeUniswapPair.mint(alice.address)).wait(); + const evmMintReceipt = await (await evmUniswapPair.getFunction("mint")(alice.address)).wait(); + const nativeMintReceipt = await (await nativeUniswapPair.getFunction("mint")(alice.address)).wait(); dumpOpcodeLogs(evmMintReceipt.transactionHash, alice.provider); - await (await evmToken1.transfer(evmUniswapPair.address, 10000)).wait(); - await (await evmToken1.transfer(nativeUniswapPair.address, 10000)).wait(); - const evmSwapReceipt = await (await evmUniswapPair.swap(0, 5000, alice.address, '0x')).wait(); - const nativeSwapReceipt = await (await nativeUniswapPair.swap(0, 5000, alice.address, '0x')).wait(); + await (await evmToken1.getFunction("transfer")(evmUniswapPair.getAddress(), 10000)).wait(); + await (await evmToken1.getFunction("transfer")(nativeUniswapPair.getAddress(), 10000)).wait(); + const evmSwapReceipt = await (await evmUniswapPair.getFunction("swap")(0, 5000, alice.address, '0x')).wait(); + const nativeSwapReceipt = await (await nativeUniswapPair.getFunction("swap")(0, 5000, alice.address, '0x')).wait(); dumpOpcodeLogs(evmSwapReceipt.transactionHash, alice.provider); const evmLiquidityTransfer = await ( - await evmUniswapPair.transfer( - evmUniswapPair.address, - (await evmUniswapPair.balanceOf(alice.address)).toString() + await evmUniswapPair.getFunction("transfer")( + evmUniswapPair.getAddress(), + (await evmUniswapPair.getFunction("balanceOf")(alice.address)).toString() ) ).wait(); await ( - await nativeUniswapPair.transfer( - nativeUniswapPair.address, - (await nativeUniswapPair.balanceOf(alice.address)).toString() + await nativeUniswapPair.getFunction("transfer")( + nativeUniswapPair.getAddress(), + (await nativeUniswapPair.getFunction("balanceOf")(alice.address)).toString() ) ).wait(); - const evmBurnReceipt = await (await evmUniswapPair.burn(alice.address)).wait(); - const nativeBurnReceipt = await (await nativeUniswapPair.burn(alice.address)).wait(); - expect(Number((await evmToken1.balanceOf(alice.address)).toString())).toBeGreaterThanOrEqual(990000); - expect(Number((await evmToken2.balanceOf(alice.address)).toString())).toBeGreaterThanOrEqual(990000); + const evmBurnReceipt = await (await evmUniswapPair.getFunction("burn")(alice.address)).wait(); + const nativeBurnReceipt = await (await nativeUniswapPair.getFunction("burn")(alice.address)).wait(); + expect(Number((await evmToken1.getFunction("balanceOf")(alice.address)).toString())).toBeGreaterThanOrEqual(990000); + expect(Number((await evmToken2.getFunction("balanceOf")(alice.address)).toString())).toBeGreaterThanOrEqual(990000); if (logGasCosts) { console.log('UniswapV2Pair native mint gas: ' + nativeMintReceipt.gasUsed); @@ -489,17 +497,17 @@ describe('EVM equivalence contract', () => { test("Should compare gas against uniswap fallback contract's call", async () => { const gasCallerFactory = getEVMContractFactory(alice, artifacts.gasCaller); const gasCallerContract = await gasCallerFactory.deploy(); - await gasCallerContract.deployTransaction.wait(); - await alice.provider.getTransactionReceipt(gasCallerContract.deployTransaction.hash); + await gasCallerContract.deploymentTransaction()?.wait(); + await alice.provider.getTransactionReceipt(gasCallerContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })()); const uniswapContract = await deploygasCallerContract(alice, artifacts.uniswapFallback); - await (await uniswapContract.setUniswapAddress(evmUniswapPair.address)).wait(); - await (await uniswapContract.setAliceAddress(alice.address)).wait(); + await (await uniswapContract.getFunction("setUniswapAddress")(evmUniswapPair.getAddress())).wait(); + await (await uniswapContract.getFunction("setAliceAddress")(alice.address)).wait(); - await (await evmToken1.transfer(evmUniswapPair.address, 10000)).wait(); - await (await evmToken1.transfer(uniswapContract.address, 10000)).wait(); + await (await evmToken1.getFunction("transfer")(evmUniswapPair.getAddress(), 10000)).wait(); + await (await evmToken1.getFunction("transfer")(uniswapContract.getAddress(), 10000)).wait(); - let result = (await gasCallerContract.callStatic.callAndGetGas(uniswapContract.address)).toString(); + let result = (await gasCallerContract.getFunction("callAndGetGas").staticCall(uniswapContract.getAddress())).toString(); const expected_gas = '165939'; // Gas cost when run with solidity interpreter expect(result).toEqual(expected_gas); @@ -635,9 +643,11 @@ async function assertStoredBytecodeHash( expectedStoredHash: string ): Promise { const ACCOUNT_CODE_STORAGE_ADDRESS = '0x0000000000000000000000000000000000008002'; - const storedCodeHash = await deployer.provider.getStorageAt( + let runner = deployer.runner ?? (() => { throw new Error('Runner get failed'); })(); + let provider = runner.provider ?? (() => { throw new Error('Provider get failed'); })(); + const storedCodeHash = await provider.getStorage( ACCOUNT_CODE_STORAGE_ADDRESS, - ethers.utils.hexZeroPad(deployedAddress, 32) + ethers.zeroPadValue(deployedAddress, 32) ); expect(storedCodeHash).toEqual(expectedStoredHash); @@ -647,18 +657,17 @@ async function assertCreatedCorrectly( deployer: zksync.Contract, deployedAddress: string, expectedEVMBytecode: string, - logs: Array ): Promise { const expectedStoredHash = getSha256BlobHash(expectedEVMBytecode); await assertStoredBytecodeHash(deployer, deployedAddress, expectedStoredHash); } function getPaddedBytecode(bytes: ethers.BytesLike) { - const length = ethers.utils.arrayify(bytes).length; + const length = ethers.getBytes(bytes).length; - const encodedLength = ethers.utils.defaultAbiCoder.encode(['uint256'], [length]); + const encodedLength = ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [length]); - let paddedBytecode = encodedLength + ethers.utils.hexlify(bytes).slice(2); + let paddedBytecode = encodedLength + ethers.toBeHex(ethers.toBigInt(bytes)).slice(2); // The length needs to be 32 mod 64. We use 64 mod 128, since // we are dealing with a hexlified string @@ -673,20 +682,20 @@ function getPaddedBytecode(bytes: ethers.BytesLike) { function getSha256BlobHash(bytes: ethers.BytesLike): string { const paddedBytes = getPaddedBytecode(bytes); - const hash = ethers.utils.arrayify(ethers.utils.sha256(paddedBytes)); + const hash = ethers.getBytes(ethers.sha256(paddedBytes)); hash[0] = 2; hash[1] = 0; // Length of the bytecode - const lengthInBytes = ethers.utils.arrayify(paddedBytes).length; + const lengthInBytes = ethers.getBytes(paddedBytes).length; hash[2] = Math.floor(lengthInBytes / 256); hash[3] = lengthInBytes % 256; - return ethers.utils.hexlify(hash); + return ethers.toBeHex(ethers.toBigInt(hash)); } async function assertContractNotCreated(deployer: zksync.Contract, deployedAddress: string): Promise { - assertStoredBytecodeHash(deployer, deployedAddress, ethers.constants.HashZero); + assertStoredBytecodeHash(deployer, deployedAddress, ethers.ZeroHash); } function printCostData() { @@ -877,14 +886,15 @@ const opcodeDataDump: any = {}; }); async function dumpOpcodeLogs(hash: string, provider: zksync.Provider): Promise { - const logs = (await provider.getTransactionReceipt(hash)).logs; + let tx_receipt = (await provider.getTransactionReceipt(hash)) ?? (() => { throw new Error('Get Transaction Receipt has failed'); })(); + const logs = tx_receipt.logs; logs.forEach((log) => { if (log.topics[0] === '0x63307236653da06aaa7e128a306b128c594b4cf3b938ef212975ed10dad17515') { //Overhead - overheadDataDump.push(Number(ethers.utils.defaultAbiCoder.decode(['uint256'], log.data).toString())); + overheadDataDump.push(Number(ethers.AbiCoder.defaultAbiCoder().decode(['uint256'], log.data).toString())); } else if (log.topics[0] === '0xca5a69edf1b934943a56c00605317596b03e2f61c3f633e8657b150f102a3dfa') { // Opcode - const parsed = ethers.utils.defaultAbiCoder.decode(['uint256', 'uint256'], log.data); + const parsed = ethers.AbiCoder.defaultAbiCoder().decode(['uint256', 'uint256'], log.data); const opcode = Number(parsed[0].toString()); const cost = Number(parsed[1].toString()); From 3561a5ba03c9a70d0a55123b09edd2f2c77e9ea3 Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 19 Aug 2024 16:37:33 -0300 Subject: [PATCH 03/10] Format code --- .../tests/evm-contracts.test.ts | 297 ++++++++++++------ 1 file changed, 202 insertions(+), 95 deletions(-) diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts index 316e65ce4bd..8c876a1b76b 100644 --- a/core/tests/ts-integration/tests/evm-contracts.test.ts +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -68,7 +68,9 @@ describe('EVM equivalence contract', () => { const counterContract = await deploygasCallerContract(alice, artifacts.counterFallback); - let result = (await gasCallerContract.getFunction("callAndGetGas").staticCall(counterContract.getAddress())).toString(); + let result = ( + await gasCallerContract.getFunction('callAndGetGas').staticCall(counterContract.getAddress()) + ).toString(); const expected_gas = '3617'; // Gas cost when run with solidity interpreter expect(result).toEqual(expected_gas); @@ -79,7 +81,9 @@ describe('EVM equivalence contract', () => { const creatorContract = await deploygasCallerContract(alice, artifacts.creatorFallback); - let result = (await gasCallerContract.getFunction("callAndGetGas").staticCall(creatorContract.getAddress())).toString(); + let result = ( + await gasCallerContract.getFunction('callAndGetGas').staticCall(creatorContract.getAddress()) + ).toString(); const expected_gas = '70601'; // Gas cost when run with solidity interpreter expect(result).toEqual(expected_gas); @@ -90,7 +94,9 @@ describe('EVM equivalence contract', () => { const counterContract = await deploygasCallerContract(alice, artifacts.opcodeTestFallback); - let result = (await gasCallerContract.getFunction("callAndGetGas").staticCall(counterContract.getAddress())).toString(); + let result = ( + await gasCallerContract.getFunction('callAndGetGas').staticCall(counterContract.getAddress()) + ).toString(); const expected_gas = '34763'; // Gas cost when run with solidity interpreter expect(result).toEqual(expected_gas); @@ -104,17 +110,22 @@ describe('EVM equivalence contract', () => { const factory = getEVMContractFactory(alice, artifacts.counter); const contract = await factory.deploy(args); await contract.deploymentTransaction()?.wait(); - const receipt = await alice.provider.getTransactionReceipt(contract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })()); + const receipt = await alice.provider.getTransactionReceipt( + contract.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })() + ); await assertCreatedCorrectly( deployer, await contract.getAddress(), - '0x' + artifacts.counter.evm.deployedBytecode.object, + '0x' + artifacts.counter.evm.deployedBytecode.object ); - expect((await contract.getFunction("get").staticCall()).toString()).toEqual('1'); - await (await contract.getFunction("increment")(1)).wait(); - expect((await contract.getFunction("get").staticCall()).toString()).toEqual('2'); + expect((await contract.getFunction('get').staticCall()).toString()).toEqual('1'); + await (await contract.getFunction('increment')(1)).wait(); + expect((await contract.getFunction('get').staticCall()).toString()).toEqual('2'); }); test('Should create2 evm contract from ZKEVM contract', async () => { @@ -207,22 +218,38 @@ describe('EVM equivalence contract', () => { const counterFactory = getEVMContractFactory(alice, artifacts.counter); const counterContract = await counterFactory.deploy(args); await counterContract.deploymentTransaction()?.wait(); - await alice.provider.getTransactionReceipt(counterContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })()); + await alice.provider.getTransactionReceipt( + counterContract.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })() + ); const proxyCallerFactory = getEVMContractFactory(alice, artifacts.proxyCaller); const proxyCallerContract = await proxyCallerFactory.deploy(); await proxyCallerContract.deploymentTransaction()?.wait(); - await alice.provider.getTransactionReceipt(proxyCallerContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })()); + await alice.provider.getTransactionReceipt( + proxyCallerContract.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })() + ); - expect((await proxyCallerContract.getFunction("proxyGet")(counterContract.getAddress())).toString()).toEqual('1'); + expect( + (await proxyCallerContract.getFunction('proxyGet')(counterContract.getAddress())).toString() + ).toEqual('1'); - await (await proxyCallerContract.getFunction("executeIncrememt")(counterContract.getAddress(), 1)).wait(); + await (await proxyCallerContract.getFunction('executeIncrememt')(counterContract.getAddress(), 1)).wait(); - expect((await proxyCallerContract.getFunction("proxyGet")(counterContract.getAddress())).toString()).toEqual('2'); + expect( + (await proxyCallerContract.getFunction('proxyGet')(counterContract.getAddress())).toString() + ).toEqual('2'); - expect((await proxyCallerContract.getFunction("proxyGetBytes").staticCall(counterContract.getAddress())).toString()).toEqual( - '0x54657374696e67' - ); + expect( + ( + await proxyCallerContract.getFunction('proxyGetBytes').staticCall(counterContract.getAddress()) + ).toString() + ).toEqual('0x54657374696e67'); }); test('Create opcode works correctly', async () => { @@ -230,18 +257,24 @@ describe('EVM equivalence contract', () => { const creatorContract = await creatorFactory.deploy(); await creatorContract.deploymentTransaction()?.wait(); - dumpOpcodeLogs(creatorContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(), alice.provider); + dumpOpcodeLogs( + creatorContract.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(), + alice.provider + ); const nonce = 1; - const runtimeBytecode = await creatorContract.getFunction("getCreationRuntimeCode")(); + const runtimeBytecode = await creatorContract.getFunction('getCreationRuntimeCode')(); const expectedAddress = ethers.getCreateAddress({ from: await creatorContract.getAddress(), nonce }); - const result = await (await creatorContract.getFunction("create")()).wait(); + const result = await (await creatorContract.getFunction('create')()).wait(); dumpOpcodeLogs(result.transactionHash, alice.provider); await assertCreatedCorrectly(deployer, expectedAddress, runtimeBytecode); @@ -254,12 +287,18 @@ describe('EVM equivalence contract', () => { const counterContract = await counterFactory.deploy(args); await counterContract.deploymentTransaction()?.wait(); - dumpOpcodeLogs(counterContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(), alice.provider); + dumpOpcodeLogs( + counterContract.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(), + alice.provider + ); let errorString; try { - await counterContract.getFunction("incrementWithRevert").staticCall(1, true); + await counterContract.getFunction('incrementWithRevert').staticCall(1, true); } catch (e: any) { errorString = e.reason; } @@ -280,22 +319,38 @@ describe('EVM equivalence contract', () => { await evmToken.deploymentTransaction()?.wait(); nativeToken = await deployContract(alice, contracts.erc20, []); - dumpOpcodeLogs(evmToken.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(), alice.provider); + dumpOpcodeLogs( + evmToken.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(), + alice.provider + ); userAccount = testMaster.newEmptyAccount(); // Only log the first deployment if (logGasCosts && !deployLogged) { - let native_hash = nativeToken.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(); - let native_transanction_receipt = (await alice.provider.getTransactionReceipt(native_hash)) ?? (() => { throw new Error('Deployment transaction has failed'); })(); - let evm_hash = evmToken.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(); - let evm_transanction_receipt = (await alice.provider.getTransactionReceipt(evm_hash)) ?? (() => { throw new Error('Deployment transaction has failed'); })(); - console.log( - 'ERC20 native deploy gas: ' + - native_transanction_receipt.gasUsed - ); - console.log( - 'ERC20 evm deploy gas: ' + - evm_transanction_receipt.gasUsed - ); + let native_hash = + nativeToken.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(); + let native_transanction_receipt = + (await alice.provider.getTransactionReceipt(native_hash)) ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(); + let evm_hash = + evmToken.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(); + let evm_transanction_receipt = + (await alice.provider.getTransactionReceipt(evm_hash)) ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(); + console.log('ERC20 native deploy gas: ' + native_transanction_receipt.gasUsed); + console.log('ERC20 evm deploy gas: ' + evm_transanction_receipt.gasUsed); deployLogged = true; } await ( @@ -307,20 +362,20 @@ describe('EVM equivalence contract', () => { }); test('view functions should work', async () => { - const evmBalanceOfCost = await evmToken.getFunction("balanceOf").estimateGas(alice.address); - const nativeBalanceOfCost = await nativeToken.getFunction("balanceOf").estimateGas(alice.address); + const evmBalanceOfCost = await evmToken.getFunction('balanceOf').estimateGas(alice.address); + const nativeBalanceOfCost = await nativeToken.getFunction('balanceOf').estimateGas(alice.address); if (logGasCosts) { console.log('ERC20 native balanceOf gas: ' + nativeBalanceOfCost.toString()); console.log('ERC20 evm balanceOf gas: ' + evmBalanceOfCost.toString()); } - expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('1000000'); - expect((await evmToken.getFunction("totalSupply")()).toString()).toEqual('1000000'); - expect((await evmToken.getFunction("balanceOf")(userAccount.address)).toString()).toEqual('0'); + expect((await evmToken.getFunction('balanceOf')(alice.address)).toString()).toEqual('1000000'); + expect((await evmToken.getFunction('totalSupply')()).toString()).toEqual('1000000'); + expect((await evmToken.getFunction('balanceOf')(userAccount.address)).toString()).toEqual('0'); }); test('transfer should work', async () => { - expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('1000000'); - const evmTransferTx = await (await evmToken.getFunction("transfer")(userAccount.address, 100000)).wait(); + expect((await evmToken.getFunction('balanceOf')(alice.address)).toString()).toEqual('1000000'); + const evmTransferTx = await (await evmToken.getFunction('transfer')(userAccount.address, 100000)).wait(); const nativeTransferTx = await (await nativeToken.transfer(userAccount.address, 100000)).wait(); if (logGasCosts) { console.log('ERC20 native transfer gas: ' + nativeTransferTx.gasUsed.toString()); @@ -328,15 +383,17 @@ describe('EVM equivalence contract', () => { } dumpOpcodeLogs(evmTransferTx.transactionHash, alice.provider); - expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('900000'); - expect((await evmToken.getFunction("balanceOf")(userAccount.address)).toString()).toEqual('100000'); + expect((await evmToken.getFunction('balanceOf')(alice.address)).toString()).toEqual('900000'); + expect((await evmToken.getFunction('balanceOf')(userAccount.address)).toString()).toEqual('100000'); }); test('approve & transferFrom should work', async () => { - expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('1000000'); - const evmApproveTx = await (await evmToken.connect(alice).getFunction("approve")(userAccount.getAddress(), 100000)).wait(); + expect((await evmToken.getFunction('balanceOf')(alice.address)).toString()).toEqual('1000000'); + const evmApproveTx = await ( + await evmToken.connect(alice).getFunction('approve')(userAccount.getAddress(), 100000) + ).wait(); const nativeApproveTx = await ( - await nativeToken.connect(alice).getFunction("approve")(userAccount.address, 100000) + await nativeToken.connect(alice).getFunction('approve')(userAccount.address, 100000) ).wait(); if (logGasCosts) { console.log('ERC20 native approve gas: ' + nativeApproveTx.gasUsed.toString()); @@ -345,10 +402,18 @@ describe('EVM equivalence contract', () => { dumpOpcodeLogs(evmApproveTx.transactionHash, alice.provider); const evmTransferFromTx = await ( - await evmToken.connect(userAccount).getFunction("transferFrom")(alice.address, userAccount.address, 100000) + await evmToken.connect(userAccount).getFunction('transferFrom')( + alice.address, + userAccount.address, + 100000 + ) ).wait(); const nativeTransferFromTx = await ( - await nativeToken.connect(userAccount).getFunction("transferFrom")(alice.address, userAccount.address, 100000) + await nativeToken.connect(userAccount).getFunction('transferFrom')( + alice.address, + userAccount.address, + 100000 + ) ).wait(); if (logGasCosts) { console.log('ERC20 native transferFrom gas: ' + nativeTransferFromTx.gasUsed.toString()); @@ -356,8 +421,8 @@ describe('EVM equivalence contract', () => { } dumpOpcodeLogs(evmTransferFromTx.transactionHash, alice.provider); - expect((await evmToken.getFunction("balanceOf")(alice.address)).toString()).toEqual('900000'); - expect((await evmToken.getFunction("balanceOf")(userAccount.address)).toString()).toEqual('100000'); + expect((await evmToken.getFunction('balanceOf')(alice.address)).toString()).toEqual('900000'); + expect((await evmToken.getFunction('balanceOf')(userAccount.address)).toString()).toEqual('100000'); }); }); @@ -397,13 +462,19 @@ describe('EVM equivalence contract', () => { ); const evmPairReceipt = await ( - await evmUniswapFactory.getFunction("createPair")(evmToken1.getAddress(), evmToken2.getAddress()) + await evmUniswapFactory.getFunction('createPair')(evmToken1.getAddress(), evmToken2.getAddress()) ).wait(); const nativePairReceipt = await ( - await nativeUniswapFactory.getFunction("createPair")(evmToken1.getAddress(), evmToken2.getAddress()) + await nativeUniswapFactory.getFunction('createPair')(evmToken1.getAddress(), evmToken2.getAddress()) ).wait(); - dumpOpcodeLogs(evmUniswapFactory.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(), alice.provider); + dumpOpcodeLogs( + evmUniswapFactory.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(), + alice.provider + ); dumpOpcodeLogs(evmPairReceipt.transactionHash, alice.provider); const evmUniswapPairFactory = getEVMContractFactory(alice, artifacts.uniswapV2Pair); @@ -424,30 +495,39 @@ describe('EVM equivalence contract', () => { nativePairReceipt.logs.find((log: any) => log.topics[0] === NEW_PAIR_TOPIC).data )[0] ); - const token1IsFirst = (await evmUniswapPair.getFunction("token0")()).toString() === evmToken1.getAddress(); + const token1IsFirst = (await evmUniswapPair.getFunction('token0')()).toString() === evmToken1.getAddress(); if (!token1IsFirst) { [evmToken1, evmToken2] = [evmToken2, evmToken1]; } - await (await evmToken1.getFunction("transfer")(evmUniswapPair.getAddress(), 100000)).wait(); - await (await evmToken1.getFunction("transfer")(nativeUniswapPair.getAddress(), 100000)).wait(); - await (await evmToken2.getFunction("transfer")(evmUniswapPair.getAddress(), 100000)).wait(); - await (await evmToken2.getFunction("transfer")(nativeUniswapPair.getAddress(), 100000)).wait(); + await (await evmToken1.getFunction('transfer')(evmUniswapPair.getAddress(), 100000)).wait(); + await (await evmToken1.getFunction('transfer')(nativeUniswapPair.getAddress(), 100000)).wait(); + await (await evmToken2.getFunction('transfer')(evmUniswapPair.getAddress(), 100000)).wait(); + await (await evmToken2.getFunction('transfer')(nativeUniswapPair.getAddress(), 100000)).wait(); // Only log the first deployment if (logGasCosts && !deployLogged) { - let native_hash = nativeUniswapFactory.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(); - let native_transanction_receipt = (await alice.provider.getTransactionReceipt(native_hash)) ?? (() => { throw new Error('Deployment transaction has failed'); })(); - let evm_hash = evmUniswapFactory.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })(); - let evm_transanction_receipt = (await alice.provider.getTransactionReceipt(evm_hash)) ?? (() => { throw new Error('Deployment transaction has failed'); })(); - console.log( - 'Uniswap Factory native deploy gas: ' + - native_transanction_receipt - .gasUsed - ); - console.log( - 'Uniswap Factory evm deploy gas: ' + - evm_transanction_receipt.gasUsed - ); + let native_hash = + nativeUniswapFactory.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(); + let native_transanction_receipt = + (await alice.provider.getTransactionReceipt(native_hash)) ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(); + let evm_hash = + evmUniswapFactory.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(); + let evm_transanction_receipt = + (await alice.provider.getTransactionReceipt(evm_hash)) ?? + (() => { + throw new Error('Deployment transaction has failed'); + })(); + console.log('Uniswap Factory native deploy gas: ' + native_transanction_receipt.gasUsed); + console.log('Uniswap Factory evm deploy gas: ' + evm_transanction_receipt.gasUsed); console.log('Uniswap Pair native create gas: ' + nativePairReceipt.gasUsed); console.log('Uniswap Pair evm create gas: ' + evmPairReceipt.gasUsed); deployLogged = true; @@ -455,32 +535,40 @@ describe('EVM equivalence contract', () => { }); test('mint, swap, and burn should work', async () => { - const evmMintReceipt = await (await evmUniswapPair.getFunction("mint")(alice.address)).wait(); - const nativeMintReceipt = await (await nativeUniswapPair.getFunction("mint")(alice.address)).wait(); + const evmMintReceipt = await (await evmUniswapPair.getFunction('mint')(alice.address)).wait(); + const nativeMintReceipt = await (await nativeUniswapPair.getFunction('mint')(alice.address)).wait(); dumpOpcodeLogs(evmMintReceipt.transactionHash, alice.provider); - await (await evmToken1.getFunction("transfer")(evmUniswapPair.getAddress(), 10000)).wait(); - await (await evmToken1.getFunction("transfer")(nativeUniswapPair.getAddress(), 10000)).wait(); - const evmSwapReceipt = await (await evmUniswapPair.getFunction("swap")(0, 5000, alice.address, '0x')).wait(); - const nativeSwapReceipt = await (await nativeUniswapPair.getFunction("swap")(0, 5000, alice.address, '0x')).wait(); + await (await evmToken1.getFunction('transfer')(evmUniswapPair.getAddress(), 10000)).wait(); + await (await evmToken1.getFunction('transfer')(nativeUniswapPair.getAddress(), 10000)).wait(); + const evmSwapReceipt = await ( + await evmUniswapPair.getFunction('swap')(0, 5000, alice.address, '0x') + ).wait(); + const nativeSwapReceipt = await ( + await nativeUniswapPair.getFunction('swap')(0, 5000, alice.address, '0x') + ).wait(); dumpOpcodeLogs(evmSwapReceipt.transactionHash, alice.provider); const evmLiquidityTransfer = await ( - await evmUniswapPair.getFunction("transfer")( + await evmUniswapPair.getFunction('transfer')( evmUniswapPair.getAddress(), - (await evmUniswapPair.getFunction("balanceOf")(alice.address)).toString() + (await evmUniswapPair.getFunction('balanceOf')(alice.address)).toString() ) ).wait(); await ( - await nativeUniswapPair.getFunction("transfer")( + await nativeUniswapPair.getFunction('transfer')( nativeUniswapPair.getAddress(), - (await nativeUniswapPair.getFunction("balanceOf")(alice.address)).toString() + (await nativeUniswapPair.getFunction('balanceOf')(alice.address)).toString() ) ).wait(); - const evmBurnReceipt = await (await evmUniswapPair.getFunction("burn")(alice.address)).wait(); - const nativeBurnReceipt = await (await nativeUniswapPair.getFunction("burn")(alice.address)).wait(); - expect(Number((await evmToken1.getFunction("balanceOf")(alice.address)).toString())).toBeGreaterThanOrEqual(990000); - expect(Number((await evmToken2.getFunction("balanceOf")(alice.address)).toString())).toBeGreaterThanOrEqual(990000); + const evmBurnReceipt = await (await evmUniswapPair.getFunction('burn')(alice.address)).wait(); + const nativeBurnReceipt = await (await nativeUniswapPair.getFunction('burn')(alice.address)).wait(); + expect(Number((await evmToken1.getFunction('balanceOf')(alice.address)).toString())).toBeGreaterThanOrEqual( + 990000 + ); + expect(Number((await evmToken2.getFunction('balanceOf')(alice.address)).toString())).toBeGreaterThanOrEqual( + 990000 + ); if (logGasCosts) { console.log('UniswapV2Pair native mint gas: ' + nativeMintReceipt.gasUsed); @@ -498,16 +586,23 @@ describe('EVM equivalence contract', () => { const gasCallerFactory = getEVMContractFactory(alice, artifacts.gasCaller); const gasCallerContract = await gasCallerFactory.deploy(); await gasCallerContract.deploymentTransaction()?.wait(); - await alice.provider.getTransactionReceipt(gasCallerContract.deploymentTransaction()?.hash ?? (() => { throw new Error('Deployment transaction has failed'); })()); + await alice.provider.getTransactionReceipt( + gasCallerContract.deploymentTransaction()?.hash ?? + (() => { + throw new Error('Deployment transaction has failed'); + })() + ); const uniswapContract = await deploygasCallerContract(alice, artifacts.uniswapFallback); - await (await uniswapContract.getFunction("setUniswapAddress")(evmUniswapPair.getAddress())).wait(); - await (await uniswapContract.getFunction("setAliceAddress")(alice.address)).wait(); + await (await uniswapContract.getFunction('setUniswapAddress')(evmUniswapPair.getAddress())).wait(); + await (await uniswapContract.getFunction('setAliceAddress')(alice.address)).wait(); - await (await evmToken1.getFunction("transfer")(evmUniswapPair.getAddress(), 10000)).wait(); - await (await evmToken1.getFunction("transfer")(uniswapContract.getAddress(), 10000)).wait(); + await (await evmToken1.getFunction('transfer')(evmUniswapPair.getAddress(), 10000)).wait(); + await (await evmToken1.getFunction('transfer')(uniswapContract.getAddress(), 10000)).wait(); - let result = (await gasCallerContract.getFunction("callAndGetGas").staticCall(uniswapContract.getAddress())).toString(); + let result = ( + await gasCallerContract.getFunction('callAndGetGas').staticCall(uniswapContract.getAddress()) + ).toString(); const expected_gas = '165939'; // Gas cost when run with solidity interpreter expect(result).toEqual(expected_gas); @@ -643,8 +738,16 @@ async function assertStoredBytecodeHash( expectedStoredHash: string ): Promise { const ACCOUNT_CODE_STORAGE_ADDRESS = '0x0000000000000000000000000000000000008002'; - let runner = deployer.runner ?? (() => { throw new Error('Runner get failed'); })(); - let provider = runner.provider ?? (() => { throw new Error('Provider get failed'); })(); + let runner = + deployer.runner ?? + (() => { + throw new Error('Runner get failed'); + })(); + let provider = + runner.provider ?? + (() => { + throw new Error('Provider get failed'); + })(); const storedCodeHash = await provider.getStorage( ACCOUNT_CODE_STORAGE_ADDRESS, ethers.zeroPadValue(deployedAddress, 32) @@ -656,7 +759,7 @@ async function assertStoredBytecodeHash( async function assertCreatedCorrectly( deployer: zksync.Contract, deployedAddress: string, - expectedEVMBytecode: string, + expectedEVMBytecode: string ): Promise { const expectedStoredHash = getSha256BlobHash(expectedEVMBytecode); await assertStoredBytecodeHash(deployer, deployedAddress, expectedStoredHash); @@ -886,7 +989,11 @@ const opcodeDataDump: any = {}; }); async function dumpOpcodeLogs(hash: string, provider: zksync.Provider): Promise { - let tx_receipt = (await provider.getTransactionReceipt(hash)) ?? (() => { throw new Error('Get Transaction Receipt has failed'); })(); + let tx_receipt = + (await provider.getTransactionReceipt(hash)) ?? + (() => { + throw new Error('Get Transaction Receipt has failed'); + })(); const logs = tx_receipt.logs; logs.forEach((log) => { if (log.topics[0] === '0x63307236653da06aaa7e128a306b128c594b4cf3b938ef212975ed10dad17515') { From 0e95d8d7a2884d1a8d9e1e2c38ccddb48e884728 Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:49:22 -0300 Subject: [PATCH 04/10] Fix some tests --- core/tests/ts-integration/package.json | 3 ++- .../tests/evm-contracts.test.ts | 18 +++++++++++------- etc/env/base/chain.toml | 2 +- etc/env/base/contracts.toml | 4 ++-- .../fee_estimate.yul/fee_estimate.yul.zbin | Bin 76256 -> 76064 bytes .../gas_test.yul/gas_test.yul.zbin | Bin 72352 -> 72160 bytes .../playground_batch.yul.zbin | Bin 76448 -> 76256 bytes .../proved_batch.yul/proved_batch.yul.zbin | Bin 72864 -> 72672 bytes yarn.lock | 13 +++++++++++++ 9 files changed, 29 insertions(+), 11 deletions(-) diff --git a/core/tests/ts-integration/package.json b/core/tests/ts-integration/package.json index e6c414bbaaf..2f438a920cb 100644 --- a/core/tests/ts-integration/package.json +++ b/core/tests/ts-integration/package.json @@ -35,6 +35,7 @@ "yaml": "^2.4.2", "zksync-web3": "^0.15.5", "csv-parser": "^3.0.0", - "csv-writer": "^1.6.0" + "csv-writer": "^1.6.0", + "solc": "0.8.20" } } diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts index 8c876a1b76b..3f188bc459f 100644 --- a/core/tests/ts-integration/tests/evm-contracts.test.ts +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -85,7 +85,7 @@ describe('EVM equivalence contract', () => { await gasCallerContract.getFunction('callAndGetGas').staticCall(creatorContract.getAddress()) ).toString(); - const expected_gas = '70601'; // Gas cost when run with solidity interpreter + const expected_gas = '70598'; // Gas cost when run with solidity interpreter - 3 (We have some changes that are needed) expect(result).toEqual(expected_gas); }); @@ -106,7 +106,7 @@ describe('EVM equivalence contract', () => { describe('Contract creation', () => { describe('Create from EOA', () => { test('Should create evm contract from EOA and allow view and non-view calls', async () => { - const args = [1]; + const args = 1; const factory = getEVMContractFactory(alice, artifacts.counter); const contract = await factory.deploy(args); await contract.deploymentTransaction()?.wait(); @@ -162,16 +162,18 @@ describe('EVM equivalence contract', () => { failReason = e.reason; } - expect(failReason).toBe('transaction failed'); + expect(failReason).toBe(null); }); test('Should NOT create evm contract from EOA when `to` is address(0x0)', async () => { - const args = [1]; + const args = 1; const factory = getEVMContractFactory(alice, artifacts.counter); const transaction = await factory.getDeployTransaction(args); + console.log(transaction); let dep_transaction = ethers.Transaction.from(transaction); dep_transaction.to = '0x0000000000000000000000000000000000000000'; + console.log(dep_transaction); const result = await (await alice.sendTransaction(dep_transaction)).wait(); const expectedAddressCreate = ethers.getCreateAddress({ @@ -213,7 +215,7 @@ describe('EVM equivalence contract', () => { describe('Inter-contract calls', () => { test('Calls (read/write) between EVM contracts should work correctly', async () => { - const args = [1]; + const args = 1; const counterFactory = getEVMContractFactory(alice, artifacts.counter); const counterContract = await counterFactory.deploy(args); @@ -281,7 +283,7 @@ describe('EVM equivalence contract', () => { }); test('Should revert correctly', async () => { - const args = [1]; + const args = 1; const counterFactory = getEVMContractFactory(alice, artifacts.counter); const counterContract = await counterFactory.deploy(args); @@ -294,7 +296,7 @@ describe('EVM equivalence contract', () => { })(), alice.provider ); - + let errorString; try { @@ -302,6 +304,8 @@ describe('EVM equivalence contract', () => { } catch (e: any) { errorString = e.reason; } + console.log(errorString); + expect(errorString).toEqual('This method always reverts'); }); }); diff --git a/etc/env/base/chain.toml b/etc/env/base/chain.toml index 58fca8eff96..5f1a9a1603d 100644 --- a/etc/env/base/chain.toml +++ b/etc/env/base/chain.toml @@ -90,7 +90,7 @@ fee_model_version = "V2" validation_computational_gas_limit = 300000 save_call_traces = true -bootloader_hash = "0x010008e54ed4ed2784197a39309255faa062e7f130424f0075bfd7ff11eb22d6" +bootloader_hash = "0x010008df0ef90cb578022657652c9ac6339748e080ea22d185ba9a07e0238a28" default_aa_hash = "0x0100058deb36e1f2eeb48bf3846d0e8eb38e9176754b73116bb41a472459a4dd" evm_simulator_hash = "0x01000f197081a9906cc411d0698c4961aeb5c74877f37f7071681da6e8ef3f31" diff --git a/etc/env/base/contracts.toml b/etc/env/base/contracts.toml index 47f1e57fea1..65c18b32068 100644 --- a/etc/env/base/contracts.toml +++ b/etc/env/base/contracts.toml @@ -26,8 +26,8 @@ RECURSION_NODE_LEVEL_VK_HASH = "0x1186ec268d49f1905f8d9c1e9d39fc33e98c74f91d91a2 RECURSION_LEAF_LEVEL_VK_HASH = "0x101e08b00193e529145ee09823378ef51a3bc8966504064f1f6ba3f1ba863210" RECURSION_CIRCUITS_SET_VKS_HASH = "0x18c1639094f58177409186e8c48d9f577c9410901d2f1d486b3e7d6cf553ae4c" GENESIS_TX_HASH = "0xb99ebfea46cbe05a21cd80fe5597d97b204befc52a16303f579c607dc1ac2e2e" -GENESIS_ROOT = "0xf511c8e55095e444bd86bc2813689935b2098edda19aa7f9d080b506e2cfc69f" -GENESIS_BATCH_COMMITMENT = "0x7c46546a6d6268914e6ee58b51a38fa44dcc707efadf8af3e68e5f3b3a28e0af" +GENESIS_ROOT = "0xd2b1a071a2da0f92d65e58e7db4743273963c1b8fa6c6a7b565e04128b5441f3" +GENESIS_BATCH_COMMITMENT = "0x300898d4ff7fb555d3ab3eb669e1d0d75efdd32e68a5a61dd4198b1ea3a1e53b" PRIORITY_TX_MAX_GAS_LIMIT = 72000000 DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT = 10000000 GENESIS_ROLLUP_LEAF_INDEX = "56" diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/fee_estimate.yul/fee_estimate.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/fee_estimate.yul/fee_estimate.yul.zbin index 785b3b1fc1768b9dc0d9cca3605b7e7b001f9470..a48d89ebe7ff2c5640d3c15271298e7689b7a370 100644 GIT binary patch literal 76064 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 diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/gas_test.yul/gas_test.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/gas_test.yul/gas_test.yul.zbin index 76009d66d53fc019df60971cf1614aaf85d07a04..5c325c1ce13b4190a2613e3f85744804d315ed13 100644 GIT binary patch literal 72160 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 Drv`(VLPEsH=T3c&J*p_5T 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 diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/playground_batch.yul/playground_batch.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/playground_batch.yul/playground_batch.yul.zbin index 286052468b316f596234fc492881b0bc0087229a..60749b415b87a731d7f7a937c28b9fd6b68a1c18 100644 GIT binary patch literal 76256 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 diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/proved_batch.yul/proved_batch.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/proved_batch.yul/proved_batch.yul.zbin index 56c5f67812c8874f08130f68645bb4fb84b04f8a..ca866fc73665a8b3fbfbc1fe83e859690793ca3c 100644 GIT binary patch literal 72672 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 VUUPGP|BLTmcgqc@zVI{m|9^8+93%h$ diff --git a/yarn.lock b/yarn.lock index be6687ce6ed..fb00befa2ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9915,6 +9915,19 @@ solc@0.8.17: semver "^5.5.0" tmp "0.0.33" +solc@0.8.20: + version "0.8.20" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.20.tgz#b49151cf5ecc8de088d3d32b0afb607b3522ba8d" + integrity sha512-fPRnGspIEqmhu63RFO3pc79sLA7ZmzO0Uy0L5l6hEt2wAsq0o7UV6pXkAp3Mfv9IBhg7Px/oTu3a+y4gs3BWrQ== + dependencies: + command-exists "^1.2.8" + commander "^8.1.0" + follow-redirects "^1.12.1" + js-sha3 "0.8.0" + memorystream "^0.3.1" + semver "^5.5.0" + tmp "0.0.33" + solc@0.8.26: version "0.8.26" resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.26.tgz#afc78078953f6ab3e727c338a2fefcd80dd5b01a" From 0c939090e0b2e6d0677dd4eab960c915f2a7b4c9 Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 19 Aug 2024 18:53:08 -0300 Subject: [PATCH 05/10] Format code --- contracts | 2 +- core/tests/ts-integration/tests/evm-contracts.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts b/contracts index b6a629f9b55..af2a2c9113f 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit b6a629f9b55b2b59409e086469c8cdff0c4bd56c +Subproject commit af2a2c9113f9d6510f45ee2ae08d29d623d03c99 diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts index 3f188bc459f..af012119b9e 100644 --- a/core/tests/ts-integration/tests/evm-contracts.test.ts +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -296,7 +296,7 @@ describe('EVM equivalence contract', () => { })(), alice.provider ); - + let errorString; try { From cdcf570a4f6f221239c2285ae507ff698347654c Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:53:56 -0300 Subject: [PATCH 06/10] Fix remaining tests --- .../tests/evm-contracts.test.ts | 101 ++---------------- 1 file changed, 11 insertions(+), 90 deletions(-) diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts index af012119b9e..75ed265094b 100644 --- a/core/tests/ts-integration/tests/evm-contracts.test.ts +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -88,19 +88,6 @@ describe('EVM equivalence contract', () => { const expected_gas = '70598'; // Gas cost when run with solidity interpreter - 3 (We have some changes that are needed) expect(result).toEqual(expected_gas); }); - - xtest("Should compare gas against opcode test fallback contract's call", async () => { - const gasCallerContract = await deploygasCallerContract(alice, artifacts.gasCaller); - - const counterContract = await deploygasCallerContract(alice, artifacts.opcodeTestFallback); - - let result = ( - await gasCallerContract.getFunction('callAndGetGas').staticCall(counterContract.getAddress()) - ).toString(); - - const expected_gas = '34763'; // Gas cost when run with solidity interpreter - expect(result).toEqual(expected_gas); - }); }); describe('Contract creation', () => { @@ -169,12 +156,14 @@ describe('EVM equivalence contract', () => { const args = 1; const factory = getEVMContractFactory(alice, artifacts.counter); - const transaction = await factory.getDeployTransaction(args); - console.log(transaction); - let dep_transaction = ethers.Transaction.from(transaction); - dep_transaction.to = '0x0000000000000000000000000000000000000000'; + const { data, ...rest } = await factory.getDeployTransaction(args); + const dep_transaction = { + ...rest, + to: '0x0000000000000000000000000000000000000000', + chainId: alice.provider._network.chainId, + data + }; console.log(dep_transaction); - const result = await (await alice.sendTransaction(dep_transaction)).wait(); const expectedAddressCreate = ethers.getCreateAddress({ from: alice.address, @@ -183,33 +172,6 @@ describe('EVM equivalence contract', () => { await assertContractNotCreated(deployer, expectedAddressCreate); }); - - // test('Should SENDALL', async () => { - // const salt = ethers.utils.randomBytes(32); - // const selfDestructBytecode = '0x' + artifacts.selfDestruct.evm.bytecode.object; - // const hash = ethers.utils.keccak256(selfDestructBytecode); - - // const selfDestructFactory = getEVMContractFactory(alice, artifacts.selfDestruct); - // const selfDestructAddress = ethers.utils.getCreate2Address(evmCreateTester.address, salt, hash); - // const selfDestruct = selfDestructFactory.attach(selfDestructAddress); - // const beneficiary = testMaster.newEmptyAccount(); - - // await (await evmCreateTester.create2(salt, selfDestructBytecode, { value: 1000 })).wait(); - // expect((await alice.provider.getBalance(selfDestructAddress)).toNumber()).toBe(1000); - - // await (await selfDestruct.destroy(beneficiary.address)).wait(); - // expect((await alice.provider.getBalance(beneficiary.address)).toNumber()).toBe(1000); - - // let failReason; - - // try { - // await (await evmCreateTester.create2(salt, selfDestructBytecode)).wait(); - // } catch (e: any) { - // failReason = e.error.reason; - // } - - // expect(failReason).toBe("execution reverted: Can't create on existing contract address"); - // }); }); }); @@ -238,18 +200,18 @@ describe('EVM equivalence contract', () => { ); expect( - (await proxyCallerContract.getFunction('proxyGet')(counterContract.getAddress())).toString() + (await proxyCallerContract.getFunction('proxyGet')(await counterContract.getAddress())).toString() ).toEqual('1'); - await (await proxyCallerContract.getFunction('executeIncrememt')(counterContract.getAddress(), 1)).wait(); + await (await proxyCallerContract.getFunction('executeIncrememt')(await counterContract.getAddress(), 1)).wait(); expect( - (await proxyCallerContract.getFunction('proxyGet')(counterContract.getAddress())).toString() + (await proxyCallerContract.getFunction('proxyGet')(await counterContract.getAddress())).toString() ).toEqual('2'); expect( ( - await proxyCallerContract.getFunction('proxyGetBytes').staticCall(counterContract.getAddress()) + await proxyCallerContract.getFunction('proxyGetBytes').staticCall(await counterContract.getAddress()) ).toString() ).toEqual('0x54657374696e67'); }); @@ -259,14 +221,6 @@ describe('EVM equivalence contract', () => { const creatorContract = await creatorFactory.deploy(); await creatorContract.deploymentTransaction()?.wait(); - dumpOpcodeLogs( - creatorContract.deploymentTransaction()?.hash ?? - (() => { - throw new Error('Deployment transaction has failed'); - })(), - alice.provider - ); - const nonce = 1; const runtimeBytecode = await creatorContract.getFunction('getCreationRuntimeCode')(); @@ -277,7 +231,6 @@ describe('EVM equivalence contract', () => { }); const result = await (await creatorContract.getFunction('create')()).wait(); - dumpOpcodeLogs(result.transactionHash, alice.provider); await assertCreatedCorrectly(deployer, expectedAddress, runtimeBytecode); }); @@ -289,14 +242,6 @@ describe('EVM equivalence contract', () => { const counterContract = await counterFactory.deploy(args); await counterContract.deploymentTransaction()?.wait(); - dumpOpcodeLogs( - counterContract.deploymentTransaction()?.hash ?? - (() => { - throw new Error('Deployment transaction has failed'); - })(), - alice.provider - ); - let errorString; try { @@ -304,7 +249,6 @@ describe('EVM equivalence contract', () => { } catch (e: any) { errorString = e.reason; } - console.log(errorString); expect(errorString).toEqual('This method always reverts'); }); @@ -323,13 +267,6 @@ describe('EVM equivalence contract', () => { await evmToken.deploymentTransaction()?.wait(); nativeToken = await deployContract(alice, contracts.erc20, []); - dumpOpcodeLogs( - evmToken.deploymentTransaction()?.hash ?? - (() => { - throw new Error('Deployment transaction has failed'); - })(), - alice.provider - ); userAccount = testMaster.newEmptyAccount(); // Only log the first deployment if (logGasCosts && !deployLogged) { @@ -385,7 +322,6 @@ describe('EVM equivalence contract', () => { console.log('ERC20 native transfer gas: ' + nativeTransferTx.gasUsed.toString()); console.log('ERC20 evm transfer gas: ' + evmTransferTx.gasUsed.toString()); } - dumpOpcodeLogs(evmTransferTx.transactionHash, alice.provider); expect((await evmToken.getFunction('balanceOf')(alice.address)).toString()).toEqual('900000'); expect((await evmToken.getFunction('balanceOf')(userAccount.address)).toString()).toEqual('100000'); @@ -403,7 +339,6 @@ describe('EVM equivalence contract', () => { console.log('ERC20 native approve gas: ' + nativeApproveTx.gasUsed.toString()); console.log('ERC20 evm approve gas: ' + evmApproveTx.gasUsed.toString()); } - dumpOpcodeLogs(evmApproveTx.transactionHash, alice.provider); const evmTransferFromTx = await ( await evmToken.connect(userAccount).getFunction('transferFrom')( @@ -423,7 +358,6 @@ describe('EVM equivalence contract', () => { console.log('ERC20 native transferFrom gas: ' + nativeTransferFromTx.gasUsed.toString()); console.log('ERC20 evm transferFrom gas: ' + evmTransferFromTx.gasUsed.toString()); } - dumpOpcodeLogs(evmTransferFromTx.transactionHash, alice.provider); expect((await evmToken.getFunction('balanceOf')(alice.address)).toString()).toEqual('900000'); expect((await evmToken.getFunction('balanceOf')(userAccount.address)).toString()).toEqual('100000'); @@ -472,14 +406,6 @@ describe('EVM equivalence contract', () => { const nativePairReceipt = await ( await nativeUniswapFactory.getFunction('createPair')(evmToken1.getAddress(), evmToken2.getAddress()) ).wait(); - dumpOpcodeLogs( - evmUniswapFactory.deploymentTransaction()?.hash ?? - (() => { - throw new Error('Deployment transaction has failed'); - })(), - alice.provider - ); - dumpOpcodeLogs(evmPairReceipt.transactionHash, alice.provider); const evmUniswapPairFactory = getEVMContractFactory(alice, artifacts.uniswapV2Pair); const nativeUniswapPairFactory = new zksync.ContractFactory( @@ -541,7 +467,6 @@ describe('EVM equivalence contract', () => { test('mint, swap, and burn should work', async () => { const evmMintReceipt = await (await evmUniswapPair.getFunction('mint')(alice.address)).wait(); const nativeMintReceipt = await (await nativeUniswapPair.getFunction('mint')(alice.address)).wait(); - dumpOpcodeLogs(evmMintReceipt.transactionHash, alice.provider); await (await evmToken1.getFunction('transfer')(evmUniswapPair.getAddress(), 10000)).wait(); await (await evmToken1.getFunction('transfer')(nativeUniswapPair.getAddress(), 10000)).wait(); @@ -551,7 +476,6 @@ describe('EVM equivalence contract', () => { const nativeSwapReceipt = await ( await nativeUniswapPair.getFunction('swap')(0, 5000, alice.address, '0x') ).wait(); - dumpOpcodeLogs(evmSwapReceipt.transactionHash, alice.provider); const evmLiquidityTransfer = await ( await evmUniswapPair.getFunction('transfer')( @@ -582,8 +506,6 @@ describe('EVM equivalence contract', () => { console.log('UniswapV2Pair native burn gas: ' + nativeBurnReceipt.gasUsed); console.log('UniswapV2Pair evm burn gas: ' + evmBurnReceipt.gasUsed); } - dumpOpcodeLogs(evmLiquidityTransfer.transactionHash, alice.provider); - dumpOpcodeLogs(evmBurnReceipt.transactionHash, alice.provider); }); test("Should compare gas against uniswap fallback contract's call", async () => { @@ -625,7 +547,6 @@ describe('EVM equivalence contract', () => { // test('should successfully execute bulk opcode test', async () => { // console.log(await deployer.evmCode(opcodeTest.address)) // // const receipt = await (await opcodeTest.execute()).wait() - // // dumpOpcodeLogs(receipt.transactionHash, alice.provider); // }); // }); From 91bf9ca1a05b5d01c0421398472731bd8a8b0b3f Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:54:16 -0300 Subject: [PATCH 07/10] Format code --- core/tests/ts-integration/tests/evm-contracts.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts index 75ed265094b..279fdd87bbb 100644 --- a/core/tests/ts-integration/tests/evm-contracts.test.ts +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -203,7 +203,9 @@ describe('EVM equivalence contract', () => { (await proxyCallerContract.getFunction('proxyGet')(await counterContract.getAddress())).toString() ).toEqual('1'); - await (await proxyCallerContract.getFunction('executeIncrememt')(await counterContract.getAddress(), 1)).wait(); + await ( + await proxyCallerContract.getFunction('executeIncrememt')(await counterContract.getAddress(), 1) + ).wait(); expect( (await proxyCallerContract.getFunction('proxyGet')(await counterContract.getAddress())).toString() @@ -211,7 +213,9 @@ describe('EVM equivalence contract', () => { expect( ( - await proxyCallerContract.getFunction('proxyGetBytes').staticCall(await counterContract.getAddress()) + await proxyCallerContract + .getFunction('proxyGetBytes') + .staticCall(await counterContract.getAddress()) ).toString() ).toEqual('0x54657374696e67'); }); From 43aa7c6e445e4d45adf165835df027f2be8f8450 Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:58:11 -0300 Subject: [PATCH 08/10] Change hashes --- contracts | 2 +- .../tests/evm-contracts.test.ts | 1 - etc/env/base/chain.toml | 6 +++--- etc/env/base/contracts.toml | 4 ++-- .../fee_estimate.yul/fee_estimate.yul.zbin | Bin 76064 -> 76064 bytes .../gas_test.yul/gas_test.yul.zbin | Bin 72160 -> 72160 bytes .../playground_batch.yul.zbin | Bin 76256 -> 76256 bytes .../proved_batch.yul/proved_batch.yul.zbin | Bin 72672 -> 72672 bytes 8 files changed, 6 insertions(+), 7 deletions(-) diff --git a/contracts b/contracts index af2a2c9113f..127ae1af133 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit af2a2c9113f9d6510f45ee2ae08d29d623d03c99 +Subproject commit 127ae1af133bbd1c94c46d81ee47a8457b1062f9 diff --git a/core/tests/ts-integration/tests/evm-contracts.test.ts b/core/tests/ts-integration/tests/evm-contracts.test.ts index 279fdd87bbb..145975b3af0 100644 --- a/core/tests/ts-integration/tests/evm-contracts.test.ts +++ b/core/tests/ts-integration/tests/evm-contracts.test.ts @@ -163,7 +163,6 @@ describe('EVM equivalence contract', () => { chainId: alice.provider._network.chainId, data }; - console.log(dep_transaction); const result = await (await alice.sendTransaction(dep_transaction)).wait(); const expectedAddressCreate = ethers.getCreateAddress({ from: alice.address, diff --git a/etc/env/base/chain.toml b/etc/env/base/chain.toml index 5f1a9a1603d..297fe2838c3 100644 --- a/etc/env/base/chain.toml +++ b/etc/env/base/chain.toml @@ -90,9 +90,9 @@ fee_model_version = "V2" validation_computational_gas_limit = 300000 save_call_traces = true -bootloader_hash = "0x010008df0ef90cb578022657652c9ac6339748e080ea22d185ba9a07e0238a28" -default_aa_hash = "0x0100058deb36e1f2eeb48bf3846d0e8eb38e9176754b73116bb41a472459a4dd" -evm_simulator_hash = "0x01000f197081a9906cc411d0698c4961aeb5c74877f37f7071681da6e8ef3f31" +bootloader_hash = "0x010008df4033788c2a544809ba3d727dcbd9487c8379a9f94e109d245a6390bb" +default_aa_hash = "0x0100058d6621a738ecdb4f523fb46ba9eb04e897c5313c9e17369350a1d664e0" +evm_simulator_hash = "0x01000f19ffd06646d0e66ed6f38d889c329dbabd347b52e3ff93563878a9874d" protective_reads_persistence_enabled = false diff --git a/etc/env/base/contracts.toml b/etc/env/base/contracts.toml index 59200adb3c2..19517e85d4e 100644 --- a/etc/env/base/contracts.toml +++ b/etc/env/base/contracts.toml @@ -26,8 +26,8 @@ RECURSION_NODE_LEVEL_VK_HASH = "0x1186ec268d49f1905f8d9c1e9d39fc33e98c74f91d91a2 RECURSION_LEAF_LEVEL_VK_HASH = "0x101e08b00193e529145ee09823378ef51a3bc8966504064f1f6ba3f1ba863210" RECURSION_CIRCUITS_SET_VKS_HASH = "0x18c1639094f58177409186e8c48d9f577c9410901d2f1d486b3e7d6cf553ae4c" GENESIS_TX_HASH = "0xb99ebfea46cbe05a21cd80fe5597d97b204befc52a16303f579c607dc1ac2e2e" -GENESIS_ROOT = "0xd2b1a071a2da0f92d65e58e7db4743273963c1b8fa6c6a7b565e04128b5441f3" -GENESIS_BATCH_COMMITMENT = "0x7721099ac60105b5ec00fced84f92cb61ec2103ab2248f7d223a705aaddc1da4" +GENESIS_ROOT = "0x6f51a4b123e59b6e06f522eb50fdb67bd0f47f00a3815b3581e85f22abfd14b5" +GENESIS_BATCH_COMMITMENT = "0xa087446fafeafdc8b4b6dc3813f5a313fa41af3327b5086e53d279ed0df3c283" PRIORITY_TX_MAX_GAS_LIMIT = 72000000 DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT = 10000000 GENESIS_ROLLUP_LEAF_INDEX = "56" diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/fee_estimate.yul/fee_estimate.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/fee_estimate.yul/fee_estimate.yul.zbin index a48d89ebe7ff2c5640d3c15271298e7689b7a370..71eb88b4e93b2042650726f2740a3acf6bd2b634 100644 GIT binary patch delta 77 zcmV-T0J8s}(gdK=1c0;whx@KpHVrC>d delta 77 zcmV-T0J8s}(gdK=1c0;w&kd>&gr22~5R&558Fg&IK2&pfc_XYAJj&i9fIC6E9B delta 75 zcmV-R0JQ($vjpI?1hAy{97(H};O30^i=4ScBN-a61{w05&jdN{pS|s$#G}CXA|OLn hMn#^o{1p88QLIJpoqlgOS21hRjFjJRZb_QD>YfS~D763p diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/playground_batch.yul/playground_batch.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/playground_batch.yul/playground_batch.yul.zbin index 60749b415b87a731d7f7a937c28b9fd6b68a1c18..c01f5f8383a616eed1671923456fc9bc6932fef9 100644 GIT binary patch delta 79 zcmV-V0I>hy(*)qt1c0;wLhy(*)qt1c0;wLDDKnt)OxkprdN@ZI&tv)zwn{e*Q)nHhtWF!Ot From 4e1df208dd87b2f1a627aa714d0912b3605dc08d Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:50:25 -0300 Subject: [PATCH 09/10] Modify hashes --- contracts | 2 +- etc/env/base/chain.toml | 4 ++-- etc/env/base/contracts.toml | 4 ++-- .../fee_estimate.yul/fee_estimate.yul.zbin | Bin 76064 -> 76064 bytes .../gas_test.yul/gas_test.yul.zbin | Bin 72160 -> 72160 bytes .../playground_batch.yul.zbin | Bin 76256 -> 76256 bytes .../proved_batch.yul/proved_batch.yul.zbin | Bin 72672 -> 72672 bytes 7 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts b/contracts index 127ae1af133..6a0fe1a1bea 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 127ae1af133bbd1c94c46d81ee47a8457b1062f9 +Subproject commit 6a0fe1a1bea2eacf798c8fffb19729046d1ce7b8 diff --git a/etc/env/base/chain.toml b/etc/env/base/chain.toml index 297fe2838c3..aca6f4f7f68 100644 --- a/etc/env/base/chain.toml +++ b/etc/env/base/chain.toml @@ -90,8 +90,8 @@ fee_model_version = "V2" validation_computational_gas_limit = 300000 save_call_traces = true -bootloader_hash = "0x010008df4033788c2a544809ba3d727dcbd9487c8379a9f94e109d245a6390bb" -default_aa_hash = "0x0100058d6621a738ecdb4f523fb46ba9eb04e897c5313c9e17369350a1d664e0" +bootloader_hash = "0x010008dfcd3aa2beca1745f437e42b55a1657712bc063e2d52aeb2dee138c399" +default_aa_hash = "0x0100058d8dede04786d92d2dd62bbbf8b696f62c5db1d4b2601ac6276f5dcad9" evm_simulator_hash = "0x01000f19ffd06646d0e66ed6f38d889c329dbabd347b52e3ff93563878a9874d" protective_reads_persistence_enabled = false diff --git a/etc/env/base/contracts.toml b/etc/env/base/contracts.toml index 19517e85d4e..7e7b5cf3ca5 100644 --- a/etc/env/base/contracts.toml +++ b/etc/env/base/contracts.toml @@ -26,8 +26,8 @@ RECURSION_NODE_LEVEL_VK_HASH = "0x1186ec268d49f1905f8d9c1e9d39fc33e98c74f91d91a2 RECURSION_LEAF_LEVEL_VK_HASH = "0x101e08b00193e529145ee09823378ef51a3bc8966504064f1f6ba3f1ba863210" RECURSION_CIRCUITS_SET_VKS_HASH = "0x18c1639094f58177409186e8c48d9f577c9410901d2f1d486b3e7d6cf553ae4c" GENESIS_TX_HASH = "0xb99ebfea46cbe05a21cd80fe5597d97b204befc52a16303f579c607dc1ac2e2e" -GENESIS_ROOT = "0x6f51a4b123e59b6e06f522eb50fdb67bd0f47f00a3815b3581e85f22abfd14b5" -GENESIS_BATCH_COMMITMENT = "0xa087446fafeafdc8b4b6dc3813f5a313fa41af3327b5086e53d279ed0df3c283" +GENESIS_ROOT = "0x45f206478fac785d68ae8217999e8caf66c4e67fea1db90139422e5c4eb5cc51" +GENESIS_BATCH_COMMITMENT = "0x9e598e2b5784e62dec66ce37d92bfa4407d7441fb77c003d09856221e286eb95" PRIORITY_TX_MAX_GAS_LIMIT = 72000000 DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT = 10000000 GENESIS_ROLLUP_LEAF_INDEX = "56" diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/fee_estimate.yul/fee_estimate.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/fee_estimate.yul/fee_estimate.yul.zbin index 71eb88b4e93b2042650726f2740a3acf6bd2b634..6f86e6f1ae0b37cb9e2749be246f52ad066e3b16 100644 GIT binary patch delta 77 zcmV-T0J8s}(gdK=1c0;whx@KpHVrC>d diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/gas_test.yul/gas_test.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/gas_test.yul/gas_test.yul.zbin index 86f867d37b38895bad3ea6bee34db0e5f425a1e3..57a8573e08eb9df117f50b22c2999d8f6c974e83 100644 GIT binary patch delta 75 zcmV-R0JQ($vjpI?1hAy{9G-endWvRY5Ufb1}MHbZul>cf^G`J=%1A|Uwb h%vV&i6JGsZ<($r8!ed6ykK(xy+@gV4kx;!xRP87~DYyUt delta 75 zcmV-R0JQ($vjpI?1hAy{9Bo%-0^HFT#FmF$-9lePl%QgiD_!~A{jxLzf}_CqA|RA} hyi>&kd>&gr22~5R&558Fg&IK2&pfc_XYAJj&i9fIC6E9B diff --git a/etc/multivm_bootloaders/vm_1_5_0_increased_memory/playground_batch.yul/playground_batch.yul.zbin b/etc/multivm_bootloaders/vm_1_5_0_increased_memory/playground_batch.yul/playground_batch.yul.zbin index c01f5f8383a616eed1671923456fc9bc6932fef9..e8ca1c721a3006074301b1c538fcb73a54e14b66 100644 GIT binary patch delta 79 zcmV-V0I>hy(*)qt1c0;wL;{3{%Uq_v-T#C)WS~ delta 79 zcmV-V0I>hy(*)qt1c0;wLendWvRY5Ufb1}MHbZul>cf^G`LjUzfFdA+ i%QMlx7RR!(V`i4DHlx Date: Tue, 20 Aug 2024 12:54:34 -0300 Subject: [PATCH 10/10] Add newlines --- core/tests/ts-integration/contracts/token/ERC20.sol | 2 +- core/tests/ts-integration/evm-contracts/CounterWithParam.sol | 2 +- core/tests/ts-integration/evm-contracts/ProxyCaller.sol | 2 +- core/tests/ts-integration/evm-contracts/SelfDestruct.sol | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/tests/ts-integration/contracts/token/ERC20.sol b/core/tests/ts-integration/contracts/token/ERC20.sol index ca3269c4ebd..514e2358624 100644 --- a/core/tests/ts-integration/contracts/token/ERC20.sol +++ b/core/tests/ts-integration/contracts/token/ERC20.sol @@ -73,4 +73,4 @@ contract ERC20{ c = a / b; } -} \ No newline at end of file +} diff --git a/core/tests/ts-integration/evm-contracts/CounterWithParam.sol b/core/tests/ts-integration/evm-contracts/CounterWithParam.sol index 40211a74654..1261e2f9fa0 100644 --- a/core/tests/ts-integration/evm-contracts/CounterWithParam.sol +++ b/core/tests/ts-integration/evm-contracts/CounterWithParam.sol @@ -37,4 +37,4 @@ contract CounterWithParam { function getBytes() public returns (bytes memory) { return "Testing"; } -} \ No newline at end of file +} diff --git a/core/tests/ts-integration/evm-contracts/ProxyCaller.sol b/core/tests/ts-integration/evm-contracts/ProxyCaller.sol index 05200d06a31..926d2a944d4 100644 --- a/core/tests/ts-integration/evm-contracts/ProxyCaller.sol +++ b/core/tests/ts-integration/evm-contracts/ProxyCaller.sol @@ -22,4 +22,4 @@ contract ProxyCaller { function proxyGetBytes(address dest) external returns(bytes memory returnData){ return ICounterWithParam(dest).getBytes(); } -} \ No newline at end of file +} diff --git a/core/tests/ts-integration/evm-contracts/SelfDestruct.sol b/core/tests/ts-integration/evm-contracts/SelfDestruct.sol index 3e3923de336..b28a25968e3 100644 --- a/core/tests/ts-integration/evm-contracts/SelfDestruct.sol +++ b/core/tests/ts-integration/evm-contracts/SelfDestruct.sol @@ -12,4 +12,4 @@ contract SelfDestruct { selfdestruct(recipient) } } -} \ No newline at end of file +}