diff --git a/fvm/evm/emulator/emulator_test.go b/fvm/evm/emulator/emulator_test.go index 1698ce2c1ea..a9a6992c29e 100644 --- a/fvm/evm/emulator/emulator_test.go +++ b/fvm/evm/emulator/emulator_test.go @@ -20,6 +20,7 @@ import ( "github.com/onflow/flow-go/fvm/evm/debug" "github.com/onflow/flow-go/fvm/evm/emulator" "github.com/onflow/flow-go/fvm/evm/testutils" + "github.com/onflow/flow-go/fvm/evm/testutils/contracts" "github.com/onflow/flow-go/fvm/evm/types" "github.com/onflow/flow-go/model/flow" @@ -746,6 +747,266 @@ func TestSelfdestruct(t *testing.T) { }) } +// test factory patterns +func TestFactoryPatterns(t *testing.T) { + testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { + testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { + + var factoryAddress types.Address + factoryContract := testutils.GetFactoryTestContract(t) + factoryDeployer := types.NewAddressFromString("factoryDeployer") + factoryDeployerBalance := big.NewInt(0).Mul(big.NewInt(1000), big.NewInt(gethParams.Ether)) + factoryBalance := big.NewInt(0).Mul(big.NewInt(100), big.NewInt(gethParams.Ether)) + + // setup the test with funded account and deploying a factory contract. + RunWithNewEmulator(t, backend, rootAddr, func(env *emulator.Emulator) { + t.Run("test deploying factory", func(t *testing.T) { + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall(types.NewDepositCall(types.EmptyAddress, factoryDeployer, factoryDeployerBalance, 0)) + require.NoError(t, err) + requireSuccessfulExecution(t, err, res) + }) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewDeployCall( + factoryDeployer, + factoryContract.ByteCode, + math.MaxUint64, + factoryBalance, + 0), + ) + requireSuccessfulExecution(t, err, res) + require.NotNil(t, res.DeployedContractAddress) + factoryAddress = *res.DeployedContractAddress + }) + }) + + t.Run("test self-destruct to a contract that is already deployed", + func(t *testing.T) { + // first test call deploy and try self destruct later + var deployed types.Address + RunWithNewBlockView(t, env, func(blk types.BlockView) { + salt := [32]byte{1} + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + factoryAddress, + factoryContract.MakeCallData(t, "deploy", salt), + 250_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + + // decode address, data is left padded + deployed = types.Address(gethCommon.BytesToAddress(res.ReturnedData[12:])) + }) + + // deposit money into the contract + depositedBalance := big.NewInt(200) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall(types.NewDepositCall( + types.EmptyAddress, + deployed, + depositedBalance, 1)) + require.NoError(t, err) + requireSuccessfulExecution(t, err, res) + }) + // check balance of contract + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + bal, err := blk.BalanceOf(deployed) + require.NoError(t, err) + require.Equal(t, depositedBalance, bal) + }) + + // set storage on deployed contract + storedValue := big.NewInt(12) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + types.Address(deployed), + testutils.MakeCallData(t, + contracts.FactoryDeployableContractABIJSON, + "set", + storedValue), + 120_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + }) + + // call self-destruct on the deployed + refundAddress := testutils.RandomAddress(t) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + types.Address(deployed), + testutils.MakeCallData(t, + contracts.FactoryDeployableContractABIJSON, + "destroy", + refundAddress), + 120_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + }) + + // check balance of the refund address and the contract + // balance should be transferred to the refund address + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + bal, err := blk.BalanceOf(refundAddress) + require.NoError(t, err) + require.Equal(t, depositedBalance, bal) + + bal, err = blk.BalanceOf(deployed) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(big.NewInt(0), bal)) + }) + + // data should still be there + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + types.Address(deployed), + testutils.MakeCallData(t, + contracts.FactoryDeployableContractABIJSON, + "get"), + 120_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + require.Equal(t, storedValue, new(big.Int).SetBytes(res.ReturnedData)) + }) + }) + + t.Run("test deploy and destroy in a single call", + func(t *testing.T) { + var originalFactoryBalance types.Balance + var err error + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + originalFactoryBalance, err = blk.BalanceOf(factoryAddress) + require.NoError(t, err) + }) + + storedValue := big.NewInt(100) + RunWithNewBlockView(t, env, func(blk types.BlockView) { + salt := [32]byte{2} + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + factoryAddress, + factoryContract.MakeCallData(t, + "deployAndDestroy", + salt, + storedValue), + 400_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + }) + + // no balance change on the caller + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + ret, err := blk.BalanceOf(factoryAddress) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(originalFactoryBalance, ret)) + }) + }) + t.Run("test deposit first to an address and then deploy in a single call", + func(t *testing.T) { + storedValue := big.NewInt(120) + balance := big.NewInt(80) + var deployed types.Address + RunWithNewBlockView(t, env, func(blk types.BlockView) { + salt := [32]byte{3} + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + factoryAddress, + factoryContract.MakeCallData(t, "depositAndDeploy", salt, balance, storedValue), + 250_000, + big.NewInt(0), + 1, + ), + ) + requireSuccessfulExecution(t, err, res) + // decode address, data is left padded + deployed = types.Address(gethCommon.BytesToAddress(res.ReturnedData[12:])) + }) + + // no balance change on the caller + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + ret, err := blk.BalanceOf(deployed) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(balance, ret)) + }) + + // check stored data + RunWithNewBlockView(t, env, func(blk types.BlockView) { + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + types.Address(deployed), + testutils.MakeCallData(t, + contracts.FactoryDeployableContractABIJSON, + "get"), + 120_000, + big.NewInt(0), + 0, + ), + ) + requireSuccessfulExecution(t, err, res) + require.Equal(t, storedValue, new(big.Int).SetBytes(res.ReturnedData)) + }) + }) + + t.Run("test deposit, deploy, destroy in a single call", + func(t *testing.T) { + var originalFactoryBalance types.Balance + var err error + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + originalFactoryBalance, err = blk.BalanceOf(factoryAddress) + require.NoError(t, err) + }) + + RunWithNewBlockView(t, env, func(blk types.BlockView) { + salt := [32]byte{4} + res, err := blk.DirectCall( + types.NewContractCall( + factoryDeployer, + factoryAddress, + factoryContract.MakeCallData(t, "depositDeployAndDestroy", salt, big.NewInt(100), big.NewInt(10)), + 250_000, + big.NewInt(0), + 1, + ), + ) + requireSuccessfulExecution(t, err, res) + }) + // no balance change on the caller + RunWithNewReadOnlyBlockView(t, env, func(blk types.ReadOnlyBlockView) { + ret, err := blk.BalanceOf(factoryAddress) + require.NoError(t, err) + require.True(t, types.BalancesAreEqual(originalFactoryBalance, ret)) + }) + }) + }) + }) + }) +} + func TestTransfers(t *testing.T) { testutils.RunWithTestBackend(t, func(backend *testutils.TestBackend) { testutils.RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) { diff --git a/fvm/evm/emulator/state/base.go b/fvm/evm/emulator/state/base.go index d66483c86d5..6adc37bae8e 100644 --- a/fvm/evm/emulator/state/base.go +++ b/fvm/evm/emulator/state/base.go @@ -187,7 +187,7 @@ func (v *BaseView) GetStorageRoot(addr common.Address) (common.Hash, error) { } // account is EOA - if account.CollectionID == nil { + if len(account.CollectionID) == 0 { return gethTypes.EmptyRootHash, nil } diff --git a/fvm/evm/emulator/state/delta.go b/fvm/evm/emulator/state/delta.go index ed1750fdbf1..ef26f60dbfa 100644 --- a/fvm/evm/emulator/state/delta.go +++ b/fvm/evm/emulator/state/delta.go @@ -104,6 +104,11 @@ func (d *DeltaView) Exist(addr gethCommon.Address) (bool, error) { if found { return true, nil } + // if is address is dirty it exists + _, found = d.dirtyAddresses[addr] + if found { + return true, nil + } return d.parent.Exist(addr) } @@ -407,6 +412,20 @@ func (d *DeltaView) SetState(sk types.SlotAddress, value gethCommon.Hash) error // the way back to the base view. This means that the state root that is returned // ignores the updates to slots during the transaction. func (d *DeltaView) GetStorageRoot(addr gethCommon.Address) (gethCommon.Hash, error) { + exist, err := d.Exist(addr) + if err != nil { + return gethCommon.Hash{}, err + } + if exist { + code, err := d.GetCode(addr) + if err != nil { + return gethCommon.Hash{}, err + } + if len(code) == 0 { + return gethTypes.EmptyRootHash, nil + } + // else go back to the parent + } // go back to parents (until we reach the base view) // Note that if storage is updated in deltas but not // committed the expected behavior is to return the root in the base view. diff --git a/fvm/evm/emulator/state/stateDB.go b/fvm/evm/emulator/state/stateDB.go index 5544bf8a79a..7dad40cc0d6 100644 --- a/fvm/evm/emulator/state/stateDB.go +++ b/fvm/evm/emulator/state/stateDB.go @@ -462,7 +462,7 @@ func (db *StateDB) Finalize() error { // Prepare is a high level logic that sadly is considered to be part of the // stateDB interface and not on the layers above. -// based on parameters that are passed it updates access lists +// based on parameters that are passed it updates access-lists func (db *StateDB) Prepare(rules gethParams.Rules, sender, coinbase gethCommon.Address, dest *gethCommon.Address, precompiles []gethCommon.Address, txAccesses gethTypes.AccessList) { if rules.IsBerlin { db.AddAddressToAccessList(sender) diff --git a/fvm/evm/emulator/state/stateDB_test.go b/fvm/evm/emulator/state/stateDB_test.go index e1879aab508..ef76234ee8a 100644 --- a/fvm/evm/emulator/state/stateDB_test.go +++ b/fvm/evm/emulator/state/stateDB_test.go @@ -328,6 +328,15 @@ func TestStateDB(t *testing.T) { require.NoError(t, db.Error()) require.Equal(t, gethTypes.EmptyRootHash, root) + db.AddBalance(addr1, uint256.NewInt(100), tracing.BalanceChangeTouchAccount) + require.NoError(t, db.Error()) + err = db.Commit(true) + require.NoError(t, err) + + root = db.GetStorageRoot(addr1) + require.NoError(t, db.Error()) + require.Equal(t, gethTypes.EmptyRootHash, root) + // add slots to the account key := testutils.RandomCommonHash(t) value := testutils.RandomCommonHash(t) diff --git a/fvm/evm/testutils/contract.go b/fvm/evm/testutils/contract.go index 1c084a4262d..e122904f796 100644 --- a/fvm/evm/testutils/contract.go +++ b/fvm/evm/testutils/contract.go @@ -52,6 +52,13 @@ func GetDummyKittyTestContract(t testing.TB) *TestContract { } } +func GetFactoryTestContract(t testing.TB) *TestContract { + return &TestContract{ + ABI: contracts.FactoryContractABIJSON, + ByteCode: contracts.FactoryContractBytes, + } +} + func RunWithDeployedContract(t testing.TB, tc *TestContract, led atree.Ledger, flowEVMRootAddress flow.Address, f func(*TestContract)) { DeployContract(t, RandomAddress(t), tc, led, flowEVMRootAddress) f(tc) diff --git a/fvm/evm/testutils/contracts/contracts.go b/fvm/evm/testutils/contracts/contracts.go index adce36b692e..83c615f3296 100644 --- a/fvm/evm/testutils/contracts/contracts.go +++ b/fvm/evm/testutils/contracts/contracts.go @@ -20,3 +20,14 @@ var DummyKittyContractBytes, _ = hex.DecodeString(dummyKittyContractBytesInHex) //go:embed dummy_kitty_abi.json var DummyKittyContractABIJSON string + +//go:embed factory_bytes.hex +var factoryContractBytesInHex string + +var FactoryContractBytes, _ = hex.DecodeString(factoryContractBytesInHex) + +//go:embed factory_abi.json +var FactoryContractABIJSON string + +//go:embed factory_deployable_abi.json +var FactoryDeployableContractABIJSON string diff --git a/fvm/evm/testutils/contracts/factory.sol b/fvm/evm/testutils/contracts/factory.sol new file mode 100644 index 00000000000..39ffea4e7be --- /dev/null +++ b/fvm/evm/testutils/contracts/factory.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: UNLICENSED + +pragma solidity >=0.7.0 <0.9.0; + +contract Factory { + constructor() payable {} + + function deploy(bytes32 salt) public returns (address) { + new Deployable{salt: salt}(); + return _getCreate2Address(salt, keccak256(abi.encodePacked(type(Deployable).creationCode))); + } + + function deployAndDestroy(bytes32 salt, uint256 value) public { + Deployable dep = new Deployable{salt: salt}(); + dep.set(value); + dep.destroy(address(this)); + } + + function depositAndDeploy(bytes32 salt, uint256 amount, uint256 stored) public returns (address) { + address addr = _getCreate2Address(salt, keccak256(abi.encodePacked(type(Deployable).creationCode))); + bool success; + assembly { + success := call(gas(), addr, amount, 0, 0, 0, 0) + } + require(success); + Deployable dep = new Deployable{salt: salt}(); + dep.set(stored); + return _getCreate2Address(salt, keccak256(abi.encodePacked(type(Deployable).creationCode))); + } + + function depositDeployAndDestroy(bytes32 salt, uint256 amount, uint256 stored) public { + address addr = _getCreate2Address(salt, keccak256(abi.encodePacked(type(Deployable).creationCode))); + bool success; + assembly { + success := call(gas(), addr, amount, 0, 0, 0, 0) + } + require(success); + Deployable dep = new Deployable{salt: salt}(); + dep.set(stored); + dep.destroy(address(this)); + } + + function _getCreate2Address(bytes32 salt, bytes32 codeHash) internal view returns (address) { + return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, codeHash))))); + } +} + +contract Deployable { + uint256 number; + constructor() payable {} + function set(uint256 num) public { + number = num; + } + function get() public view returns (uint256){ + return number; + } + function destroy(address etherDestination) external { + selfdestruct(payable(etherDestination)); + } + + receive() external payable { + } +} \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/factory_abi.json b/fvm/evm/testutils/contracts/factory_abi.json new file mode 100644 index 00000000000..924407ab38a --- /dev/null +++ b/fvm/evm/testutils/contracts/factory_abi.json @@ -0,0 +1,96 @@ +[ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + } + ], + "name": "deploy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "deployAndDestroy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stored", + "type": "uint256" + } + ], + "name": "depositAndDeploy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "stored", + "type": "uint256" + } + ], + "name": "depositDeployAndDestroy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/factory_bytes.hex b/fvm/evm/testutils/contracts/factory_bytes.hex new file mode 100644 index 00000000000..ebe208d2232 --- /dev/null +++ b/fvm/evm/testutils/contracts/factory_bytes.hex @@ -0,0 +1 @@ +6080604052610af6806100115f395ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c80632b85ba381461004e578063624ff5c41461007e5780639e3bb99e1461009a578063cf4fe092146100b6575b5f80fd5b610068600480360381019061006391906105b4565b6100e6565b604051610075919061061e565b60405180910390f35b6100986004803603810190610093919061066a565b61016d565b005b6100b460048036038101906100af91906106ba565b6102d8565b005b6100d060048036038101906100cb919061066a565b6103d6565b6040516100dd919061061e565b60405180910390f35b5f816040516100f490610570565b8190604051809103905ff5905080158015610111573d5f803e3d5ffd5b5050610166826040518060200161012790610570565b6020820181038252601f19601f8201166040525060405160200161014b919061074a565b60405160208183030381529060405280519060200120610534565b9050919050565b5f6101c1846040518060200161018290610570565b6020820181038252601f19601f820116604052506040516020016101a6919061074a565b60405160208183030381529060405280519060200120610534565b90505f805f805f87865af19050806101d7575f80fd5b5f856040516101e590610570565b8190604051809103905ff5905080158015610202573d5f803e3d5ffd5b5090508073ffffffffffffffffffffffffffffffffffffffff166360fe47b1856040518263ffffffff1660e01b815260040161023e919061076f565b5f604051808303815f87803b158015610255575f80fd5b505af1158015610267573d5f803e3d5ffd5b505050508073ffffffffffffffffffffffffffffffffffffffff1662f55d9d306040518263ffffffff1660e01b81526004016102a3919061061e565b5f604051808303815f87803b1580156102ba575f80fd5b505af11580156102cc573d5f803e3d5ffd5b50505050505050505050565b5f826040516102e690610570565b8190604051809103905ff5905080158015610303573d5f803e3d5ffd5b5090508073ffffffffffffffffffffffffffffffffffffffff166360fe47b1836040518263ffffffff1660e01b815260040161033f919061076f565b5f604051808303815f87803b158015610356575f80fd5b505af1158015610368573d5f803e3d5ffd5b505050508073ffffffffffffffffffffffffffffffffffffffff1662f55d9d306040518263ffffffff1660e01b81526004016103a4919061061e565b5f604051808303815f87803b1580156103bb575f80fd5b505af11580156103cd573d5f803e3d5ffd5b50505050505050565b5f8061042b85604051806020016103ec90610570565b6020820181038252601f19601f82011660405250604051602001610410919061074a565b60405160208183030381529060405280519060200120610534565b90505f805f805f88865af1905080610441575f80fd5b5f8660405161044f90610570565b8190604051809103905ff590508015801561046c573d5f803e3d5ffd5b5090508073ffffffffffffffffffffffffffffffffffffffff166360fe47b1866040518263ffffffff1660e01b81526004016104a8919061076f565b5f604051808303815f87803b1580156104bf575f80fd5b505af11580156104d1573d5f803e3d5ffd5b5050505061052887604051806020016104e990610570565b6020820181038252601f19601f8201166040525060405160200161050d919061074a565b60405160208183030381529060405280519060200120610534565b93505050509392505050565b5f60ff60f81b3084846040516020016105509493929190610838565b604051602081830303815290604052805190602001205f1c905092915050565b61023b8061088683390190565b5f80fd5b5f819050919050565b61059381610581565b811461059d575f80fd5b50565b5f813590506105ae8161058a565b92915050565b5f602082840312156105c9576105c861057d565b5b5f6105d6848285016105a0565b91505092915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610608826105df565b9050919050565b610618816105fe565b82525050565b5f6020820190506106315f83018461060f565b92915050565b5f819050919050565b61064981610637565b8114610653575f80fd5b50565b5f8135905061066481610640565b92915050565b5f805f606084860312156106815761068061057d565b5b5f61068e868287016105a0565b935050602061069f86828701610656565b92505060406106b086828701610656565b9150509250925092565b5f80604083850312156106d0576106cf61057d565b5b5f6106dd858286016105a0565b92505060206106ee85828601610656565b9150509250929050565b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f610724826106f8565b61072e8185610702565b935061073e81856020860161070c565b80840191505092915050565b5f610755828461071a565b915081905092915050565b61076981610637565b82525050565b5f6020820190506107825f830184610760565b92915050565b5f7fff0000000000000000000000000000000000000000000000000000000000000082169050919050565b5f819050919050565b6107cd6107c882610788565b6107b3565b82525050565b5f8160601b9050919050565b5f6107e9826107d3565b9050919050565b5f6107fa826107df565b9050919050565b61081261080d826105fe565b6107f0565b82525050565b5f819050919050565b61083261082d82610581565b610818565b82525050565b5f61084382876107bc565b6001820191506108538286610801565b6014820191506108638285610821565b6020820191506108738284610821565b6020820191508190509594505050505056fe608060405261022a806100115f395ff3fe608060405260043610610036575f3560e01c8062f55d9d1461004157806360fe47b1146100695780636d4ce63c146100915761003d565b3661003d57005b5f80fd5b34801561004c575f80fd5b5061006760048036038101906100629190610143565b6100bb565b005b348015610074575f80fd5b5061008f600480360381019061008a91906101a1565b6100d4565b005b34801561009c575f80fd5b506100a56100dd565b6040516100b291906101db565b60405180910390f35b8073ffffffffffffffffffffffffffffffffffffffff16ff5b805f8190555050565b5f8054905090565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610112826100e9565b9050919050565b61012281610108565b811461012c575f80fd5b50565b5f8135905061013d81610119565b92915050565b5f60208284031215610158576101576100e5565b5b5f6101658482850161012f565b91505092915050565b5f819050919050565b6101808161016e565b811461018a575f80fd5b50565b5f8135905061019b81610177565b92915050565b5f602082840312156101b6576101b56100e5565b5b5f6101c38482850161018d565b91505092915050565b6101d58161016e565b82525050565b5f6020820190506101ee5f8301846101cc565b9291505056fea2646970667358221220cbb07f58eed2fb91caf5b496fe4f528d45cf25085898c4f2735c5dcf5bd2fc9f64736f6c634300081a0033a2646970667358221220d1e0a588152bd25c8a5276df79dde8e504c854e590f1dbb00ec9808aee242b4164736f6c634300081a0033 \ No newline at end of file diff --git a/fvm/evm/testutils/contracts/factory_deployable_abi.json b/fvm/evm/testutils/contracts/factory_deployable_abi.json new file mode 100644 index 00000000000..f33f2acc2fa --- /dev/null +++ b/fvm/evm/testutils/contracts/factory_deployable_abi.json @@ -0,0 +1,50 @@ +[ + { + "inputs": [], + "stateMutability": "payable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "etherDestination", + "type": "address" + } + ], + "name": "destroy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "get", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "num", + "type": "uint256" + } + ], + "name": "set", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file