diff --git a/l2os/bindings/l2oo/l2_output_oracle.go b/l2os/bindings/l2oo/l2_output_oracle.go index 932bb9f5..d2d9386b 100644 --- a/l2os/bindings/l2oo/l2_output_oracle.go +++ b/l2os/bindings/l2oo/l2_output_oracle.go @@ -28,10 +28,16 @@ var ( _ = event.NewSubscription ) +// L2OutputOracleOutputProposal is an auto generated low-level Go binding around an user-defined struct. +type L2OutputOracleOutputProposal struct { + OutputRoot [32]byte + Timestamp *big.Int +} + // L2OutputOracleMetaData contains all meta data concerning the L2OutputOracle contract. var L2OutputOracleMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_submissionInterval\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_l2BlockTime\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_genesisL2Output\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_historicalTotalBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_startingBlockTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"_l2Output\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_l2timestamp\",\"type\":\"uint256\"}],\"name\":\"l2OutputAppended\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"_l2Output\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_l2timestamp\",\"type\":\"uint256\"}],\"name\":\"l2OutputDeleted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"HISTORICAL_TOTAL_BLOCKS\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"L2_BLOCK_TIME\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"STARTING_BLOCK_TIMESTAMP\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SUBMISSION_INTERVAL\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_l2Output\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_l2timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_l1Blockhash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_l1Blocknumber\",\"type\":\"uint256\"}],\"name\":\"appendL2Output\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l2timestamp\",\"type\":\"uint256\"}],\"name\":\"computeL2BlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_l2Output\",\"type\":\"bytes32\"}],\"name\":\"deleteL2Output\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l2Timestamp\",\"type\":\"uint256\"}],\"name\":\"getL2Output\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestBlockTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x61010060405234801561001157600080fd5b50604051610e3d380380610e3d83398101604081905261003091610148565b610039336100f8565b61004385876101ac565b156100ba5760405162461bcd60e51b815260206004820152603760248201527f5375626d697373696f6e20496e74657276616c206d7573742062652061206d7560448201527f6c7469706c65206f66204c3220426c6f636b2054696d65000000000000000000606482015260840160405180910390fd5b608086905260a0859052600082815260026020526040902084905560c0839052600182905560e08290526100ed816100f8565b5050505050506101ce565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060008060008060c0878903121561016157600080fd5b86516020880151604089015160608a015160808b015160a08c0151949a50929850909650945092506001600160a01b038116811461019e57600080fd5b809150509295509295509295565b6000826101c957634e487b7160e01b600052601260045260246000fd5b500690565b60805160a05160c05160e051610c0d610230600039600081816101af015281816102d401526103aa01526000818161028e01526103db01526000818160e801526103890152600081816101e30152818161085801526108880152610c0d6000f3fe6080604052600436106100d15760003560e01c806341840fa61161007f5780638da5cb5b116100595780638da5cb5b1461021a578063a25ae5571461024f578063a4771aad1461027c578063f2fde38b146102b057600080fd5b806341840fa61461019d578063529933df146101d1578063715018a61461020557600080fd5b806325188104116100b057806325188104146101535780632b5d15d914610168578063357e951f1461018857600080fd5b80622134cc146100d657806302e513451461011d5780630c1952d31461013d575b600080fd5b3480156100e257600080fd5b5061010a7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b34801561012957600080fd5b5061010a610138366004610aeb565b6102d0565b34801561014957600080fd5b5061010a60015481565b610166610161366004610b04565b610405565b005b34801561017457600080fd5b50610166610183366004610aeb565b6106ee565b34801561019457600080fd5b5061010a610884565b3480156101a957600080fd5b5061010a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156101dd57600080fd5b5061010a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561021157600080fd5b506101666108b9565b34801561022657600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610114565b34801561025b57600080fd5b5061010a61026a366004610aeb565b60009081526002602052604090205490565b34801561028857600080fd5b5061010a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102bc57600080fd5b506101666102cb366004610b36565b610946565b60007f0000000000000000000000000000000000000000000000000000000000000000821015610387576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f54696d657374616d70207072696f7220746f207374617274696e67426c6f636b60448201527f54696d657374616d70000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008303816103d8576103d8610b73565b047f0000000000000000000000000000000000000000000000000000000000000000600101019050919050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610486576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161037e565b428310610515576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f43616e6e6f7420617070656e64204c32206f757470757420696e20667574757260448201527f6500000000000000000000000000000000000000000000000000000000000000606482015260840161037e565b61051d610884565b83146105ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f54696d657374616d70206e6f7420657175616c20746f206e657874206578706560448201527f637465642074696d657374616d70000000000000000000000000000000000000606482015260840161037e565b83610612576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f74207375626d697420656d707479204c32206f7574707574000000604482015260640161037e565b81156106a857818140146106a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f426c6f636b6861736820646f6573206e6f74206d61746368207468652068617360448201527f6820617420746865206578706563746564206865696768742e00000000000000606482015260840161037e565b600083815260026020526040808220869055600185905551849186917f92701dc658a5d84c16077ea6de344b9995e21a96a05d45e4cd22f37a3d266f8b9190a350505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461076f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161037e565b600154600090815260026020526040902054818114610810576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f43616e206f6e6c792064656c65746520746865206d6f737420726563656e742060448201527f6f75747075742e00000000000000000000000000000000000000000000000000606482015260840161037e565b60015460405182907f36160157c233b38fe28ace444408a0db0f7b3d02e652bb27ea9604cbd2a3f26690600090a3600180546000908152600260205260408120555461087d907f000000000000000000000000000000000000000000000000000000000000000090610bd1565b6001555050565b60007f00000000000000000000000000000000000000000000000000000000000000006001546108b49190610be8565b905090565b60005473ffffffffffffffffffffffffffffffffffffffff16331461093a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161037e565b6109446000610a76565b565b60005473ffffffffffffffffffffffffffffffffffffffff1633146109c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161037e565b73ffffffffffffffffffffffffffffffffffffffff8116610a6a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161037e565b610a7381610a76565b50565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060208284031215610afd57600080fd5b5035919050565b60008060008060808587031215610b1a57600080fd5b5050823594602084013594506040840135936060013592509050565b600060208284031215610b4857600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610b6c57600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015610be357610be3610ba2565b500390565b60008219821115610bfb57610bfb610ba2565b50019056fea164736f6c634300080a000a", + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_submissionInterval\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_l2BlockTime\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_genesisL2Output\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_historicalTotalBlocks\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_startingBlockTimestamp\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sequencer\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"_l2Output\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_l1Timestamp\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_l2timestamp\",\"type\":\"uint256\"}],\"name\":\"l2OutputAppended\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"_l2Output\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_l1Timestamp\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"_l2timestamp\",\"type\":\"uint256\"}],\"name\":\"l2OutputDeleted\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"HISTORICAL_TOTAL_BLOCKS\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"L2_BLOCK_TIME\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"STARTING_BLOCK_TIMESTAMP\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SUBMISSION_INTERVAL\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_l2Output\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_l2timestamp\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_l1Blockhash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"_l1Blocknumber\",\"type\":\"uint256\"}],\"name\":\"appendL2Output\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l2timestamp\",\"type\":\"uint256\"}],\"name\":\"computeL2BlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"outputRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"internalType\":\"structL2OutputOracle.OutputProposal\",\"name\":\"_proposal\",\"type\":\"tuple\"}],\"name\":\"deleteL2Output\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l2Timestamp\",\"type\":\"uint256\"}],\"name\":\"getL2Output\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"outputRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"internalType\":\"structL2OutputOracle.OutputProposal\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"latestBlockTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"nextTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x61010060405234801561001157600080fd5b50604051610fa1380380610fa183398101604081905261003091610164565b61003933610114565b61004385876101c8565b156100ba5760405162461bcd60e51b815260206004820152603760248201527f5375626d697373696f6e20496e74657276616c206d7573742062652061206d7560448201527f6c7469706c65206f66204c3220426c6f636b2054696d65000000000000000000606482015260840160405180910390fd5b608086905260a0859052604080518082018252858152426020808301918252600086815260029091529290922090518155905160019182015560c084905282905560e082905261010981610114565b5050505050506101ea565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060008060008060c0878903121561017d57600080fd5b86516020880151604089015160608a015160808b015160a08c0151949a50929850909650945092506001600160a01b03811681146101ba57600080fd5b809150509295509295509295565b6000826101e557634e487b7160e01b600052601260045260246000fd5b500690565b60805160a05160c05160e051610d5561024c600039600081816101af0152818161031b01526103f10152600081816102d5015261042201526000818160e801526103d00152600081816101e301528181610625015261095a0152610d556000f3fe6080604052600436106100d15760003560e01c806341840fa61161007f5780638da5cb5b116100595780638da5cb5b1461021a578063a25ae5571461024f578063a4771aad146102c3578063f2fde38b146102f757600080fd5b806341840fa61461019d578063529933df146101d1578063715018a61461020557600080fd5b80630c1952d3116100b05780630c1952d31461015f5780632518810414610175578063357e951f1461018857600080fd5b80622134cc146100d657806302e513451461011d578063093b3d901461013d575b600080fd5b3480156100e257600080fd5b5061010a7f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020015b60405180910390f35b34801561012957600080fd5b5061010a610138366004610bbd565b610317565b34801561014957600080fd5b5061015d610158366004610bd6565b61044c565b005b34801561016b57600080fd5b5061010a60015481565b61015d610183366004610c4c565b610651565b34801561019457600080fd5b5061010a610956565b3480156101a957600080fd5b5061010a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156101dd57600080fd5b5061010a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561021157600080fd5b5061015d61098b565b34801561022657600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610114565b34801561025b57600080fd5b506102a861026a366004610bbd565b604080518082019091526000808252602082015250600090815260026020908152604091829020825180840190935280548352600101549082015290565b60408051825181526020928301519281019290925201610114565b3480156102cf57600080fd5b5061010a7f000000000000000000000000000000000000000000000000000000000000000081565b34801561030357600080fd5b5061015d610312366004610c7e565b610a18565b60007f00000000000000000000000000000000000000000000000000000000000000008210156103ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f54696d657374616d70207072696f7220746f207374617274696e67426c6f636b60448201527f54696d657374616d70000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083038161041f5761041f610cbb565b047f0000000000000000000000000000000000000000000000000000000000000000600101019050919050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146104cd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016103c5565b6001805460009081526002602090815260409182902082518084019093528054808452930154908201528251909114610588576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f43616e206f6e6c792064656c65746520746865206d6f737420726563656e742060448201527f6f75747075742e0000000000000000000000000000000000000000000000000060648201526084016103c5565b80602001518260200151146105d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600060248201526044016103c5565b600154602082015182516040517f6897e92e2fea3b89bf0d45ed867487716da71bfa624c6878569d227d736c700990600090a46001805460009081526002602052604081208181558201555461064a907f000000000000000000000000000000000000000000000000000000000000000090610d19565b6001555050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146106d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016103c5565b428310610761576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f43616e6e6f7420617070656e64204c32206f757470757420696e20667574757260448201527f650000000000000000000000000000000000000000000000000000000000000060648201526084016103c5565b610769610956565b83146107f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f54696d657374616d70206e6f7420657175616c20746f206e657874206578706560448201527f637465642074696d657374616d7000000000000000000000000000000000000060648201526084016103c5565b8361085e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e6e6f74207375626d697420656d707479204c32206f757470757400000060448201526064016103c5565b81156108f457818140146108f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f426c6f636b6861736820646f6573206e6f74206d61746368207468652068617360448201527f6820617420746865206578706563746564206865696768742e0000000000000060648201526084016103c5565b60408051808201825285815242602080830182815260008881526002909252848220935184555160019384015591869055915185929187917f54fbf9b58db0b6543a3a0cb9fbd4e98a7c7b88878978fa125b84a2c7f51d34ba9190a450505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001546109869190610d30565b905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a0c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016103c5565b610a166000610b48565b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a99576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016103c5565b73ffffffffffffffffffffffffffffffffffffffff8116610b3c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016103c5565b610b4581610b48565b50565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600060208284031215610bcf57600080fd5b5035919050565b600060408284031215610be857600080fd5b6040516040810181811067ffffffffffffffff82111715610c32577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052823581526020928301359281019290925250919050565b60008060008060808587031215610c6257600080fd5b5050823594602084013594506040840135936060013592509050565b600060208284031215610c9057600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610cb457600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015610d2b57610d2b610cea565b500390565b60008219821115610d4357610d43610cea565b50019056fea164736f6c634300080a000a", } // L2OutputOracleABI is the input ABI used to generate the binding from. @@ -358,16 +364,16 @@ func (_L2OutputOracle *L2OutputOracleCallerSession) ComputeL2BlockNumber(_l2time // GetL2Output is a free data retrieval call binding the contract method 0xa25ae557. // -// Solidity: function getL2Output(uint256 _l2Timestamp) view returns(bytes32) -func (_L2OutputOracle *L2OutputOracleCaller) GetL2Output(opts *bind.CallOpts, _l2Timestamp *big.Int) ([32]byte, error) { +// Solidity: function getL2Output(uint256 _l2Timestamp) view returns((bytes32,uint256)) +func (_L2OutputOracle *L2OutputOracleCaller) GetL2Output(opts *bind.CallOpts, _l2Timestamp *big.Int) (L2OutputOracleOutputProposal, error) { var out []interface{} err := _L2OutputOracle.contract.Call(opts, &out, "getL2Output", _l2Timestamp) if err != nil { - return *new([32]byte), err + return *new(L2OutputOracleOutputProposal), err } - out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + out0 := *abi.ConvertType(out[0], new(L2OutputOracleOutputProposal)).(*L2OutputOracleOutputProposal) return out0, err @@ -375,15 +381,15 @@ func (_L2OutputOracle *L2OutputOracleCaller) GetL2Output(opts *bind.CallOpts, _l // GetL2Output is a free data retrieval call binding the contract method 0xa25ae557. // -// Solidity: function getL2Output(uint256 _l2Timestamp) view returns(bytes32) -func (_L2OutputOracle *L2OutputOracleSession) GetL2Output(_l2Timestamp *big.Int) ([32]byte, error) { +// Solidity: function getL2Output(uint256 _l2Timestamp) view returns((bytes32,uint256)) +func (_L2OutputOracle *L2OutputOracleSession) GetL2Output(_l2Timestamp *big.Int) (L2OutputOracleOutputProposal, error) { return _L2OutputOracle.Contract.GetL2Output(&_L2OutputOracle.CallOpts, _l2Timestamp) } // GetL2Output is a free data retrieval call binding the contract method 0xa25ae557. // -// Solidity: function getL2Output(uint256 _l2Timestamp) view returns(bytes32) -func (_L2OutputOracle *L2OutputOracleCallerSession) GetL2Output(_l2Timestamp *big.Int) ([32]byte, error) { +// Solidity: function getL2Output(uint256 _l2Timestamp) view returns((bytes32,uint256)) +func (_L2OutputOracle *L2OutputOracleCallerSession) GetL2Output(_l2Timestamp *big.Int) (L2OutputOracleOutputProposal, error) { return _L2OutputOracle.Contract.GetL2Output(&_L2OutputOracle.CallOpts, _l2Timestamp) } @@ -501,25 +507,25 @@ func (_L2OutputOracle *L2OutputOracleTransactorSession) AppendL2Output(_l2Output return _L2OutputOracle.Contract.AppendL2Output(&_L2OutputOracle.TransactOpts, _l2Output, _l2timestamp, _l1Blockhash, _l1Blocknumber) } -// DeleteL2Output is a paid mutator transaction binding the contract method 0x2b5d15d9. +// DeleteL2Output is a paid mutator transaction binding the contract method 0x093b3d90. // -// Solidity: function deleteL2Output(bytes32 _l2Output) returns() -func (_L2OutputOracle *L2OutputOracleTransactor) DeleteL2Output(opts *bind.TransactOpts, _l2Output [32]byte) (*types.Transaction, error) { - return _L2OutputOracle.contract.Transact(opts, "deleteL2Output", _l2Output) +// Solidity: function deleteL2Output((bytes32,uint256) _proposal) returns() +func (_L2OutputOracle *L2OutputOracleTransactor) DeleteL2Output(opts *bind.TransactOpts, _proposal L2OutputOracleOutputProposal) (*types.Transaction, error) { + return _L2OutputOracle.contract.Transact(opts, "deleteL2Output", _proposal) } -// DeleteL2Output is a paid mutator transaction binding the contract method 0x2b5d15d9. +// DeleteL2Output is a paid mutator transaction binding the contract method 0x093b3d90. // -// Solidity: function deleteL2Output(bytes32 _l2Output) returns() -func (_L2OutputOracle *L2OutputOracleSession) DeleteL2Output(_l2Output [32]byte) (*types.Transaction, error) { - return _L2OutputOracle.Contract.DeleteL2Output(&_L2OutputOracle.TransactOpts, _l2Output) +// Solidity: function deleteL2Output((bytes32,uint256) _proposal) returns() +func (_L2OutputOracle *L2OutputOracleSession) DeleteL2Output(_proposal L2OutputOracleOutputProposal) (*types.Transaction, error) { + return _L2OutputOracle.Contract.DeleteL2Output(&_L2OutputOracle.TransactOpts, _proposal) } -// DeleteL2Output is a paid mutator transaction binding the contract method 0x2b5d15d9. +// DeleteL2Output is a paid mutator transaction binding the contract method 0x093b3d90. // -// Solidity: function deleteL2Output(bytes32 _l2Output) returns() -func (_L2OutputOracle *L2OutputOracleTransactorSession) DeleteL2Output(_l2Output [32]byte) (*types.Transaction, error) { - return _L2OutputOracle.Contract.DeleteL2Output(&_L2OutputOracle.TransactOpts, _l2Output) +// Solidity: function deleteL2Output((bytes32,uint256) _proposal) returns() +func (_L2OutputOracle *L2OutputOracleTransactorSession) DeleteL2Output(_proposal L2OutputOracleOutputProposal) (*types.Transaction, error) { + return _L2OutputOracle.Contract.DeleteL2Output(&_L2OutputOracle.TransactOpts, _proposal) } // RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. @@ -787,46 +793,55 @@ func (it *L2OutputOracleL2OutputAppendedIterator) Close() error { // L2OutputOracleL2OutputAppended represents a L2OutputAppended event raised by the L2OutputOracle contract. type L2OutputOracleL2OutputAppended struct { L2Output [32]byte + L1Timestamp *big.Int L2timestamp *big.Int Raw types.Log // Blockchain specific contextual infos } -// FilterL2OutputAppended is a free log retrieval operation binding the contract event 0x92701dc658a5d84c16077ea6de344b9995e21a96a05d45e4cd22f37a3d266f8b. +// FilterL2OutputAppended is a free log retrieval operation binding the contract event 0x54fbf9b58db0b6543a3a0cb9fbd4e98a7c7b88878978fa125b84a2c7f51d34ba. // -// Solidity: event l2OutputAppended(bytes32 indexed _l2Output, uint256 indexed _l2timestamp) -func (_L2OutputOracle *L2OutputOracleFilterer) FilterL2OutputAppended(opts *bind.FilterOpts, _l2Output [][32]byte, _l2timestamp []*big.Int) (*L2OutputOracleL2OutputAppendedIterator, error) { +// Solidity: event l2OutputAppended(bytes32 indexed _l2Output, uint256 indexed _l1Timestamp, uint256 indexed _l2timestamp) +func (_L2OutputOracle *L2OutputOracleFilterer) FilterL2OutputAppended(opts *bind.FilterOpts, _l2Output [][32]byte, _l1Timestamp []*big.Int, _l2timestamp []*big.Int) (*L2OutputOracleL2OutputAppendedIterator, error) { var _l2OutputRule []interface{} for _, _l2OutputItem := range _l2Output { _l2OutputRule = append(_l2OutputRule, _l2OutputItem) } + var _l1TimestampRule []interface{} + for _, _l1TimestampItem := range _l1Timestamp { + _l1TimestampRule = append(_l1TimestampRule, _l1TimestampItem) + } var _l2timestampRule []interface{} for _, _l2timestampItem := range _l2timestamp { _l2timestampRule = append(_l2timestampRule, _l2timestampItem) } - logs, sub, err := _L2OutputOracle.contract.FilterLogs(opts, "l2OutputAppended", _l2OutputRule, _l2timestampRule) + logs, sub, err := _L2OutputOracle.contract.FilterLogs(opts, "l2OutputAppended", _l2OutputRule, _l1TimestampRule, _l2timestampRule) if err != nil { return nil, err } return &L2OutputOracleL2OutputAppendedIterator{contract: _L2OutputOracle.contract, event: "l2OutputAppended", logs: logs, sub: sub}, nil } -// WatchL2OutputAppended is a free log subscription operation binding the contract event 0x92701dc658a5d84c16077ea6de344b9995e21a96a05d45e4cd22f37a3d266f8b. +// WatchL2OutputAppended is a free log subscription operation binding the contract event 0x54fbf9b58db0b6543a3a0cb9fbd4e98a7c7b88878978fa125b84a2c7f51d34ba. // -// Solidity: event l2OutputAppended(bytes32 indexed _l2Output, uint256 indexed _l2timestamp) -func (_L2OutputOracle *L2OutputOracleFilterer) WatchL2OutputAppended(opts *bind.WatchOpts, sink chan<- *L2OutputOracleL2OutputAppended, _l2Output [][32]byte, _l2timestamp []*big.Int) (event.Subscription, error) { +// Solidity: event l2OutputAppended(bytes32 indexed _l2Output, uint256 indexed _l1Timestamp, uint256 indexed _l2timestamp) +func (_L2OutputOracle *L2OutputOracleFilterer) WatchL2OutputAppended(opts *bind.WatchOpts, sink chan<- *L2OutputOracleL2OutputAppended, _l2Output [][32]byte, _l1Timestamp []*big.Int, _l2timestamp []*big.Int) (event.Subscription, error) { var _l2OutputRule []interface{} for _, _l2OutputItem := range _l2Output { _l2OutputRule = append(_l2OutputRule, _l2OutputItem) } + var _l1TimestampRule []interface{} + for _, _l1TimestampItem := range _l1Timestamp { + _l1TimestampRule = append(_l1TimestampRule, _l1TimestampItem) + } var _l2timestampRule []interface{} for _, _l2timestampItem := range _l2timestamp { _l2timestampRule = append(_l2timestampRule, _l2timestampItem) } - logs, sub, err := _L2OutputOracle.contract.WatchLogs(opts, "l2OutputAppended", _l2OutputRule, _l2timestampRule) + logs, sub, err := _L2OutputOracle.contract.WatchLogs(opts, "l2OutputAppended", _l2OutputRule, _l1TimestampRule, _l2timestampRule) if err != nil { return nil, err } @@ -858,9 +873,9 @@ func (_L2OutputOracle *L2OutputOracleFilterer) WatchL2OutputAppended(opts *bind. }), nil } -// ParseL2OutputAppended is a log parse operation binding the contract event 0x92701dc658a5d84c16077ea6de344b9995e21a96a05d45e4cd22f37a3d266f8b. +// ParseL2OutputAppended is a log parse operation binding the contract event 0x54fbf9b58db0b6543a3a0cb9fbd4e98a7c7b88878978fa125b84a2c7f51d34ba. // -// Solidity: event l2OutputAppended(bytes32 indexed _l2Output, uint256 indexed _l2timestamp) +// Solidity: event l2OutputAppended(bytes32 indexed _l2Output, uint256 indexed _l1Timestamp, uint256 indexed _l2timestamp) func (_L2OutputOracle *L2OutputOracleFilterer) ParseL2OutputAppended(log types.Log) (*L2OutputOracleL2OutputAppended, error) { event := new(L2OutputOracleL2OutputAppended) if err := _L2OutputOracle.contract.UnpackLog(event, "l2OutputAppended", log); err != nil { @@ -940,46 +955,55 @@ func (it *L2OutputOracleL2OutputDeletedIterator) Close() error { // L2OutputOracleL2OutputDeleted represents a L2OutputDeleted event raised by the L2OutputOracle contract. type L2OutputOracleL2OutputDeleted struct { L2Output [32]byte + L1Timestamp *big.Int L2timestamp *big.Int Raw types.Log // Blockchain specific contextual infos } -// FilterL2OutputDeleted is a free log retrieval operation binding the contract event 0x36160157c233b38fe28ace444408a0db0f7b3d02e652bb27ea9604cbd2a3f266. +// FilterL2OutputDeleted is a free log retrieval operation binding the contract event 0x6897e92e2fea3b89bf0d45ed867487716da71bfa624c6878569d227d736c7009. // -// Solidity: event l2OutputDeleted(bytes32 indexed _l2Output, uint256 indexed _l2timestamp) -func (_L2OutputOracle *L2OutputOracleFilterer) FilterL2OutputDeleted(opts *bind.FilterOpts, _l2Output [][32]byte, _l2timestamp []*big.Int) (*L2OutputOracleL2OutputDeletedIterator, error) { +// Solidity: event l2OutputDeleted(bytes32 indexed _l2Output, uint256 indexed _l1Timestamp, uint256 indexed _l2timestamp) +func (_L2OutputOracle *L2OutputOracleFilterer) FilterL2OutputDeleted(opts *bind.FilterOpts, _l2Output [][32]byte, _l1Timestamp []*big.Int, _l2timestamp []*big.Int) (*L2OutputOracleL2OutputDeletedIterator, error) { var _l2OutputRule []interface{} for _, _l2OutputItem := range _l2Output { _l2OutputRule = append(_l2OutputRule, _l2OutputItem) } + var _l1TimestampRule []interface{} + for _, _l1TimestampItem := range _l1Timestamp { + _l1TimestampRule = append(_l1TimestampRule, _l1TimestampItem) + } var _l2timestampRule []interface{} for _, _l2timestampItem := range _l2timestamp { _l2timestampRule = append(_l2timestampRule, _l2timestampItem) } - logs, sub, err := _L2OutputOracle.contract.FilterLogs(opts, "l2OutputDeleted", _l2OutputRule, _l2timestampRule) + logs, sub, err := _L2OutputOracle.contract.FilterLogs(opts, "l2OutputDeleted", _l2OutputRule, _l1TimestampRule, _l2timestampRule) if err != nil { return nil, err } return &L2OutputOracleL2OutputDeletedIterator{contract: _L2OutputOracle.contract, event: "l2OutputDeleted", logs: logs, sub: sub}, nil } -// WatchL2OutputDeleted is a free log subscription operation binding the contract event 0x36160157c233b38fe28ace444408a0db0f7b3d02e652bb27ea9604cbd2a3f266. +// WatchL2OutputDeleted is a free log subscription operation binding the contract event 0x6897e92e2fea3b89bf0d45ed867487716da71bfa624c6878569d227d736c7009. // -// Solidity: event l2OutputDeleted(bytes32 indexed _l2Output, uint256 indexed _l2timestamp) -func (_L2OutputOracle *L2OutputOracleFilterer) WatchL2OutputDeleted(opts *bind.WatchOpts, sink chan<- *L2OutputOracleL2OutputDeleted, _l2Output [][32]byte, _l2timestamp []*big.Int) (event.Subscription, error) { +// Solidity: event l2OutputDeleted(bytes32 indexed _l2Output, uint256 indexed _l1Timestamp, uint256 indexed _l2timestamp) +func (_L2OutputOracle *L2OutputOracleFilterer) WatchL2OutputDeleted(opts *bind.WatchOpts, sink chan<- *L2OutputOracleL2OutputDeleted, _l2Output [][32]byte, _l1Timestamp []*big.Int, _l2timestamp []*big.Int) (event.Subscription, error) { var _l2OutputRule []interface{} for _, _l2OutputItem := range _l2Output { _l2OutputRule = append(_l2OutputRule, _l2OutputItem) } + var _l1TimestampRule []interface{} + for _, _l1TimestampItem := range _l1Timestamp { + _l1TimestampRule = append(_l1TimestampRule, _l1TimestampItem) + } var _l2timestampRule []interface{} for _, _l2timestampItem := range _l2timestamp { _l2timestampRule = append(_l2timestampRule, _l2timestampItem) } - logs, sub, err := _L2OutputOracle.contract.WatchLogs(opts, "l2OutputDeleted", _l2OutputRule, _l2timestampRule) + logs, sub, err := _L2OutputOracle.contract.WatchLogs(opts, "l2OutputDeleted", _l2OutputRule, _l1TimestampRule, _l2timestampRule) if err != nil { return nil, err } @@ -1011,9 +1035,9 @@ func (_L2OutputOracle *L2OutputOracleFilterer) WatchL2OutputDeleted(opts *bind.W }), nil } -// ParseL2OutputDeleted is a log parse operation binding the contract event 0x36160157c233b38fe28ace444408a0db0f7b3d02e652bb27ea9604cbd2a3f266. +// ParseL2OutputDeleted is a log parse operation binding the contract event 0x6897e92e2fea3b89bf0d45ed867487716da71bfa624c6878569d227d736c7009. // -// Solidity: event l2OutputDeleted(bytes32 indexed _l2Output, uint256 indexed _l2timestamp) +// Solidity: event l2OutputDeleted(bytes32 indexed _l2Output, uint256 indexed _l1Timestamp, uint256 indexed _l2timestamp) func (_L2OutputOracle *L2OutputOracleFilterer) ParseL2OutputDeleted(log types.Log) (*L2OutputOracleL2OutputDeleted, error) { event := new(L2OutputOracleL2OutputDeleted) if err := _L2OutputOracle.contract.UnpackLog(event, "l2OutputDeleted", log); err != nil { diff --git a/l2os/drivers/l2output/driver.go b/l2os/drivers/l2output/driver.go index 9db81858..350d103a 100644 --- a/l2os/drivers/l2output/driver.go +++ b/l2os/drivers/l2output/driver.go @@ -61,6 +61,7 @@ func NewDriver(cfg Config) (*Driver, error) { ) walletAddr := crypto.PubkeyToAddress(cfg.PrivKey.PublicKey) + log.Info("Configured driver", "wallet", walletAddr, "l2-output-contract", cfg.L2OOAddr) return &Driver{ cfg: cfg, diff --git a/opnode/contracts/Makefile b/opnode/contracts/Makefile index 20dfa49c..7b9e9bec 100644 --- a/opnode/contracts/Makefile +++ b/opnode/contracts/Makefile @@ -1,13 +1,9 @@ SHELL := /bin/bash CONTRACTS_PATH := "../../packages/contracts/" -OPTIMISM_PORTAL := ../../packages/contracts/contracts/L1/OptimismPortal.sol -L1_BLOCK_INFO := ../../packages/contracts/contracts/L2/L1Block.sol - bindings: bindings-l1-block-info bindings-optimism-portal deployed-bin-l1-block-info deployed-bin-optimism-portal - # Split up b/c I don't know how to include this step in the L1 Block Info Bindings # What is occuring is that the `temp` variable is hard to pull into the `eval` deployed-bin-l1-block-info: bindings-l1-block-info diff --git a/opnode/contracts/deposit/deposit_feed_raw.go b/opnode/contracts/deposit/deposit_feed_raw.go index fa5b453b..58e512d9 100644 --- a/opnode/contracts/deposit/deposit_feed_raw.go +++ b/opnode/contracts/deposit/deposit_feed_raw.go @@ -38,8 +38,8 @@ type WithdrawalVerifierOutputRootProof struct { // OptimismPortalMetaData contains all meta data concerning the OptimismPortal contract. var OptimismPortalMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"_l2Oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_finalizationPeriod\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidOutputRootProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidWithdrawalInclusionProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonZeroCreationTarget\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotYetFinal\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WithdrawalAlreadyFinalized\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"mint\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCreation\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"TransactionDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"WithdrawalFinalized\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"FINALIZATION_PERIOD\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"L2_ORACLE\",\"outputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"_isCreation\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"depositTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_timestamp\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawerStorageRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"latestBlockhash\",\"type\":\"bytes32\"}],\"internalType\":\"structWithdrawalVerifier.OutputRootProof\",\"name\":\"_outputRootProof\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"_withdrawalProof\",\"type\":\"bytes\"}],\"name\":\"finalizeWithdrawalTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"finalizedWithdrawals\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l2Sender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", - Bin: "0x60c0604052600080546001600160a01b03191661dead17905534801561002457600080fd5b50604051620022f9380380620022f98339810160408190526100459161005b565b6001600160a01b0390911660a052608052610095565b6000806040838503121561006e57600080fd5b82516001600160a01b038116811461008557600080fd5b6020939093015192949293505050565b60805160a051612231620000c86000396000818160a501526103500152600081816101a301526102c401526122316000f3fe6080604052600436106100685760003560e01c8063e9e05c4211610043578063e9e05c421461015e578063eecf1c3614610171578063ff61cc931461019157600080fd5b80621c2ff6146100935780639bf62d82146100f1578063a14238e71461011e57600080fd5b3661008e5761008c33346175306000604051806020016040528060008152506101d3565b005b600080fd5b34801561009f57600080fd5b506100c77f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156100fd57600080fd5b506000546100c79073ffffffffffffffffffffffffffffffffffffffff1681565b34801561012a57600080fd5b5061014e610139366004611c41565b60016020526000908152604090205460ff1681565b60405190151581526020016100e8565b61008c61016c366004611cb2565b6101d3565b34801561017d57600080fd5b5061008c61018c366004611e18565b6102c2565b34801561019d57600080fd5b506101c57f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016100e8565b8180156101f5575073ffffffffffffffffffffffffffffffffffffffff851615155b1561022c576040517ff98844ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3332811461024d575033731111000000000000000000000000000000001111015b8573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f78231ae6eb73366f912bb1d64351601fb76344c537bbab635ce14d0f376f019534888888886040516102b2959493929190611f18565b60405180910390a3505050505050565b7f0000000000000000000000000000000000000000000000000000000000000000840142101561031e576040517fe4750a3000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401602060405180830381865afa1580156103ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d09190611fb4565b9050610424846040805182356020828101919091528301358183015290820135606082810191909152820135608082015260009060a001604051602081830303815290604052805190602001209050919050565b811461045c576040517f9cc00b5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061046d8d8d8d8d8d8d8d610648565b905061047f818660400135868661068a565b6104b5576040517feb00eb2200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526001602081905260409091205460ff1615151415610504576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180600083815260200190815260200160002060006101000a81548160ff0219169083151502179055508b6000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008b73ffffffffffffffffffffffffffffffffffffffff168b8b908b8b60405161059b929190611fcd565b600060405180830381858888f193505050503d80600081146105d9576040519150601f19603f3d011682016040523d82523d6000602084013e6105de565b606091505b5050600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17815560405191925083917f894485e328061b8d209b7dd043d2f613fc2892260497cadefac9a183962a990f9190a25050505050505050505050505050565b6000878787878787876040516020016106679796959493929190611fdd565b604051602081830303815290604052805190602001209050979650505050505050565b6000808560016040516020016106aa929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120908301819052925061077091016040516020818303038152906040526040518060400160405280600181526020017f010000000000000000000000000000000000000000000000000000000000000081525086868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b925061077a915050565b9695505050505050565b60008061078686610794565b9050610770818686866107c6565b606081805190602001206040516020016107b091815260200190565b6040516020818303038152906040529050919050565b60008060006107d68786866107f7565b915091508180156107ec57506107ec86826108f1565b979650505050505050565b6000606060006108068561090d565b90506000806000610818848a89610a08565b8151929550909350915015808061082c5750815b610897576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f50726f76696465642070726f6f6620697320696e76616c69642e00000000000060448201526064015b60405180910390fd5b6000816108b357604051806020016040528060008152506108df565b6108df866108c2600188612099565b815181106108d2576108d26120b0565b6020026020010151610f25565b919b919a509098505050505050505050565b6000818051906020012083805190602001201490505b92915050565b6060600061091a83610f4f565b90506000815167ffffffffffffffff81111561093857610938611c83565b60405190808252806020026020018201604052801561097d57816020015b60408051808201909152606080825260208201528152602001906001900390816109565790505b50905060005b8251811015610a005760006109b08483815181106109a3576109a36120b0565b6020026020010151610f82565b905060405180604001604052808281526020016109cc83610f4f565b8152508383815181106109e1576109e16120b0565b60200260200101819052505080806109f8906120df565b915050610983565b509392505050565b60006060818080610a188761102c565b90506000869050600080610a3f604051806040016040528060608152602001606081525090565b60005b8c51811015610ee1578c8181518110610a5d57610a5d6120b0565b602002602001015191508284610a739190612118565b9350610a80600188612118565b965083610afe57815180516020909101208514610af9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e76616c696420726f6f742068617368000000000000000000000000000000604482015260640161088e565b610bef565b815151602011610b7a57815180516020909101208514610af9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f496e76616c6964206c6172676520696e7465726e616c20686173680000000000604482015260640161088e565b84610b8883600001516111af565b14610bef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f496e76616c696420696e7465726e616c206e6f64652068617368000000000000604482015260640161088e565b610bfb60106001612118565b8260200151511415610c74578551841415610c1557610ee1565b6000868581518110610c2957610c296120b0565b602001015160f81c60f81b60f81c9050600083602001518260ff1681518110610c5457610c546120b0565b60200260200101519050610c67816111d7565b9650600194505050610ecf565b60028260200151511415610e6d576000610c8d83611214565b9050600081600081518110610ca457610ca46120b0565b016020015160f81c90506000610cbb60028361215f565b610cc6906002612181565b90506000610cd7848360ff16611238565b90506000610ce58b8a611238565b90506000610cf3838361126e565b905060ff851660021480610d0a575060ff85166003145b15610d6057808351148015610d1f5750808251145b15610d3157610d2e818b612118565b99505b507f80000000000000000000000000000000000000000000000000000000000000009950610ee1945050505050565b60ff85161580610d73575060ff85166001145b15610de55782518114610daf57507f80000000000000000000000000000000000000000000000000000000000000009950610ee1945050505050565b610dd68860200151600181518110610dc957610dc96120b0565b60200260200101516111d7565b9a509750610ecf945050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f52656365697665642061206e6f6465207769746820616e20756e6b6e6f776e2060448201527f7072656669780000000000000000000000000000000000000000000000000000606482015260840161088e565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f526563656976656420616e20756e706172736561626c65206e6f64652e000000604482015260640161088e565b80610ed9816120df565b915050610a42565b507f8000000000000000000000000000000000000000000000000000000000000000841486610f108786611238565b909e909d50909b509950505050505050505050565b6020810151805160609161090791610f3f90600190612099565b815181106109a3576109a36120b0565b6040805180820182526000808252602091820152815180830190925282518252808301908201526060906109079061131a565b60606000806000610f928561154d565b919450925090506000816001811115610fad57610fad6121a4565b14611014576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c696420524c502062797465732076616c75652e0000000000000000604482015260640161088e565b61102385602001518484611954565b95945050505050565b606060008251600261103e91906121d3565b67ffffffffffffffff81111561105657611056611c83565b6040519080825280601f01601f191660200182016040528015611080576020820181803683370190505b50905060005b83518110156111a85760048482815181106110a3576110a36120b0565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016901c826110d88360026121d3565b815181106110e8576110e86120b0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350601084828151811061112b5761112b6120b0565b016020015161113d919060f81c61215f565b60f81b8261114c8360026121d3565b611157906001612118565b81518110611167576111676120b0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806111a0816120df565b915050611086565b5092915050565b60006020825110156111c357506020015190565b818060200190518101906109079190611fb4565b600060606020836000015110156111f8576111f183611a33565b9050611204565b61120183610f82565b90505b61120d816111af565b9392505050565b606061090761123383602001516000815181106109a3576109a36120b0565b61102c565b6060825182106112575750604080516020810190915260008152610907565b61120d83838486516112699190612099565b611a3e565b6000805b8084511180156112825750808351115b8015611303575082818151811061129b5761129b6120b0565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168482815181106112da576112da6120b0565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b1561120d5780611312816120df565b915050611272565b60606000806113288461154d565b91935090915060019050816001811115611344576113446121a4565b146113ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420524c50206c6973742076616c75652e000000000000000000604482015260640161088e565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816113c45790505090506000835b8651811015611542576020821061148a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f50726f766964656420524c50206c6973742065786365656473206d6178206c6960448201527f7374206c656e6774682e00000000000000000000000000000000000000000000606482015260840161088e565b6000806114c76040518060400160405280858c600001516114ab9190612099565b8152602001858c602001516114c09190612118565b905261154d565b5091509150604051806040016040528083836114e39190612118565b8152602001848b602001516114f89190612118565b81525085858151811061150d5761150d6120b0565b6020908102919091010152611523600185612118565b935061152f8183612118565b6115399084612118565b925050506113f1565b508152949350505050565b6000806000808460000151116115bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f524c50206974656d2063616e6e6f74206265206e756c6c2e0000000000000000604482015260640161088e565b6020840151805160001a607f81116115e457600060016000945094509450505061194d565b60b7811161167a5760006115f9608083612099565b905080876000015111611668576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f496e76616c696420524c502073686f727420737472696e672e00000000000000604482015260640161088e565b6001955093506000925061194d915050565b60bf811161179d57600061168f60b783612099565b9050808760000151116116fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f496e76616c696420524c50206c6f6e6720737472696e67206c656e6774682e00604482015260640161088e565b600183015160208290036101000a90046117188183612118565b885111611781576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c696420524c50206c6f6e6720737472696e672e0000000000000000604482015260640161088e565b61178c826001612118565b965094506000935061194d92505050565b60f781116118325760006117b260c083612099565b905080876000015111611821576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420524c502073686f7274206c6973742e000000000000000000604482015260640161088e565b60019550935084925061194d915050565b600061183f60f783612099565b9050808760000151116118ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f496e76616c696420524c50206c6f6e67206c697374206c656e6774682e000000604482015260640161088e565b600183015160208290036101000a90046118c88183612118565b885111611931576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e76616c696420524c50206c6f6e67206c6973742e00000000000000000000604482015260640161088e565b61193c826001612118565b965094506001935061194d92505050565b9193909250565b606060008267ffffffffffffffff81111561197157611971611c83565b6040519080825280601f01601f19166020018201604052801561199b576020820181803683370190505b5090508051600014156119af57905061120d565b60006119bb8587612118565b90506020820160005b6119cf602087612210565b811015611a0657825182526119e5602084612118565b92506119f2602083612118565b9150806119fe816120df565b9150506119c4565b5060006001602087066020036101000a039050808251168119845116178252839450505050509392505050565b606061090782611c2b565b606081611a4c81601f612118565b1015611ab4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161088e565b82611abf8382612118565b1015611b27576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161088e565b611b318284612118565b84511015611b9b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604482015260640161088e565b606082158015611bba5760405191506000825260208201604052611c22565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015611bf3578051835260209283019201611bdb565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6060610907826020015160008460000151611954565b600060208284031215611c5357600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611c7e57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600080600060a08688031215611cca57600080fd5b611cd386611c5a565b945060208601359350604086013567ffffffffffffffff8082168214611cf857600080fd5b9093506060870135908115158214611d0f57600080fd5b90925060808701359080821115611d2557600080fd5b818801915088601f830112611d3957600080fd5b813581811115611d4b57611d4b611c83565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611d9157611d91611c83565b816040528281528b6020848701011115611daa57600080fd5b8260208601602083013760006020848301015280955050505050509295509295909350565b60008083601f840112611de157600080fd5b50813567ffffffffffffffff811115611df957600080fd5b602083019150836020828501011115611e1157600080fd5b9250929050565b60008060008060008060008060008060006101808c8e031215611e3a57600080fd5b8b359a50611e4a60208d01611c5a565b9950611e5860408d01611c5a565b985060608c0135975060808c0135965067ffffffffffffffff60a08d01351115611e8157600080fd5b611e918d60a08e01358e01611dcf565b909650945060c08c0135935060808c8e037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff20011215611ecf57600080fd5b60e08c01925067ffffffffffffffff6101608d01351115611eef57600080fd5b611f008d6101608e01358e01611dcf565b81935080925050509295989b509295989b9093969950565b85815260006020868184015267ffffffffffffffff86166040840152841515606084015260a0608084015283518060a085015260005b81811015611f6a5785810183015185820160c001528201611f4e565b81811115611f7c57600060c083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160c001979650505050505050565b600060208284031215611fc657600080fd5b5051919050565b8183823760009101908152919050565b878152600073ffffffffffffffffffffffffffffffffffffffff808916602084015280881660408401525085606083015284608083015260c060a08301528260c0830152828460e0840137600060e0848401015260e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116830101905098975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156120ab576120ab61206a565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156121115761211161206a565b5060010190565b6000821982111561212b5761212b61206a565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061217257612172612130565b8060ff84160691505092915050565b600060ff821660ff84168082101561219b5761219b61206a565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561220b5761220b61206a565b500290565b60008261221f5761221f612130565b50049056fea164736f6c634300080a000a", + ABI: "[{\"inputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"_l2Oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_finalizationPeriod\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"InvalidOutputRootProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidWithdrawalInclusionProof\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NonZeroCreationTarget\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"WithdrawalAlreadyFinalized\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"mint\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"gasLimit\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"isCreation\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"TransactionDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"WithdrawalFinalized\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"FINALIZATION_PERIOD\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"L2_ORACLE\",\"outputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"_isCreation\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"depositTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"_sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_l2Timestamp\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"withdrawerStorageRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"latestBlockhash\",\"type\":\"bytes32\"}],\"internalType\":\"structWithdrawalVerifier.OutputRootProof\",\"name\":\"_outputRootProof\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"_withdrawalProof\",\"type\":\"bytes\"}],\"name\":\"finalizeWithdrawalTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"finalizedWithdrawals\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l2Sender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + Bin: "0x60c0604052600080546001600160a01b03191661dead1790553480156200002557600080fd5b50604051620025da380380620025da83398101604081905262000048916200005f565b6001600160a01b0390911660a0526080526200009b565b600080604083850312156200007357600080fd5b82516001600160a01b03811681146200008b57600080fd5b6020939093015192949293505050565b60805160a05161250c620000ce6000396000818160a6015261036d01526000818161019701526103f0015261250c6000f3fe6080604052600436106100685760003560e01c8063e9e05c4211610043578063e9e05c421461015f578063eecf1c3614610172578063ff61cc931461018557600080fd5b80621c2ff6146100945780639bf62d82146100f2578063a14238e71461011f57600080fd5b3661008f5761008d3334620186a06000604051806020016040528060008152506101c7565b005b600080fd5b3480156100a057600080fd5b506100c87f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156100fe57600080fd5b506000546100c89073ffffffffffffffffffffffffffffffffffffffff1681565b34801561012b57600080fd5b5061014f61013a366004611e74565b60016020526000908152604090205460ff1681565b60405190151581526020016100e9565b61008d61016d366004611f34565b6101c7565b61008d61018036600461207e565b6102b6565b34801561019157600080fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016100e9565b8180156101e9575073ffffffffffffffffffffffffffffffffffffffff851615155b15610220576040517ff98844ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33328114610241575033731111000000000000000000000000000000001111015b8573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f78231ae6eb73366f912bb1d64351601fb76344c537bbab635ce14d0f376f019534888888886040516102a69594939291906121e9565b60405180910390a3505050505050565b73ffffffffffffffffffffffffffffffffffffffff891630141561033b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f43616e6e6f742073656e64206d65737361676520746f2073656c662e0000000060448201526064015b60405180910390fd5b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae557906024016040805180830381865afa1580156103c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ec9190612220565b90507f0000000000000000000000000000000000000000000000000000000000000000816020015161041e919061229e565b4211610486576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f50726f706f73616c206973206e6f74207965742066696e616c697a65642e00006044820152606401610332565b61049d610498368690038601866122b6565b6107c0565b8151146104d6576040517f9cc00b5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061051c8d8d8d8d8d8d8d8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061081c92505050565b905061056381866040013586868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061085b92505050565b610599576040517feb00eb2200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526001602081905260409091205460ff16151514156105e8576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081815260016020819052604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909117905561062e89614e2061229e565b5a10156106bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f496e73756666696369656e742067617320746f2066696e616c697a652077697460448201527f6864726177616c2e0000000000000000000000000000000000000000000000006064820152608401610332565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8e16178155604080516020601f8b01819004810282018101909252898152610742918e918d918f9186918f908f908190840183828082843760009201919091525061092492505050565b50600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915082907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b906107a890841515815260200190565b60405180910390a25050505050505050505050505050565b600081600001518260200151836040015184606001516040516020016107ff949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b60008686868686866040516020016108399695949392919061231c565b6040516020818303038152906040528051906020012090509695505050505050565b604080516020810185905260009181018290528190606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828252805160209182012090830181905292506109199101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f010000000000000000000000000000000000000000000000000000000000000060208301529085876109af565b9150505b9392505050565b6000606060008060008661ffff1667ffffffffffffffff81111561094a5761094a611eb6565b6040519080825280601f01601f191660200182016040528015610974576020820181803683370190505b5090506000808751602089018b8e8ef191503d925086831115610995578692505b828152826000602083013e90999098509650505050505050565b6000806109bb866109d3565b90506109c981868686610a05565b9695505050505050565b606081805190602001206040516020016109ef91815260200190565b6040516020818303038152906040529050919050565b6000806000610a15878686610a36565b91509150818015610a2b5750610a2b8682610b2b565b979650505050505050565b600060606000610a4585610b47565b90506000806000610a57848a89610c42565b81519295509093509150158080610a6b5750815b610ad1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f50726f76696465642070726f6f6620697320696e76616c69642e0000000000006044820152606401610332565b600081610aed5760405180602001604052806000815250610b19565b610b1986610afc600188612373565b81518110610b0c57610b0c61238a565b602002602001015161115f565b919b919a509098505050505050505050565b6000818051906020012083805190602001201490505b92915050565b60606000610b5483611189565b90506000815167ffffffffffffffff811115610b7257610b72611eb6565b604051908082528060200260200182016040528015610bb757816020015b6040805180820190915260608082526020820152815260200190600190039081610b905790505b50905060005b8251811015610c3a576000610bea848381518110610bdd57610bdd61238a565b60200260200101516111bc565b90506040518060400160405280828152602001610c0683611189565b815250838381518110610c1b57610c1b61238a565b6020026020010181905250508080610c32906123b9565b915050610bbd565b509392505050565b60006060818080610c5287611266565b90506000869050600080610c79604051806040016040528060608152602001606081525090565b60005b8c5181101561111b578c8181518110610c9757610c9761238a565b602002602001015191508284610cad919061229e565b9350610cba60018861229e565b965083610d3857815180516020909101208514610d33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e76616c696420726f6f7420686173680000000000000000000000000000006044820152606401610332565b610e29565b815151602011610db457815180516020909101208514610d33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f496e76616c6964206c6172676520696e7465726e616c206861736800000000006044820152606401610332565b84610dc283600001516113e9565b14610e29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f496e76616c696420696e7465726e616c206e6f646520686173680000000000006044820152606401610332565b610e356010600161229e565b8260200151511415610eae578551841415610e4f5761111b565b6000868581518110610e6357610e6361238a565b602001015160f81c60f81b60f81c9050600083602001518260ff1681518110610e8e57610e8e61238a565b60200260200101519050610ea181611411565b9650600194505050611109565b600282602001515114156110a7576000610ec783611447565b9050600081600081518110610ede57610ede61238a565b016020015160f81c90506000610ef5600283612421565b610f00906002612443565b90506000610f11848360ff1661146b565b90506000610f1f8b8a61146b565b90506000610f2d83836114a1565b905060ff851660021480610f44575060ff85166003145b15610f9a57808351148015610f595750808251145b15610f6b57610f68818b61229e565b99505b507f8000000000000000000000000000000000000000000000000000000000000000995061111b945050505050565b60ff85161580610fad575060ff85166001145b1561101f5782518114610fe957507f8000000000000000000000000000000000000000000000000000000000000000995061111b945050505050565b61101088602001516001815181106110035761100361238a565b6020026020010151611411565b9a509750611109945050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f52656365697665642061206e6f6465207769746820616e20756e6b6e6f776e2060448201527f70726566697800000000000000000000000000000000000000000000000000006064820152608401610332565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f526563656976656420616e20756e706172736561626c65206e6f64652e0000006044820152606401610332565b80611113816123b9565b915050610c7c565b507f800000000000000000000000000000000000000000000000000000000000000084148661114a878661146b565b909e909d50909b509950505050505050505050565b60208101518051606091610b419161117990600190612373565b81518110610bdd57610bdd61238a565b604080518082018252600080825260209182015281518083019092528251825280830190820152606090610b419061154d565b606060008060006111cc85611780565b9194509250905060008160018111156111e7576111e7612466565b1461124e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c696420524c502062797465732076616c75652e00000000000000006044820152606401610332565b61125d85602001518484611b87565b95945050505050565b60606000825160026112789190612495565b67ffffffffffffffff81111561129057611290611eb6565b6040519080825280601f01601f1916602001820160405280156112ba576020820181803683370190505b50905060005b83518110156113e25760048482815181106112dd576112dd61238a565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016901c82611312836002612495565b815181106113225761132261238a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060108482815181106113655761136561238a565b0160200151611377919060f81c612421565b60f81b82611386836002612495565b61139190600161229e565b815181106113a1576113a161238a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806113da816123b9565b9150506112c0565b5092915050565b60006020825110156113fd57506020015190565b81806020019051810190610b4191906124d2565b600060606020836000015110156114325761142b83611c66565b905061143e565b61143b836111bc565b90505b61091d816113e9565b6060610b416114668360200151600081518110610bdd57610bdd61238a565b611266565b60608251821061148a5750604080516020810190915260008152610b41565b61091d838384865161149c9190612373565b611c71565b6000805b8084511180156114b55750808351115b801561153657508281815181106114ce576114ce61238a565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191684828151811061150d5761150d61238a565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b1561091d5780611545816123b9565b9150506114a5565b606060008061155b84611780565b9193509091506001905081600181111561157757611577612466565b146115de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420524c50206c6973742076616c75652e0000000000000000006044820152606401610332565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816115f75790505090506000835b865181101561177557602082106116bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f50726f766964656420524c50206c6973742065786365656473206d6178206c6960448201527f7374206c656e6774682e000000000000000000000000000000000000000000006064820152608401610332565b6000806116fa6040518060400160405280858c600001516116de9190612373565b8152602001858c602001516116f3919061229e565b9052611780565b509150915060405180604001604052808383611716919061229e565b8152602001848b6020015161172b919061229e565b8152508585815181106117405761174061238a565b602090810291909101015261175660018561229e565b9350611762818361229e565b61176c908461229e565b92505050611624565b508152949350505050565b6000806000808460000151116117f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f524c50206974656d2063616e6e6f74206265206e756c6c2e00000000000000006044820152606401610332565b6020840151805160001a607f8111611817576000600160009450945094505050611b80565b60b781116118ad57600061182c608083612373565b90508087600001511161189b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f496e76616c696420524c502073686f727420737472696e672e000000000000006044820152606401610332565b60019550935060009250611b80915050565b60bf81116119d05760006118c260b783612373565b905080876000015111611931576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f496e76616c696420524c50206c6f6e6720737472696e67206c656e6774682e006044820152606401610332565b600183015160208290036101000a900461194b818361229e565b8851116119b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c696420524c50206c6f6e6720737472696e672e00000000000000006044820152606401610332565b6119bf82600161229e565b9650945060009350611b8092505050565b60f78111611a655760006119e560c083612373565b905080876000015111611a54576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420524c502073686f7274206c6973742e0000000000000000006044820152606401610332565b600195509350849250611b80915050565b6000611a7260f783612373565b905080876000015111611ae1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f496e76616c696420524c50206c6f6e67206c697374206c656e6774682e0000006044820152606401610332565b600183015160208290036101000a9004611afb818361229e565b885111611b64576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e76616c696420524c50206c6f6e67206c6973742e000000000000000000006044820152606401610332565b611b6f82600161229e565b9650945060019350611b8092505050565b9193909250565b606060008267ffffffffffffffff811115611ba457611ba4611eb6565b6040519080825280601f01601f191660200182016040528015611bce576020820181803683370190505b509050805160001415611be257905061091d565b6000611bee858761229e565b90506020820160005b611c026020876124eb565b811015611c395782518252611c1860208461229e565b9250611c2560208361229e565b915080611c31816123b9565b915050611bf7565b5060006001602087066020036101000a039050808251168119845116178252839450505050509392505050565b6060610b4182611e5e565b606081611c7f81601f61229e565b1015611ce7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610332565b82611cf2838261229e565b1015611d5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610332565b611d64828461229e565b84511015611dce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610332565b606082158015611ded5760405191506000825260208201604052611e55565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015611e26578051835260209283019201611e0e565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6060610b41826020015160008460000151611b87565b600060208284031215611e8657600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611eb157600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611f2c57611f2c611eb6565b604052919050565b600080600080600060a08688031215611f4c57600080fd5b611f5586611e8d565b94506020808701359450604087013567ffffffffffffffff8082168214611f7b57600080fd5b9094506060880135908115158214611f9257600080fd5b90935060808801359080821115611fa857600080fd5b818901915089601f830112611fbc57600080fd5b813581811115611fce57611fce611eb6565b611ffe847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611ee5565b91508082528a8482850101111561201457600080fd5b80848401858401376000848284010152508093505050509295509295909350565b60008083601f84011261204757600080fd5b50813567ffffffffffffffff81111561205f57600080fd5b60208301915083602082850101111561207757600080fd5b9250929050565b60008060008060008060008060008060006101808c8e0312156120a057600080fd5b8b359a506120b060208d01611e8d565b99506120be60408d01611e8d565b985060608c0135975060808c0135965067ffffffffffffffff60a08d013511156120e757600080fd5b6120f78d60a08e01358e01612035565b909650945060c08c0135935060808c8e037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2001121561213557600080fd5b60e08c01925067ffffffffffffffff6101608d0135111561215557600080fd5b6121668d6101608e01358e01612035565b81935080925050509295989b509295989b9093969950565b6000815180845260005b818110156121a457602081850181015186830182015201612188565b818111156121b6576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b85815284602082015267ffffffffffffffff84166040820152821515606082015260a060808201526000610a2b60a083018461217e565b60006040828403121561223257600080fd5b6040516040810181811067ffffffffffffffff8211171561225557612255611eb6565b604052825181526020928301519281019290925250919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156122b1576122b161226f565b500190565b6000608082840312156122c857600080fd5b6040516080810181811067ffffffffffffffff821117156122eb576122eb611eb6565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261236760c083018461217e565b98975050505050505050565b6000828210156123855761238561226f565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156123eb576123eb61226f565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff831680612434576124346123f2565b8060ff84160691505092915050565b600060ff821660ff84168082101561245d5761245d61226f565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156124cd576124cd61226f565b500290565b6000602082840312156124e457600080fd5b5051919050565b6000826124fa576124fa6123f2565b50049056fea164736f6c634300080a000a", } // OptimismPortalABI is the input ABI used to generate the binding from. @@ -356,23 +356,23 @@ func (_OptimismPortal *OptimismPortalTransactorSession) DepositTransaction(_to c // FinalizeWithdrawalTransaction is a paid mutator transaction binding the contract method 0xeecf1c36. // -// Solidity: function finalizeWithdrawalTransaction(uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _gasLimit, bytes _data, uint256 _timestamp, (bytes32,bytes32,bytes32,bytes32) _outputRootProof, bytes _withdrawalProof) returns() -func (_OptimismPortal *OptimismPortalTransactor) FinalizeWithdrawalTransaction(opts *bind.TransactOpts, _nonce *big.Int, _sender common.Address, _target common.Address, _value *big.Int, _gasLimit *big.Int, _data []byte, _timestamp *big.Int, _outputRootProof WithdrawalVerifierOutputRootProof, _withdrawalProof []byte) (*types.Transaction, error) { - return _OptimismPortal.contract.Transact(opts, "finalizeWithdrawalTransaction", _nonce, _sender, _target, _value, _gasLimit, _data, _timestamp, _outputRootProof, _withdrawalProof) +// Solidity: function finalizeWithdrawalTransaction(uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _gasLimit, bytes _data, uint256 _l2Timestamp, (bytes32,bytes32,bytes32,bytes32) _outputRootProof, bytes _withdrawalProof) payable returns() +func (_OptimismPortal *OptimismPortalTransactor) FinalizeWithdrawalTransaction(opts *bind.TransactOpts, _nonce *big.Int, _sender common.Address, _target common.Address, _value *big.Int, _gasLimit *big.Int, _data []byte, _l2Timestamp *big.Int, _outputRootProof WithdrawalVerifierOutputRootProof, _withdrawalProof []byte) (*types.Transaction, error) { + return _OptimismPortal.contract.Transact(opts, "finalizeWithdrawalTransaction", _nonce, _sender, _target, _value, _gasLimit, _data, _l2Timestamp, _outputRootProof, _withdrawalProof) } // FinalizeWithdrawalTransaction is a paid mutator transaction binding the contract method 0xeecf1c36. // -// Solidity: function finalizeWithdrawalTransaction(uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _gasLimit, bytes _data, uint256 _timestamp, (bytes32,bytes32,bytes32,bytes32) _outputRootProof, bytes _withdrawalProof) returns() -func (_OptimismPortal *OptimismPortalSession) FinalizeWithdrawalTransaction(_nonce *big.Int, _sender common.Address, _target common.Address, _value *big.Int, _gasLimit *big.Int, _data []byte, _timestamp *big.Int, _outputRootProof WithdrawalVerifierOutputRootProof, _withdrawalProof []byte) (*types.Transaction, error) { - return _OptimismPortal.Contract.FinalizeWithdrawalTransaction(&_OptimismPortal.TransactOpts, _nonce, _sender, _target, _value, _gasLimit, _data, _timestamp, _outputRootProof, _withdrawalProof) +// Solidity: function finalizeWithdrawalTransaction(uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _gasLimit, bytes _data, uint256 _l2Timestamp, (bytes32,bytes32,bytes32,bytes32) _outputRootProof, bytes _withdrawalProof) payable returns() +func (_OptimismPortal *OptimismPortalSession) FinalizeWithdrawalTransaction(_nonce *big.Int, _sender common.Address, _target common.Address, _value *big.Int, _gasLimit *big.Int, _data []byte, _l2Timestamp *big.Int, _outputRootProof WithdrawalVerifierOutputRootProof, _withdrawalProof []byte) (*types.Transaction, error) { + return _OptimismPortal.Contract.FinalizeWithdrawalTransaction(&_OptimismPortal.TransactOpts, _nonce, _sender, _target, _value, _gasLimit, _data, _l2Timestamp, _outputRootProof, _withdrawalProof) } // FinalizeWithdrawalTransaction is a paid mutator transaction binding the contract method 0xeecf1c36. // -// Solidity: function finalizeWithdrawalTransaction(uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _gasLimit, bytes _data, uint256 _timestamp, (bytes32,bytes32,bytes32,bytes32) _outputRootProof, bytes _withdrawalProof) returns() -func (_OptimismPortal *OptimismPortalTransactorSession) FinalizeWithdrawalTransaction(_nonce *big.Int, _sender common.Address, _target common.Address, _value *big.Int, _gasLimit *big.Int, _data []byte, _timestamp *big.Int, _outputRootProof WithdrawalVerifierOutputRootProof, _withdrawalProof []byte) (*types.Transaction, error) { - return _OptimismPortal.Contract.FinalizeWithdrawalTransaction(&_OptimismPortal.TransactOpts, _nonce, _sender, _target, _value, _gasLimit, _data, _timestamp, _outputRootProof, _withdrawalProof) +// Solidity: function finalizeWithdrawalTransaction(uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _gasLimit, bytes _data, uint256 _l2Timestamp, (bytes32,bytes32,bytes32,bytes32) _outputRootProof, bytes _withdrawalProof) payable returns() +func (_OptimismPortal *OptimismPortalTransactorSession) FinalizeWithdrawalTransaction(_nonce *big.Int, _sender common.Address, _target common.Address, _value *big.Int, _gasLimit *big.Int, _data []byte, _l2Timestamp *big.Int, _outputRootProof WithdrawalVerifierOutputRootProof, _withdrawalProof []byte) (*types.Transaction, error) { + return _OptimismPortal.Contract.FinalizeWithdrawalTransaction(&_OptimismPortal.TransactOpts, _nonce, _sender, _target, _value, _gasLimit, _data, _l2Timestamp, _outputRootProof, _withdrawalProof) } // Receive is a paid mutator transaction binding the contract receive function. @@ -623,13 +623,14 @@ func (it *OptimismPortalWithdrawalFinalizedIterator) Close() error { // OptimismPortalWithdrawalFinalized represents a WithdrawalFinalized event raised by the OptimismPortal contract. type OptimismPortalWithdrawalFinalized struct { - Arg0 [32]byte - Raw types.Log // Blockchain specific contextual infos + Arg0 [32]byte + Success bool + Raw types.Log // Blockchain specific contextual infos } -// FilterWithdrawalFinalized is a free log retrieval operation binding the contract event 0x894485e328061b8d209b7dd043d2f613fc2892260497cadefac9a183962a990f. +// FilterWithdrawalFinalized is a free log retrieval operation binding the contract event 0xdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b. // -// Solidity: event WithdrawalFinalized(bytes32 indexed arg0) +// Solidity: event WithdrawalFinalized(bytes32 indexed arg0, bool success) func (_OptimismPortal *OptimismPortalFilterer) FilterWithdrawalFinalized(opts *bind.FilterOpts, arg0 [][32]byte) (*OptimismPortalWithdrawalFinalizedIterator, error) { var arg0Rule []interface{} @@ -644,9 +645,9 @@ func (_OptimismPortal *OptimismPortalFilterer) FilterWithdrawalFinalized(opts *b return &OptimismPortalWithdrawalFinalizedIterator{contract: _OptimismPortal.contract, event: "WithdrawalFinalized", logs: logs, sub: sub}, nil } -// WatchWithdrawalFinalized is a free log subscription operation binding the contract event 0x894485e328061b8d209b7dd043d2f613fc2892260497cadefac9a183962a990f. +// WatchWithdrawalFinalized is a free log subscription operation binding the contract event 0xdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b. // -// Solidity: event WithdrawalFinalized(bytes32 indexed arg0) +// Solidity: event WithdrawalFinalized(bytes32 indexed arg0, bool success) func (_OptimismPortal *OptimismPortalFilterer) WatchWithdrawalFinalized(opts *bind.WatchOpts, sink chan<- *OptimismPortalWithdrawalFinalized, arg0 [][32]byte) (event.Subscription, error) { var arg0Rule []interface{} @@ -686,9 +687,9 @@ func (_OptimismPortal *OptimismPortalFilterer) WatchWithdrawalFinalized(opts *bi }), nil } -// ParseWithdrawalFinalized is a log parse operation binding the contract event 0x894485e328061b8d209b7dd043d2f613fc2892260497cadefac9a183962a990f. +// ParseWithdrawalFinalized is a log parse operation binding the contract event 0xdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b. // -// Solidity: event WithdrawalFinalized(bytes32 indexed arg0) +// Solidity: event WithdrawalFinalized(bytes32 indexed arg0, bool success) func (_OptimismPortal *OptimismPortalFilterer) ParseWithdrawalFinalized(log types.Log) (*OptimismPortalWithdrawalFinalized, error) { event := new(OptimismPortalWithdrawalFinalized) if err := _OptimismPortal.contract.UnpackLog(event, "WithdrawalFinalized", log); err != nil { diff --git a/opnode/contracts/deposit/optimism_portal_deployed.go b/opnode/contracts/deposit/optimism_portal_deployed.go index 4f695d30..a1cad53d 100644 --- a/opnode/contracts/deposit/optimism_portal_deployed.go +++ b/opnode/contracts/deposit/optimism_portal_deployed.go @@ -2,4 +2,4 @@ // This file is a generated binding and any manual changes will be lost. package deposit -var OptimismPortalDeployedBin = "0x6080604052600436106100685760003560e01c8063e9e05c4211610043578063e9e05c421461015e578063eecf1c3614610171578063ff61cc931461019157600080fd5b80621c2ff6146100935780639bf62d82146100f1578063a14238e71461011e57600080fd5b3661008e5761008c33346175306000604051806020016040528060008152506101d3565b005b600080fd5b34801561009f57600080fd5b506100c77f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156100fd57600080fd5b506000546100c79073ffffffffffffffffffffffffffffffffffffffff1681565b34801561012a57600080fd5b5061014e610139366004611c41565b60016020526000908152604090205460ff1681565b60405190151581526020016100e8565b61008c61016c366004611cb2565b6101d3565b34801561017d57600080fd5b5061008c61018c366004611e18565b6102c2565b34801561019d57600080fd5b506101c57f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016100e8565b8180156101f5575073ffffffffffffffffffffffffffffffffffffffff851615155b1561022c576040517ff98844ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3332811461024d575033731111000000000000000000000000000000001111015b8573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f78231ae6eb73366f912bb1d64351601fb76344c537bbab635ce14d0f376f019534888888886040516102b2959493929190611f18565b60405180910390a3505050505050565b7f0000000000000000000000000000000000000000000000000000000000000000840142101561031e576040517fe4750a3000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401602060405180830381865afa1580156103ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103d09190611fb4565b9050610424846040805182356020828101919091528301358183015290820135606082810191909152820135608082015260009060a001604051602081830303815290604052805190602001209050919050565b811461045c576040517f9cc00b5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061046d8d8d8d8d8d8d8d610648565b905061047f818660400135868661068a565b6104b5576040517feb00eb2200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526001602081905260409091205460ff1615151415610504576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180600083815260200190815260200160002060006101000a81548160ff0219169083151502179055508b6000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008b73ffffffffffffffffffffffffffffffffffffffff168b8b908b8b60405161059b929190611fcd565b600060405180830381858888f193505050503d80600081146105d9576040519150601f19603f3d011682016040523d82523d6000602084013e6105de565b606091505b5050600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17815560405191925083917f894485e328061b8d209b7dd043d2f613fc2892260497cadefac9a183962a990f9190a25050505050505050505050505050565b6000878787878787876040516020016106679796959493929190611fdd565b604051602081830303815290604052805190602001209050979650505050505050565b6000808560016040516020016106aa929190918252602082015260400190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120908301819052925061077091016040516020818303038152906040526040518060400160405280600181526020017f010000000000000000000000000000000000000000000000000000000000000081525086868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508b925061077a915050565b9695505050505050565b60008061078686610794565b9050610770818686866107c6565b606081805190602001206040516020016107b091815260200190565b6040516020818303038152906040529050919050565b60008060006107d68786866107f7565b915091508180156107ec57506107ec86826108f1565b979650505050505050565b6000606060006108068561090d565b90506000806000610818848a89610a08565b8151929550909350915015808061082c5750815b610897576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f50726f76696465642070726f6f6620697320696e76616c69642e00000000000060448201526064015b60405180910390fd5b6000816108b357604051806020016040528060008152506108df565b6108df866108c2600188612099565b815181106108d2576108d26120b0565b6020026020010151610f25565b919b919a509098505050505050505050565b6000818051906020012083805190602001201490505b92915050565b6060600061091a83610f4f565b90506000815167ffffffffffffffff81111561093857610938611c83565b60405190808252806020026020018201604052801561097d57816020015b60408051808201909152606080825260208201528152602001906001900390816109565790505b50905060005b8251811015610a005760006109b08483815181106109a3576109a36120b0565b6020026020010151610f82565b905060405180604001604052808281526020016109cc83610f4f565b8152508383815181106109e1576109e16120b0565b60200260200101819052505080806109f8906120df565b915050610983565b509392505050565b60006060818080610a188761102c565b90506000869050600080610a3f604051806040016040528060608152602001606081525090565b60005b8c51811015610ee1578c8181518110610a5d57610a5d6120b0565b602002602001015191508284610a739190612118565b9350610a80600188612118565b965083610afe57815180516020909101208514610af9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e76616c696420726f6f742068617368000000000000000000000000000000604482015260640161088e565b610bef565b815151602011610b7a57815180516020909101208514610af9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f496e76616c6964206c6172676520696e7465726e616c20686173680000000000604482015260640161088e565b84610b8883600001516111af565b14610bef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f496e76616c696420696e7465726e616c206e6f64652068617368000000000000604482015260640161088e565b610bfb60106001612118565b8260200151511415610c74578551841415610c1557610ee1565b6000868581518110610c2957610c296120b0565b602001015160f81c60f81b60f81c9050600083602001518260ff1681518110610c5457610c546120b0565b60200260200101519050610c67816111d7565b9650600194505050610ecf565b60028260200151511415610e6d576000610c8d83611214565b9050600081600081518110610ca457610ca46120b0565b016020015160f81c90506000610cbb60028361215f565b610cc6906002612181565b90506000610cd7848360ff16611238565b90506000610ce58b8a611238565b90506000610cf3838361126e565b905060ff851660021480610d0a575060ff85166003145b15610d6057808351148015610d1f5750808251145b15610d3157610d2e818b612118565b99505b507f80000000000000000000000000000000000000000000000000000000000000009950610ee1945050505050565b60ff85161580610d73575060ff85166001145b15610de55782518114610daf57507f80000000000000000000000000000000000000000000000000000000000000009950610ee1945050505050565b610dd68860200151600181518110610dc957610dc96120b0565b60200260200101516111d7565b9a509750610ecf945050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f52656365697665642061206e6f6465207769746820616e20756e6b6e6f776e2060448201527f7072656669780000000000000000000000000000000000000000000000000000606482015260840161088e565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f526563656976656420616e20756e706172736561626c65206e6f64652e000000604482015260640161088e565b80610ed9816120df565b915050610a42565b507f8000000000000000000000000000000000000000000000000000000000000000841486610f108786611238565b909e909d50909b509950505050505050505050565b6020810151805160609161090791610f3f90600190612099565b815181106109a3576109a36120b0565b6040805180820182526000808252602091820152815180830190925282518252808301908201526060906109079061131a565b60606000806000610f928561154d565b919450925090506000816001811115610fad57610fad6121a4565b14611014576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c696420524c502062797465732076616c75652e0000000000000000604482015260640161088e565b61102385602001518484611954565b95945050505050565b606060008251600261103e91906121d3565b67ffffffffffffffff81111561105657611056611c83565b6040519080825280601f01601f191660200182016040528015611080576020820181803683370190505b50905060005b83518110156111a85760048482815181106110a3576110a36120b0565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016901c826110d88360026121d3565b815181106110e8576110e86120b0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350601084828151811061112b5761112b6120b0565b016020015161113d919060f81c61215f565b60f81b8261114c8360026121d3565b611157906001612118565b81518110611167576111676120b0565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806111a0816120df565b915050611086565b5092915050565b60006020825110156111c357506020015190565b818060200190518101906109079190611fb4565b600060606020836000015110156111f8576111f183611a33565b9050611204565b61120183610f82565b90505b61120d816111af565b9392505050565b606061090761123383602001516000815181106109a3576109a36120b0565b61102c565b6060825182106112575750604080516020810190915260008152610907565b61120d83838486516112699190612099565b611a3e565b6000805b8084511180156112825750808351115b8015611303575082818151811061129b5761129b6120b0565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168482815181106112da576112da6120b0565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b1561120d5780611312816120df565b915050611272565b60606000806113288461154d565b91935090915060019050816001811115611344576113446121a4565b146113ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420524c50206c6973742076616c75652e000000000000000000604482015260640161088e565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816113c45790505090506000835b8651811015611542576020821061148a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f50726f766964656420524c50206c6973742065786365656473206d6178206c6960448201527f7374206c656e6774682e00000000000000000000000000000000000000000000606482015260840161088e565b6000806114c76040518060400160405280858c600001516114ab9190612099565b8152602001858c602001516114c09190612118565b905261154d565b5091509150604051806040016040528083836114e39190612118565b8152602001848b602001516114f89190612118565b81525085858151811061150d5761150d6120b0565b6020908102919091010152611523600185612118565b935061152f8183612118565b6115399084612118565b925050506113f1565b508152949350505050565b6000806000808460000151116115bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f524c50206974656d2063616e6e6f74206265206e756c6c2e0000000000000000604482015260640161088e565b6020840151805160001a607f81116115e457600060016000945094509450505061194d565b60b7811161167a5760006115f9608083612099565b905080876000015111611668576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f496e76616c696420524c502073686f727420737472696e672e00000000000000604482015260640161088e565b6001955093506000925061194d915050565b60bf811161179d57600061168f60b783612099565b9050808760000151116116fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f496e76616c696420524c50206c6f6e6720737472696e67206c656e6774682e00604482015260640161088e565b600183015160208290036101000a90046117188183612118565b885111611781576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c696420524c50206c6f6e6720737472696e672e0000000000000000604482015260640161088e565b61178c826001612118565b965094506000935061194d92505050565b60f781116118325760006117b260c083612099565b905080876000015111611821576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420524c502073686f7274206c6973742e000000000000000000604482015260640161088e565b60019550935084925061194d915050565b600061183f60f783612099565b9050808760000151116118ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f496e76616c696420524c50206c6f6e67206c697374206c656e6774682e000000604482015260640161088e565b600183015160208290036101000a90046118c88183612118565b885111611931576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e76616c696420524c50206c6f6e67206c6973742e00000000000000000000604482015260640161088e565b61193c826001612118565b965094506001935061194d92505050565b9193909250565b606060008267ffffffffffffffff81111561197157611971611c83565b6040519080825280601f01601f19166020018201604052801561199b576020820181803683370190505b5090508051600014156119af57905061120d565b60006119bb8587612118565b90506020820160005b6119cf602087612210565b811015611a0657825182526119e5602084612118565b92506119f2602083612118565b9150806119fe816120df565b9150506119c4565b5060006001602087066020036101000a039050808251168119845116178252839450505050509392505050565b606061090782611c2b565b606081611a4c81601f612118565b1015611ab4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161088e565b82611abf8382612118565b1015611b27576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604482015260640161088e565b611b318284612118565b84511015611b9b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604482015260640161088e565b606082158015611bba5760405191506000825260208201604052611c22565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015611bf3578051835260209283019201611bdb565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6060610907826020015160008460000151611954565b600060208284031215611c5357600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611c7e57600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600080600060a08688031215611cca57600080fd5b611cd386611c5a565b945060208601359350604086013567ffffffffffffffff8082168214611cf857600080fd5b9093506060870135908115158214611d0f57600080fd5b90925060808701359080821115611d2557600080fd5b818801915088601f830112611d3957600080fd5b813581811115611d4b57611d4b611c83565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611d9157611d91611c83565b816040528281528b6020848701011115611daa57600080fd5b8260208601602083013760006020848301015280955050505050509295509295909350565b60008083601f840112611de157600080fd5b50813567ffffffffffffffff811115611df957600080fd5b602083019150836020828501011115611e1157600080fd5b9250929050565b60008060008060008060008060008060006101808c8e031215611e3a57600080fd5b8b359a50611e4a60208d01611c5a565b9950611e5860408d01611c5a565b985060608c0135975060808c0135965067ffffffffffffffff60a08d01351115611e8157600080fd5b611e918d60a08e01358e01611dcf565b909650945060c08c0135935060808c8e037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff20011215611ecf57600080fd5b60e08c01925067ffffffffffffffff6101608d01351115611eef57600080fd5b611f008d6101608e01358e01611dcf565b81935080925050509295989b509295989b9093969950565b85815260006020868184015267ffffffffffffffff86166040840152841515606084015260a0608084015283518060a085015260005b81811015611f6a5785810183015185820160c001528201611f4e565b81811115611f7c57600060c083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160c001979650505050505050565b600060208284031215611fc657600080fd5b5051919050565b8183823760009101908152919050565b878152600073ffffffffffffffffffffffffffffffffffffffff808916602084015280881660408401525085606083015284608083015260c060a08301528260c0830152828460e0840137600060e0848401015260e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116830101905098975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156120ab576120ab61206a565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156121115761211161206a565b5060010190565b6000821982111561212b5761212b61206a565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff83168061217257612172612130565b8060ff84160691505092915050565b600060ff821660ff84168082101561219b5761219b61206a565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561220b5761220b61206a565b500290565b60008261221f5761221f612130565b50049056fea164736f6c634300080a000a" +var OptimismPortalDeployedBin = "0x6080604052600436106100685760003560e01c8063e9e05c4211610043578063e9e05c421461015f578063eecf1c3614610172578063ff61cc931461018557600080fd5b80621c2ff6146100945780639bf62d82146100f2578063a14238e71461011f57600080fd5b3661008f5761008d3334620186a06000604051806020016040528060008152506101c7565b005b600080fd5b3480156100a057600080fd5b506100c87f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156100fe57600080fd5b506000546100c89073ffffffffffffffffffffffffffffffffffffffff1681565b34801561012b57600080fd5b5061014f61013a366004611e74565b60016020526000908152604090205460ff1681565b60405190151581526020016100e9565b61008d61016d366004611f34565b6101c7565b61008d61018036600461207e565b6102b6565b34801561019157600080fd5b506101b97f000000000000000000000000000000000000000000000000000000000000000081565b6040519081526020016100e9565b8180156101e9575073ffffffffffffffffffffffffffffffffffffffff851615155b15610220576040517ff98844ef00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33328114610241575033731111000000000000000000000000000000001111015b8573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f78231ae6eb73366f912bb1d64351601fb76344c537bbab635ce14d0f376f019534888888886040516102a69594939291906121e9565b60405180910390a3505050505050565b73ffffffffffffffffffffffffffffffffffffffff891630141561033b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f43616e6e6f742073656e64206d65737361676520746f2073656c662e0000000060448201526064015b60405180910390fd5b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae557906024016040805180830381865afa1580156103c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103ec9190612220565b90507f0000000000000000000000000000000000000000000000000000000000000000816020015161041e919061229e565b4211610486576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f50726f706f73616c206973206e6f74207965742066696e616c697a65642e00006044820152606401610332565b61049d610498368690038601866122b6565b6107c0565b8151146104d6576040517f9cc00b5b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600061051c8d8d8d8d8d8d8d8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061081c92505050565b905061056381866040013586868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061085b92505050565b610599576040517feb00eb2200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008181526001602081905260409091205460ff16151514156105e8576040517fae89945400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081815260016020819052604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909117905561062e89614e2061229e565b5a10156106bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f496e73756666696369656e742067617320746f2066696e616c697a652077697460448201527f6864726177616c2e0000000000000000000000000000000000000000000000006064820152608401610332565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8e16178155604080516020601f8b01819004810282018101909252898152610742918e918d918f9186918f908f908190840183828082843760009201919091525061092492505050565b50600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915082907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b906107a890841515815260200190565b60405180910390a25050505050505050505050505050565b600081600001518260200151836040015184606001516040516020016107ff949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b60008686868686866040516020016108399695949392919061231c565b6040516020818303038152906040528051906020012090509695505050505050565b604080516020810185905260009181018290528190606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828252805160209182012090830181905292506109199101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f010000000000000000000000000000000000000000000000000000000000000060208301529085876109af565b9150505b9392505050565b6000606060008060008661ffff1667ffffffffffffffff81111561094a5761094a611eb6565b6040519080825280601f01601f191660200182016040528015610974576020820181803683370190505b5090506000808751602089018b8e8ef191503d925086831115610995578692505b828152826000602083013e90999098509650505050505050565b6000806109bb866109d3565b90506109c981868686610a05565b9695505050505050565b606081805190602001206040516020016109ef91815260200190565b6040516020818303038152906040529050919050565b6000806000610a15878686610a36565b91509150818015610a2b5750610a2b8682610b2b565b979650505050505050565b600060606000610a4585610b47565b90506000806000610a57848a89610c42565b81519295509093509150158080610a6b5750815b610ad1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f50726f76696465642070726f6f6620697320696e76616c69642e0000000000006044820152606401610332565b600081610aed5760405180602001604052806000815250610b19565b610b1986610afc600188612373565b81518110610b0c57610b0c61238a565b602002602001015161115f565b919b919a509098505050505050505050565b6000818051906020012083805190602001201490505b92915050565b60606000610b5483611189565b90506000815167ffffffffffffffff811115610b7257610b72611eb6565b604051908082528060200260200182016040528015610bb757816020015b6040805180820190915260608082526020820152815260200190600190039081610b905790505b50905060005b8251811015610c3a576000610bea848381518110610bdd57610bdd61238a565b60200260200101516111bc565b90506040518060400160405280828152602001610c0683611189565b815250838381518110610c1b57610c1b61238a565b6020026020010181905250508080610c32906123b9565b915050610bbd565b509392505050565b60006060818080610c5287611266565b90506000869050600080610c79604051806040016040528060608152602001606081525090565b60005b8c5181101561111b578c8181518110610c9757610c9761238a565b602002602001015191508284610cad919061229e565b9350610cba60018861229e565b965083610d3857815180516020909101208514610d33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f496e76616c696420726f6f7420686173680000000000000000000000000000006044820152606401610332565b610e29565b815151602011610db457815180516020909101208514610d33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f496e76616c6964206c6172676520696e7465726e616c206861736800000000006044820152606401610332565b84610dc283600001516113e9565b14610e29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f496e76616c696420696e7465726e616c206e6f646520686173680000000000006044820152606401610332565b610e356010600161229e565b8260200151511415610eae578551841415610e4f5761111b565b6000868581518110610e6357610e6361238a565b602001015160f81c60f81b60f81c9050600083602001518260ff1681518110610e8e57610e8e61238a565b60200260200101519050610ea181611411565b9650600194505050611109565b600282602001515114156110a7576000610ec783611447565b9050600081600081518110610ede57610ede61238a565b016020015160f81c90506000610ef5600283612421565b610f00906002612443565b90506000610f11848360ff1661146b565b90506000610f1f8b8a61146b565b90506000610f2d83836114a1565b905060ff851660021480610f44575060ff85166003145b15610f9a57808351148015610f595750808251145b15610f6b57610f68818b61229e565b99505b507f8000000000000000000000000000000000000000000000000000000000000000995061111b945050505050565b60ff85161580610fad575060ff85166001145b1561101f5782518114610fe957507f8000000000000000000000000000000000000000000000000000000000000000995061111b945050505050565b61101088602001516001815181106110035761100361238a565b6020026020010151611411565b9a509750611109945050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f52656365697665642061206e6f6465207769746820616e20756e6b6e6f776e2060448201527f70726566697800000000000000000000000000000000000000000000000000006064820152608401610332565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f526563656976656420616e20756e706172736561626c65206e6f64652e0000006044820152606401610332565b80611113816123b9565b915050610c7c565b507f800000000000000000000000000000000000000000000000000000000000000084148661114a878661146b565b909e909d50909b509950505050505050505050565b60208101518051606091610b419161117990600190612373565b81518110610bdd57610bdd61238a565b604080518082018252600080825260209182015281518083019092528251825280830190820152606090610b419061154d565b606060008060006111cc85611780565b9194509250905060008160018111156111e7576111e7612466565b1461124e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c696420524c502062797465732076616c75652e00000000000000006044820152606401610332565b61125d85602001518484611b87565b95945050505050565b60606000825160026112789190612495565b67ffffffffffffffff81111561129057611290611eb6565b6040519080825280601f01601f1916602001820160405280156112ba576020820181803683370190505b50905060005b83518110156113e25760048482815181106112dd576112dd61238a565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016901c82611312836002612495565b815181106113225761132261238a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060108482815181106113655761136561238a565b0160200151611377919060f81c612421565b60f81b82611386836002612495565b61139190600161229e565b815181106113a1576113a161238a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350806113da816123b9565b9150506112c0565b5092915050565b60006020825110156113fd57506020015190565b81806020019051810190610b4191906124d2565b600060606020836000015110156114325761142b83611c66565b905061143e565b61143b836111bc565b90505b61091d816113e9565b6060610b416114668360200151600081518110610bdd57610bdd61238a565b611266565b60608251821061148a5750604080516020810190915260008152610b41565b61091d838384865161149c9190612373565b611c71565b6000805b8084511180156114b55750808351115b801561153657508281815181106114ce576114ce61238a565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191684828151811061150d5761150d61238a565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b1561091d5780611545816123b9565b9150506114a5565b606060008061155b84611780565b9193509091506001905081600181111561157757611577612466565b146115de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420524c50206c6973742076616c75652e0000000000000000006044820152606401610332565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816115f75790505090506000835b865181101561177557602082106116bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f50726f766964656420524c50206c6973742065786365656473206d6178206c6960448201527f7374206c656e6774682e000000000000000000000000000000000000000000006064820152608401610332565b6000806116fa6040518060400160405280858c600001516116de9190612373565b8152602001858c602001516116f3919061229e565b9052611780565b509150915060405180604001604052808383611716919061229e565b8152602001848b6020015161172b919061229e565b8152508585815181106117405761174061238a565b602090810291909101015261175660018561229e565b9350611762818361229e565b61176c908461229e565b92505050611624565b508152949350505050565b6000806000808460000151116117f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f524c50206974656d2063616e6e6f74206265206e756c6c2e00000000000000006044820152606401610332565b6020840151805160001a607f8111611817576000600160009450945094505050611b80565b60b781116118ad57600061182c608083612373565b90508087600001511161189b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f496e76616c696420524c502073686f727420737472696e672e000000000000006044820152606401610332565b60019550935060009250611b80915050565b60bf81116119d05760006118c260b783612373565b905080876000015111611931576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f496e76616c696420524c50206c6f6e6720737472696e67206c656e6774682e006044820152606401610332565b600183015160208290036101000a900461194b818361229e565b8851116119b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f496e76616c696420524c50206c6f6e6720737472696e672e00000000000000006044820152606401610332565b6119bf82600161229e565b9650945060009350611b8092505050565b60f78111611a655760006119e560c083612373565b905080876000015111611a54576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f496e76616c696420524c502073686f7274206c6973742e0000000000000000006044820152606401610332565b600195509350849250611b80915050565b6000611a7260f783612373565b905080876000015111611ae1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f496e76616c696420524c50206c6f6e67206c697374206c656e6774682e0000006044820152606401610332565b600183015160208290036101000a9004611afb818361229e565b885111611b64576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f496e76616c696420524c50206c6f6e67206c6973742e000000000000000000006044820152606401610332565b611b6f82600161229e565b9650945060019350611b8092505050565b9193909250565b606060008267ffffffffffffffff811115611ba457611ba4611eb6565b6040519080825280601f01601f191660200182016040528015611bce576020820181803683370190505b509050805160001415611be257905061091d565b6000611bee858761229e565b90506020820160005b611c026020876124eb565b811015611c395782518252611c1860208461229e565b9250611c2560208361229e565b915080611c31816123b9565b915050611bf7565b5060006001602087066020036101000a039050808251168119845116178252839450505050509392505050565b6060610b4182611e5e565b606081611c7f81601f61229e565b1015611ce7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610332565b82611cf2838261229e565b1015611d5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610332565b611d64828461229e565b84511015611dce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610332565b606082158015611ded5760405191506000825260208201604052611e55565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015611e26578051835260209283019201611e0e565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6060610b41826020015160008460000151611b87565b600060208284031215611e8657600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611eb157600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611f2c57611f2c611eb6565b604052919050565b600080600080600060a08688031215611f4c57600080fd5b611f5586611e8d565b94506020808701359450604087013567ffffffffffffffff8082168214611f7b57600080fd5b9094506060880135908115158214611f9257600080fd5b90935060808801359080821115611fa857600080fd5b818901915089601f830112611fbc57600080fd5b813581811115611fce57611fce611eb6565b611ffe847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611ee5565b91508082528a8482850101111561201457600080fd5b80848401858401376000848284010152508093505050509295509295909350565b60008083601f84011261204757600080fd5b50813567ffffffffffffffff81111561205f57600080fd5b60208301915083602082850101111561207757600080fd5b9250929050565b60008060008060008060008060008060006101808c8e0312156120a057600080fd5b8b359a506120b060208d01611e8d565b99506120be60408d01611e8d565b985060608c0135975060808c0135965067ffffffffffffffff60a08d013511156120e757600080fd5b6120f78d60a08e01358e01612035565b909650945060c08c0135935060808c8e037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2001121561213557600080fd5b60e08c01925067ffffffffffffffff6101608d0135111561215557600080fd5b6121668d6101608e01358e01612035565b81935080925050509295989b509295989b9093969950565b6000815180845260005b818110156121a457602081850181015186830182015201612188565b818111156121b6576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b85815284602082015267ffffffffffffffff84166040820152821515606082015260a060808201526000610a2b60a083018461217e565b60006040828403121561223257600080fd5b6040516040810181811067ffffffffffffffff8211171561225557612255611eb6565b604052825181526020928301519281019290925250919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156122b1576122b161226f565b500190565b6000608082840312156122c857600080fd5b6040516080810181811067ffffffffffffffff821117156122eb576122eb611eb6565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261236760c083018461217e565b98975050505050505050565b6000828210156123855761238561226f565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156123eb576123eb61226f565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600060ff831680612434576124346123f2565b8060ff84160691505092915050565b600060ff821660ff84168082101561245d5761245d61226f565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156124cd576124cd61226f565b500290565b6000602082840312156124e457600080fd5b5051919050565b6000826124fa576124fa6123f2565b50049056fea164736f6c634300080a000a" diff --git a/opnode/test/system_test.go b/opnode/test/system_test.go index 9958309c..35ce7894 100644 --- a/opnode/test/system_test.go +++ b/opnode/test/system_test.go @@ -178,7 +178,7 @@ func TestL2OutputSubmitter(t *testing.T) { require.Nil(t, err) require.Len(t, l2Output, 2) - require.Equal(t, l2Output[1][:], committedL2Output[:]) + require.Equal(t, l2Output[1][:], committedL2Output.OutputRoot[:]) break } diff --git a/ops/devnet-up.sh b/ops/devnet-up.sh index 6e59fa45..5d83c9b7 100755 --- a/ops/devnet-up.sh +++ b/ops/devnet-up.sh @@ -81,9 +81,17 @@ else echo "Contracts already deployed, skipping." fi +function get_deployed_bytecode() { + echo $(jq -r .deployedBytecode ./packages/contracts/artifacts/contracts/$1) +} + # Pull out the necessary bytecode/addresses from the artifacts/deployments. -WITHDRAWER_BYTECODE=$(jq -r .deployedBytecode < ./packages/contracts/artifacts/contracts/L2/Withdrawer.sol/Withdrawer.json) -L1_BLOCK_INFO_BYTECODE=$(jq -r .deployedBytecode < ./packages/contracts/artifacts/contracts/L2/L1Block.sol/L1Block.json) +L2_TO_L1_MESSAGE_PASSER_BYTECODE=$(get_deployed_bytecode L2/L2ToL1MessagePasser.sol/L2ToL1MessagePasser.json) +L2_CROSS_DOMAIN_MESSENGER_BYTECODE=$(get_deployed_bytecode L2/L2CrossDomainMessenger.sol/L2CrossDomainMessenger.json) +OPTIMISM_MINTABLE_TOKEN_FACTORY_BYTECODE=$(get_deployed_bytecode universal/OptimismMintableTokenFactory.sol/OptimismMintableTokenFactory.json) +L2_STANDARD_BRIDGE_BYTECODE=$(get_deployed_bytecode L2/L2StandardBridge.sol/L2StandardBridge.json) +L1_BLOCK_INFO_BYTECODE=$(get_deployed_bytecode L2/L1Block.sol/L1Block.json) + DEPOSIT_CONTRACT_ADDRESS=$(jq -r .address < ./packages/contracts/deployments/devnetL1/OptimismPortal.json) L2OO_ADDRESS=$(jq -r .address < ./packages/contracts/deployments/devnetL1/L2OutputOracle.json) @@ -91,8 +99,14 @@ L2OO_ADDRESS=$(jq -r .address < ./packages/contracts/deployments/devnetL1/L2Outp # since the replaced values will be the same. jq ". | .alloc.\"4200000000000000000000000000000000000015\".code = \"$L1_BLOCK_INFO_BYTECODE\"" < ./ops/genesis-l2.json | \ jq ". | .alloc.\"4200000000000000000000000000000000000015\".balance = \"0x0\"" | \ - jq ". | .alloc.\"4200000000000000000000000000000000000016\".code = \"$WITHDRAWER_BYTECODE\"" | \ - jq ". | .alloc.\"4200000000000000000000000000000000000016\".balance = \"0x0\"" | \ + jq ". | .alloc.\"4200000000000000000000000000000000000000\".code = \"$L2_TO_L1_MESSAGE_PASSER_BYTECODE\"" | \ + jq ". | .alloc.\"4200000000000000000000000000000000000000\".balance = \"0x0\"" | \ + jq ". | .alloc.\"4200000000000000000000000000000000000007\".code = \"$L2_CROSS_DOMAIN_MESSENGER_BYTECODE\"" | \ + jq ". | .alloc.\"4200000000000000000000000000000000000007\".balance = \"0x0\"" | \ + jq ". | .alloc.\"4200000000000000000000000000000000000012\".code = \"$OPTIMISM_MINTABLE_TOKEN_FACTORY_BYTECODE\"" | \ + jq ". | .alloc.\"4200000000000000000000000000000000000012\".balance = \"0x0\"" | \ + jq ". | .alloc.\"4200000000000000000000000000000000000010\".code = \"$L2_STANDARD_BRIDGE_BYTECODE\"" | \ + jq ". | .alloc.\"4200000000000000000000000000000000000010\".balance = \"0x0\"" | \ jq ". | .timestamp = \"$GENESIS_TIMESTAMP\" " > ./.devnet/genesis-l2.json # Bring up L2. diff --git a/packages/contracts/.gas-snapshot b/packages/contracts/.gas-snapshot index 8a288b6a..efc77046 100644 --- a/packages/contracts/.gas-snapshot +++ b/packages/contracts/.gas-snapshot @@ -1,61 +1,88 @@ -DepositFeedTest:test_depositTransaction_ContractCreationReverts() (gas: 9290) -DepositFeedTest:test_depositTransaction_NoValueContract() (gas: 24388) -DepositFeedTest:test_depositTransaction_NoValueEOA() (gas: 24734) -DepositFeedTest:test_depositTransaction_createWithZeroValueForContract() (gas: 24451) -DepositFeedTest:test_depositTransaction_createWithZeroValueForEOA() (gas: 24707) -DepositFeedTest:test_depositTransaction_withEthValueAndContractContractCreation() (gas: 31429) -DepositFeedTest:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 22859) -DepositFeedTest:test_depositTransaction_withEthValueFromContract() (gas: 31098) -DepositFeedTest:test_depositTransaction_withEthValueFromEOA() (gas: 31713) -L1BLockTest:test_basefee() (gas: 7531) -L1BLockTest:test_hash() (gas: 7530) -L1BLockTest:test_number() (gas: 7555) -L1BLockTest:test_sequenceNumber() (gas: 7577) -L1BLockTest:test_timestamp() (gas: 7587) -L1CrossDomainMessenger_Test:testCannot_pause() (gas: 10843) -L1CrossDomainMessenger_Test:test_blockAndUnblockSuccessfulMessage() (gas: 105412) -L1CrossDomainMessenger_Test:test_pause() (gas: 31793) -L1CrossDomainMessenger_Test:test_relayMessageBlockingAuth() (gas: 18542) -L1CrossDomainMessenger_Test:test_relayMessageInsideFraudProofWindow() (gas: 12305) -L1CrossDomainMessenger_Test:test_relayMessageSucceeds() (gas: 82425) -L1CrossDomainMessenger_Test:test_relayMessageToSystemContract() (gas: 30160) -L1CrossDomainMessenger_Test:test_relayRevertOnBlockedMessage() (gas: 55761) -L1CrossDomainMessenger_Test:test_relayShouldRevertIfPaused() (gas: 41560) -L1CrossDomainMessenger_Test:test_relayShouldRevertSendingSameMessageTwice() (gas: 188) -L1CrossDomainMessenger_Test:test_revertOnInvalidOutputRootProof() (gas: 16067) -L1CrossDomainMessenger_Test:test_sendMessage() (gas: 44041) -L1CrossDomainMessenger_Test:test_sendMessageTwice() (gas: 49061) -L1CrossDomainMessenger_Test:test_xDomainMessageSenderResets() (gas: 81565) -L1StandardBridge_Test:test_L1BridgeSetsPortalAndL2Bridge() (gas: 14825) -L2CrossDomainMessenger_Test:test_L2MessengerCallsTarget() (gas: 64095) -L2CrossDomainMessenger_Test:test_L2MessengerCannotCallL2MessagePasser() (gas: 42128) -L2CrossDomainMessenger_Test:test_L2MessengerCannotRelaySameMessageTwice() (gas: 67491) -L2CrossDomainMessenger_Test:test_L2MessengerCorrectL1Messenger() (gas: 9762) -L2CrossDomainMessenger_Test:test_L2MessengerRevertInvalidL1XDomainMessenger() (gas: 11535) -L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 122584) -L2CrossDomainMessenger_Test:test_L2MessengerSendSameMessageTwice() (gas: 162903) -L2CrossDomainMessenger_Test:test_L2MessengerXDomainMessageSenderReset() (gas: 69285) -L2CrossDomainMessenger_Test:test_L2MessengerxDomainMsgSender() (gas: 11159) -L2OutputOracleTest:testCannot_appendCurrentTimestamp() (gas: 18605) -L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 16724) -L2OutputOracleTest:testCannot_appendFutureTimestamp() (gas: 18642) -L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 16088) +CrossDomainHashing_Test:test_l2TransactionHash() (gas: 78639) +L1BLockTest:test_basefee() (gas: 7575) +L1BLockTest:test_hash() (gas: 7552) +L1BLockTest:test_number() (gas: 7651) +L1BLockTest:test_sequenceNumber() (gas: 7585) +L1BLockTest:test_timestamp() (gas: 7661) +L1CrossDomainMessenger_Test:testCannot_L1MessengerPause() (gas: 10909) +L1CrossDomainMessenger_Test:test_L1MessengerMessageVersion() (gas: 8343) +L1CrossDomainMessenger_Test:test_L1MessengerPause() (gas: 31882) +L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageSucceeds() (gas: 61062) +L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageToSystemContract() (gas: 44727) +L1CrossDomainMessenger_Test:test_L1MessengerRelayShouldRevertIfPaused() (gas: 41564) +L1CrossDomainMessenger_Test:test_L1MessengerSendMessage() (gas: 74867) +L1CrossDomainMessenger_Test:test_L1MessengerTwiceSendMessage() (gas: 66341) +L1CrossDomainMessenger_Test:test_L1MessengerXDomainSenderReverts() (gas: 10565) +L1CrossDomainMessenger_Test:test_L1MessengerxDomainMessageSenderResets() (gas: 58335) +L1StandardBridge_Test:test_depositERC20() (gas: 373162) +L1StandardBridge_Test:test_depositERC20To() (gas: 374939) +L1StandardBridge_Test:test_depositETH() (gas: 106067) +L1StandardBridge_Test:test_depositETHTo() (gas: 112908) +L1StandardBridge_Test:test_donateETH() (gas: 17500) +L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438858) +L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 47952) +L1StandardBridge_Test:test_initialize() (gas: 14863) +L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085) +L1StandardBridge_Test:test_onlyEOADepositETH() (gas: 30637) +L1StandardBridge_Test:test_onlyL2BridgeFinalizeERC20Withdrawal() (gas: 23521) +L1StandardBridge_Test:test_onlyPortalFinalizeERC20Withdrawal() (gas: 22853) +L1StandardBridge_Test:test_receive() (gas: 100261) +L2CrossDomainMessenger_Test:testCannot_L2MessengerPause() (gas: 10865) +L2CrossDomainMessenger_Test:test_L2MessengerMessageVersion() (gas: 8387) +L2CrossDomainMessenger_Test:test_L2MessengerPause() (gas: 31792) +L2CrossDomainMessenger_Test:test_L2MessengerRelayMessageSucceeds() (gas: 57407) +L2CrossDomainMessenger_Test:test_L2MessengerRelayMessageToSystemContract() (gas: 24523) +L2CrossDomainMessenger_Test:test_L2MessengerRelayShouldRevertIfPaused() (gas: 41511) +L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 116411) +L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133114) +L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10587) +L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54749) +L2OutputOracleTest:testCannot_appendCurrentTimestamp() (gas: 18627) +L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 16734) +L2OutputOracleTest:testCannot_appendFutureTimestamp() (gas: 18708) +L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 16458) L2OutputOracleTest:testCannot_appendUnexpectedTimestamp() (gas: 18893) -L2OutputOracleTest:testCannot_computePreHistoricalL2BlockNumber() (gas: 11048) -L2OutputOracleTest:testCannot_deleteL2Output_ifNotSequencer() (gas: 16287) -L2OutputOracleTest:testCannot_deleteL2Output_ifWrongOutput() (gas: 23243) -L2OutputOracleTest:test_appendingAnotherOutput() (gas: 47332) +L2OutputOracleTest:testCannot_computePreHistoricalL2BlockNumber() (gas: 11093) +L2OutputOracleTest:testCannot_deleteL2Output_ifNotSequencer() (gas: 18793) +L2OutputOracleTest:testCannot_deleteWrongL2Output() (gas: 77307) +L2OutputOracleTest:test_appendingAnotherOutput() (gas: 68605) L2OutputOracleTest:test_computeL2BlockNumber() (gas: 15003) -L2OutputOracleTest:test_deleteL2Output() (gas: 30647) -L2OutputOracleTest:test_getL2Output() (gas: 15071) -L2OutputOracleTest:test_latestBlockTimestamp() (gas: 9699) -L2OutputOracleTest:test_nextTimestamp() (gas: 12031) -L2OutputOracleTest_Constructor:test_constructor() (gas: 29173) -L2StandardBridge_Test:test_L2BridgeCorrectL1Bridge() (gas: 9726) -OptimismPortal_Test:test_receive_withEthValueFromEOA() (gas: 21979) -WithdrawalsRelay_finalizeWithdrawalTransaction_Test:test_cannotVerifyInvalidProof() (gas: 37418) -WithdrawalsRelay_finalizeWithdrawalTransaction_Test:test_cannotVerifyRecentWithdrawal() (gas: 33219) -WithdrawalsRelay_finalizeWithdrawalTransaction_Test:test_verifyWithdrawal() (gas: 190048) -WithdawerBurnTest:test_burn() (gas: 50276) -WithdrawerTestInitiateWithdrawal:test_initiateWithdrawal_fromContract() (gas: 71990) -WithdrawerTestInitiateWithdrawal:test_initiateWithdrawal_fromEOA() (gas: 72475) +L2OutputOracleTest:test_constructor() (gas: 33752) +L2OutputOracleTest:test_deleteL2Output() (gas: 64320) +L2OutputOracleTest:test_getL2Output() (gas: 74601) +L2OutputOracleTest:test_latestBlockTimestamp() (gas: 68377) +L2OutputOracleTest:test_nextTimestamp() (gas: 9236) +L2StandardBridge_Test:test_finalizeDeposit() (gas: 97520) +L2StandardBridge_Test:test_initialize() (gas: 14812) +L2StandardBridge_Test:test_receive() (gas: 137222) +L2StandardBridge_Test:test_withdraw() (gas: 353410) +L2StandardBridge_Test:test_withdrawTo() (gas: 354225) +L2ToL1MessagePasserTest:test_burn() (gas: 112001) +L2ToL1MessagePasserTest:test_initiateWithdrawal_fromContract() (gas: 67935) +L2ToL1MessagePasserTest:test_initiateWithdrawal_fromEOA() (gas: 74851) +OptimismMintableTokenFactory_Test:test_bridge() (gas: 9850) +OptimismMintableTokenFactory_Test:test_burn() (gas: 52791) +OptimismMintableTokenFactory_Test:test_burnRevertsFromNotBridge() (gas: 13211) +OptimismMintableTokenFactory_Test:test_l1Token() (gas: 9779) +OptimismMintableTokenFactory_Test:test_l2Bridge() (gas: 9768) +OptimismMintableTokenFactory_Test:test_mint() (gas: 65732) +OptimismMintableTokenFactory_Test:test_mintRevertsFromNotBridge() (gas: 13213) +OptimismMintableTokenFactory_Test:test_remoteToken() (gas: 9762) +OptimismMintableTokenFactory_Test:test_bridge() (gas: 9772) +OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1106538) +OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2193987) +OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9374) +OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696) +OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 11302) +OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 9399) +OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 24797) +OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 19657) +OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 24478) +OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 24824) +OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 24497) +OptimismPortal_Test:test_depositTransaction_createWithZeroValueForEOA() (gas: 24841) +OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreation() (gas: 31519) +OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 22949) +OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 31188) +OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 31804) +OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 26565) diff --git a/packages/contracts/contracts/L1/L1CrossDomainMessenger.sol b/packages/contracts/contracts/L1/L1CrossDomainMessenger.sol new file mode 100644 index 00000000..78634938 --- /dev/null +++ b/packages/contracts/contracts/L1/L1CrossDomainMessenger.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { + Lib_PredeployAddresses +} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; +import { OptimismPortal } from "./OptimismPortal.sol"; +import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol"; + +/** + * @title L1CrossDomainMessenger + * @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages + * from L2 onto L1. + * This contract should be deployed behind an upgradable proxy + */ +contract L1CrossDomainMessenger is CrossDomainMessenger { + /************* + * Variables * + *************/ + + /** + * @notice Address of the OptimismPortal. + */ + OptimismPortal public portal; + + /******************** + * Public Functions * + ********************/ + + /** + * @notice Initialize the L1CrossDomainMessenger + * @param _portal The OptimismPortal + */ + function initialize(OptimismPortal _portal) external { + portal = _portal; + + address[] memory blockedSystemAddresses = new address[](1); + blockedSystemAddresses[0] = address(this); + + _initialize(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, blockedSystemAddresses); + } + + /********************** + * Internal Functions * + **********************/ + + /** + * @notice Ensure that the L1CrossDomainMessenger can only be called + * by the OptimismPortal and the L2 sender is the L2CrossDomainMessenger. + */ + function _isSystemMessageSender() internal view override returns (bool) { + return msg.sender == address(portal) && portal.l2Sender() == otherMessenger; + } + + /** + * @notice Sending a message in the L1CrossDomainMessenger involves + * depositing through the OptimismPortal. + */ + function _sendMessage( + address _to, + uint64 _gasLimit, + uint256 _value, + bytes memory _data + ) internal override { + portal.depositTransaction{ value: _value }(_to, _value, _gasLimit, false, _data); + } +} diff --git a/packages/contracts/contracts/L1/L1StandardBridge.sol b/packages/contracts/contracts/L1/L1StandardBridge.sol new file mode 100644 index 00000000..7ac3b80b --- /dev/null +++ b/packages/contracts/contracts/L1/L1StandardBridge.sol @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { + Lib_PredeployAddresses +} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; +import { StandardBridge } from "../universal/StandardBridge.sol"; + +/** + * @title L1StandardBridge + * @dev The L1 ETH and ERC20 Bridge is a contract which stores deposited L1 funds and standard + * tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits + * and listening to it for newly finalized withdrawals. + */ +contract L1StandardBridge is StandardBridge { + /********** + * Events * + **********/ + + event ETHDepositInitiated( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); + + event ETHWithdrawalFinalized( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); + + event ERC20DepositInitiated( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event ERC20WithdrawalFinalized( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + /******************** + * Public Functions * + ********************/ + + /** + * @dev initialize the L1StandardBridge with the address of the + * messenger in the same domain + */ + function initialize(address payable _messenger) public { + _initialize(_messenger, payable(Lib_PredeployAddresses.L2_STANDARD_BRIDGE)); + } + + /** + * @dev Get the address of the corresponding L2 bridge contract. + * This is a legacy getter, provided for backwards compatibility. + * @return Address of the corresponding L2 bridge contract. + */ + function l2TokenBridge() external returns (address) { + return address(otherBridge); + } + + /** + * @dev Deposit an amount of the ETH to the caller's balance on L2. + * @param _minGasLimit limit required to complete the deposit on L2. + * @param _data Optional data to forward to L2. This data is provided + * solely as a convenience for external contracts. Aside from enforcing a maximum + * length, these contracts provide no guarantees about its content. + */ + function depositETH(uint32 _minGasLimit, bytes calldata _data) external payable onlyEOA { + _initiateETHDeposit(msg.sender, msg.sender, _minGasLimit, _data); + } + + /** + * @dev Deposit an amount of ETH to a recipient's balance on L2. + * @param _to L2 address to credit the withdrawal to. + * @param _minGasLimit Gas limit required to complete the deposit on L2. + * @param _data Optional data to forward to L2. This data is provided + * solely as a convenience for external contracts. Aside from enforcing a maximum + * length, these contracts provide no guarantees about its content. + */ + function depositETHTo( + address _to, + uint32 _minGasLimit, + bytes calldata _data + ) external payable { + _initiateETHDeposit(msg.sender, _to, _minGasLimit, _data); + } + + /** + * @dev deposit an amount of the ERC20 to the caller's balance on L2. + * @param _l1Token Address of the L1 ERC20 we are depositing + * @param _l2Token Address of the L1 respective L2 ERC20 + * @param _amount Amount of the ERC20 to deposit + * @param _minGasLimit limit required to complete the deposit on L2. + * @param _data Optional data to forward to L2. This data is provided + * solely as a convenience for external contracts. Aside from enforcing a maximum + * length, these contracts provide no guarantees about its content. + */ + function depositERC20( + address _l1Token, + address _l2Token, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) external virtual onlyEOA { + _initiateERC20Deposit( + _l1Token, + _l2Token, + msg.sender, + msg.sender, + _amount, + _minGasLimit, + _data + ); + } + + /** + * @dev deposit an amount of ERC20 to a recipient's balance on L2. + * @param _l1Token Address of the L1 ERC20 we are depositing + * @param _l2Token Address of the L1 respective L2 ERC20 + * @param _to L2 address to credit the withdrawal to. + * @param _amount Amount of the ERC20 to deposit. + * @param _minGasLimit Gas limit required to complete the deposit on L2. + * @param _data Optional data to forward to L2. This data is provided + * solely as a convenience for external contracts. Aside from enforcing a maximum + * length, these contracts provide no guarantees about its content. + */ + function depositERC20To( + address _l1Token, + address _l2Token, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) external virtual { + _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _minGasLimit, _data); + } + + function finalizeETHWithdrawal( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable onlyOtherBridge { + emit ETHWithdrawalFinalized(_from, _to, _amount, _data); + finalizeBridgeETH(_from, _to, _amount, _data); + } + + /** + * @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the + * L1 ERC20 token. + * This call will fail if the initialized withdrawal from L2 has not been finalized. + * + * @param _l1Token Address of L1 token to finalizeWithdrawal for. + * @param _l2Token Address of L2 token where withdrawal was initiated. + * @param _from L2 address initiating the transfer. + * @param _to L1 address to credit the withdrawal to. + * @param _amount Amount of the ERC20 to deposit. + * @param _data Data provided by the sender on L2. This data is provided + * solely as a convenience for external contracts. Aside from enforcing a maximum + * length, these contracts provide no guarantees about its content. + */ + function finalizeERC20Withdrawal( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external onlyOtherBridge { + emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); + finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _data); + } + + /********************** + * Internal Functions * + **********************/ + + function _initiateETHDeposit( + address _from, + address _to, + uint32 _minGasLimit, + bytes memory _data + ) internal { + emit ETHDepositInitiated(_from, _to, msg.value, _data); + _initiateBridgeETH(_from, _to, msg.value, _minGasLimit, _data); + } + + function _initiateERC20Deposit( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) internal { + emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _data); + _initiateBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _minGasLimit, _data); + } +} diff --git a/packages/contracts/contracts/L1/L2OutputOracle.sol b/packages/contracts/contracts/L1/L2OutputOracle.sol index d1bb474a..c3703ce4 100644 --- a/packages/contracts/contracts/L1/L2OutputOracle.sol +++ b/packages/contracts/contracts/L1/L2OutputOracle.sol @@ -1,14 +1,14 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; -/* Library Imports */ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; /** * @title L2OutputOracle - * @notice + * @notice The L2 state is committed to in this contract + * The payable keyword is used on appendL2Output to save gas on the msg.value check. + * This contract should be deployed behind an upgradable proxy */ -// The payable keyword is used on appendL2Output to save gas on the msg.value check. // slither-disable-next-line locked-ether contract L2OutputOracle is Ownable { /********** @@ -16,10 +16,18 @@ contract L2OutputOracle is Ownable { **********/ /// @notice Emitted when an output is appended. - event l2OutputAppended(bytes32 indexed _l2Output, uint256 indexed _l2timestamp); + event l2OutputAppended( + bytes32 indexed _l2Output, + uint256 indexed _l1Timestamp, + uint256 indexed _l2timestamp + ); /// @notice Emitted when an output is deleted. - event l2OutputDeleted(bytes32 indexed _l2Output, uint256 indexed _l2timestamp); + event l2OutputDeleted( + bytes32 indexed _l2Output, + uint256 indexed _l1Timestamp, + uint256 indexed _l2timestamp + ); /********************** * Contract Variables * @@ -41,7 +49,16 @@ contract L2OutputOracle is Ownable { uint256 public latestBlockTimestamp; /// @notice A mapping from L2 timestamps to the output root for the block with that timestamp. - mapping(uint256 => bytes32) internal l2Outputs; + mapping(uint256 => OutputProposal) internal l2Outputs; + + /// @notice OutputProposal represents a commitment to the L2 state. + /// The timestamp is the L1 timestamp that the output root is posted. + /// This timestamp is used to verify that the finalization period + /// has passed since the output root was submitted. + struct OutputProposal { + bytes32 outputRoot; + uint256 timestamp; + } /*************** * Constructor * @@ -72,10 +89,13 @@ contract L2OutputOracle is Ownable { SUBMISSION_INTERVAL = _submissionInterval; L2_BLOCK_TIME = _l2BlockTime; - l2Outputs[_startingBlockTimestamp] = _genesisL2Output; // solhint-disable not-rely-on-time + // solhint-disable-next-line not-rely-on-time + l2Outputs[_startingBlockTimestamp] = OutputProposal(_genesisL2Output, block.timestamp); HISTORICAL_TOTAL_BLOCKS = _historicalTotalBlocks; - latestBlockTimestamp = _startingBlockTimestamp; // solhint-disable not-rely-on-time - STARTING_BLOCK_TIMESTAMP = _startingBlockTimestamp; // solhint-disable not-rely-on-time + // solhint-disable-next-line not-rely-on-time + latestBlockTimestamp = _startingBlockTimestamp; + // solhint-disable-next-line not-rely-on-time + STARTING_BLOCK_TIMESTAMP = _startingBlockTimestamp; _transferOwnership(sequencer); } @@ -119,21 +139,30 @@ contract L2OutputOracle is Ownable { ); } - l2Outputs[_l2timestamp] = _l2Output; + l2Outputs[_l2timestamp] = OutputProposal(_l2Output, block.timestamp); latestBlockTimestamp = _l2timestamp; - emit l2OutputAppended(_l2Output, _l2timestamp); + emit l2OutputAppended(_l2Output, block.timestamp, _l2timestamp); } /** * @notice Deletes the most recent output. - * @param _l2Output The value of the most recent output. Used to prevent erroneously deleting - * the wrong root + * @param _proposal Represents the output proposal to delete */ - function deleteL2Output(bytes32 _l2Output) external onlyOwner { - bytes32 outputToDelete = l2Outputs[latestBlockTimestamp]; - require(_l2Output == outputToDelete, "Can only delete the most recent output."); - emit l2OutputDeleted(outputToDelete, latestBlockTimestamp); + function deleteL2Output(OutputProposal memory _proposal) external onlyOwner { + OutputProposal memory outputToDelete = l2Outputs[latestBlockTimestamp]; + + require( + _proposal.outputRoot == outputToDelete.outputRoot, + "Can only delete the most recent output." + ); + require(_proposal.timestamp == outputToDelete.timestamp, ""); + + emit l2OutputDeleted( + outputToDelete.outputRoot, + outputToDelete.timestamp, + latestBlockTimestamp + ); delete l2Outputs[latestBlockTimestamp]; latestBlockTimestamp = latestBlockTimestamp - SUBMISSION_INTERVAL; @@ -147,11 +176,11 @@ contract L2OutputOracle is Ownable { } /** - * @notice Returns the L2 output root given a target L2 block timestamp. Returns 0 if none is - * found. + * @notice Returns the L2 output proposal given a target L2 block timestamp. + * Returns a null output proposal if none is found. * @param _l2Timestamp The L2 block timestamp of the target block. */ - function getL2Output(uint256 _l2Timestamp) external view returns (bytes32) { + function getL2Output(uint256 _l2Timestamp) external view returns (OutputProposal memory) { return l2Outputs[_l2Timestamp]; } diff --git a/packages/contracts/contracts/L1/OptimismPortal.sol b/packages/contracts/contracts/L1/OptimismPortal.sol index d3fb9b0c..a0656cde 100644 --- a/packages/contracts/contracts/L1/OptimismPortal.sol +++ b/packages/contracts/contracts/L1/OptimismPortal.sol @@ -1,32 +1,111 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; -/* Inherited Imports */ -import { DepositFeed } from "./abstracts/DepositFeed.sol"; -import { WithdrawalsRelay } from "./abstracts/WithdrawalsRelay.sol"; - -/* Interactions Imports */ import { L2OutputOracle } from "./L2OutputOracle.sol"; +import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; +import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; +import { ExcessivelySafeCall } from "../libraries/ExcessivelySafeCall.sol"; /** * @title OptimismPortal - * @notice The OptimismPortal is a contract on L1 used to deposit and withdraw between L2 and L1. - * The OptimismPortal must inherit from both the DepositFeed and WithdrawalsRelay as it holds the - * pool of ETH which is deposited to and withdrawn from L2. Aside from affecting the ETH balance, - * the deposit and withdrawal codepaths should be independent from one another. + * This contract should be deployed behind an upgradable proxy. */ -contract OptimismPortal is DepositFeed, WithdrawalsRelay { +contract OptimismPortal { + /********** + * Errors * + **********/ + + /** + * @notice Error emitted when the output root proof is invalid. + */ + error InvalidOutputRootProof(); + + /** + * @notice Error emitted when the withdrawal inclusion proof is invalid. + */ + error InvalidWithdrawalInclusionProof(); + + /** + * @notice Error emitted when a withdrawal has already been finalized. + */ + error WithdrawalAlreadyFinalized(); + + /** + * @notice Error emitted on deposits which create a new contract with a non-zero target. + */ + error NonZeroCreationTarget(); + + /********** + * Events * + **********/ + + /** + * @notice Emitted when a Transaction is deposited from L1 to L2. The parameters of this + * event are read by the rollup node and used to derive deposit transactions on L2. + */ + event TransactionDeposited( + address indexed from, + address indexed to, + uint256 mint, + uint256 value, + uint64 gasLimit, + bool isCreation, + bytes data + ); + + /** + * @notice Emitted when a withdrawal is finalized + */ + event WithdrawalFinalized(bytes32 indexed, bool success); + + /************* + * Constants * + *************/ + + /** + * @notice Value used to reset the l2Sender, this is more efficient than setting it to zero. + */ + address internal constant DEFAULT_L2_SENDER = 0x000000000000000000000000000000000000dEaD; + + /************* + * Variables * + *************/ + + /** + * @notice Minimum time that must elapse before a withdrawal can be finalized. + */ + uint256 public immutable FINALIZATION_PERIOD; + + /** + * @notice Address of the L2OutputOracle. + */ + L2OutputOracle public immutable L2_ORACLE; + + /** + * @notice Public variable which can be used to read the address of the L2 account which + * initated the withdrawal. Can also be used to determine whether or not execution is occuring + * downstream of a call to finalizeWithdrawalTransaction(). + */ + address public l2Sender = DEFAULT_L2_SENDER; + + /** + * @notice A list of withdrawal hashes which have been successfully finalized. + * Used for replay protection. + */ + mapping(bytes32 => bool) public finalizedWithdrawals; + /*************** * Constructor * ***************/ - constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriod) - WithdrawalsRelay(_l2Oracle, _finalizationPeriod) - {} + constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriod) { + L2_ORACLE = _l2Oracle; + FINALIZATION_PERIOD = _finalizationPeriod; + } - /********************** - * External Functions * - **********************/ + /******************** + * Public Functions * + ********************/ /** * @notice Accepts value so that users can send ETH directly to this contract and @@ -35,6 +114,132 @@ contract OptimismPortal is DepositFeed, WithdrawalsRelay { * depositTransaction() function directly. */ receive() external payable { - depositTransaction(msg.sender, msg.value, 30000, false, bytes("")); + depositTransaction(msg.sender, msg.value, 100000, false, bytes("")); + } + + /** + * @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in + * deriving deposit transactions. + * @param _to The L2 destination address. + * @param _value The ETH value to send in the deposit transaction. + * @param _gasLimit The L2 gasLimit. + * @param _isCreation Whether or not the transaction should be contract creation. + * @param _data The input data. + */ + function depositTransaction( + address _to, + uint256 _value, + uint64 _gasLimit, + bool _isCreation, + bytes memory _data + ) public payable { + // Differentiate between sending to address(0) + // and creating a contract + if (_isCreation && _to != address(0)) { + revert NonZeroCreationTarget(); + } + + address from = msg.sender; + // Transform the from-address to its alias if the caller is a contract. + if (msg.sender != tx.origin) { + from = AddressAliasHelper.applyL1ToL2Alias(msg.sender); + } + + emit TransactionDeposited(from, _to, msg.value, _value, _gasLimit, _isCreation, _data); + } + + /** + * @notice Finalizes a withdrawal transaction. + * @param _nonce Nonce for the provided message. + * @param _sender Message sender address on L2. + * @param _target Target address on L1. + * @param _value ETH to send to the target. + * @param _gasLimit Gas to be forwarded to the target. + * @param _data Data to send to the target. + * @param _l2Timestamp L2 timestamp of the outputRoot. + * @param _outputRootProof Inclusion proof of the withdrawer contracts storage root. + * @param _withdrawalProof Inclusion proof for the given withdrawal in the withdrawer contract. + */ + function finalizeWithdrawalTransaction( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes calldata _data, + uint256 _l2Timestamp, + WithdrawalVerifier.OutputRootProof calldata _outputRootProof, + bytes calldata _withdrawalProof + ) external payable { + // Prevent reentrency + require(_target != address(this), "Cannot send message to self."); + + // Get the output root. + L2OutputOracle.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2Timestamp); + + // Ensure that enough time has passed since the proposal was submitted + // before allowing a withdrawal. A fault proof should be submitted + // before this check is allowed to pass. + require( + block.timestamp > proposal.timestamp + FINALIZATION_PERIOD, + "Proposal is not yet finalized." + ); + + // Verify that the output root can be generated with the elements in the proof. + if (proposal.outputRoot != WithdrawalVerifier._deriveOutputRoot(_outputRootProof)) { + revert InvalidOutputRootProof(); + } + + // Verify that the hash of the withdrawal transaction's arguments are included in the + // storage hash of the withdrawer contract. + bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash( + _nonce, + _sender, + _target, + _value, + _gasLimit, + _data + ); + + // Verify proof that a withdrawal on L2 was initated + if ( + WithdrawalVerifier._verifyWithdrawalInclusion( + withdrawalHash, + _outputRootProof.withdrawerStorageRoot, + _withdrawalProof + ) == false + ) { + revert InvalidWithdrawalInclusionProof(); + } + + // Check that this withdrawal has not already been finalized. + if (finalizedWithdrawals[withdrawalHash] == true) { + revert WithdrawalAlreadyFinalized(); + } + + // Set the withdrawal as finalized + finalizedWithdrawals[withdrawalHash] = true; + + // Save enough gas so that the call cannot use up all of the gas + require(gasleft() >= _gasLimit + 20000, "Insufficient gas to finalize withdrawal."); + + // Set the l2Sender so that other contracts can know which account + // on L2 is making the withdrawal + l2Sender = _sender; + // Make the call and ensure that a contract cannot out of gas + // us by returning a huge amount of data + (bool success, ) = ExcessivelySafeCall.excessivelySafeCall( + _target, + _gasLimit, + _value, + 0, + _data + ); + // Be sure to reset the l2Sender + l2Sender = DEFAULT_L2_SENDER; + + // All withdrawals are immediately finalized. Replayability can + // be achieved through contracts built on top of this contract + emit WithdrawalFinalized(withdrawalHash, success); } } diff --git a/packages/contracts/contracts/L1/abstracts/DepositFeed.sol b/packages/contracts/contracts/L1/abstracts/DepositFeed.sol deleted file mode 100644 index 46b040d0..00000000 --- a/packages/contracts/contracts/L1/abstracts/DepositFeed.sol +++ /dev/null @@ -1,73 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity 0.8.10; - -import { IDepositFeed } from "./IDepositFeed.sol"; - -/* Library Imports */ -import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; - -/** - * @title DepositFeed - * @notice Implements the logic for depositing from L1 to L2. - */ -abstract contract DepositFeed is IDepositFeed { - /********** - * Errors * - **********/ - - /** - * @notice Error emitted on deposits which create a new contract with a non-zero target. - */ - error NonZeroCreationTarget(); - - /********** - * Events * - **********/ - - /** - * @notice Emitted when a Transaction is deposited from L1 to L2. The parameters of this - * event are read by the rollup node and used to derive deposit transactions on L2. - */ - event TransactionDeposited( - address indexed from, - address indexed to, - uint256 mint, - uint256 value, - uint64 gasLimit, - bool isCreation, - bytes data - ); - - /********************** - * External Functions * - **********************/ - - /** - * @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in - * deriving deposit transactions. - * @param _to The L2 destination address. - * @param _value The ETH value to send in the deposit transaction. - * @param _gasLimit The L2 gasLimit. - * @param _isCreation Whether or not the transaction should be contract creation. - * @param _data The input data. - */ - function depositTransaction( - address _to, - uint256 _value, - uint64 _gasLimit, - bool _isCreation, - bytes memory _data - ) public payable { - if (_isCreation && _to != address(0)) { - revert NonZeroCreationTarget(); - } - - address from = msg.sender; - // Transform the from-address to its alias if the caller is a contract. - if (msg.sender != tx.origin) { - from = AddressAliasHelper.applyL1ToL2Alias(msg.sender); - } - - emit TransactionDeposited(from, _to, msg.value, _value, _gasLimit, _isCreation, _data); - } -} diff --git a/packages/contracts/contracts/L1/abstracts/IDepositFeed.sol b/packages/contracts/contracts/L1/abstracts/IDepositFeed.sol deleted file mode 100644 index e61662a3..00000000 --- a/packages/contracts/contracts/L1/abstracts/IDepositFeed.sol +++ /dev/null @@ -1,11 +0,0 @@ -pragma solidity ^0.8.10; - -interface IDepositFeed { - function depositTransaction( - address _to, - uint256 _value, - uint64 _gasLimit, - bool _isCreation, - bytes memory _data - ) external payable; -} diff --git a/packages/contracts/contracts/L1/abstracts/WithdrawalsRelay.sol b/packages/contracts/contracts/L1/abstracts/WithdrawalsRelay.sol deleted file mode 100644 index 22309967..00000000 --- a/packages/contracts/contracts/L1/abstracts/WithdrawalsRelay.sol +++ /dev/null @@ -1,154 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity 0.8.10; - -/* Interactions Imports */ -import { L2OutputOracle } from "../L2OutputOracle.sol"; - -/* Library Imports */ -import { WithdrawalVerifier } from "../../libraries/Lib_WithdrawalVerifier.sol"; - -/** - * @title WithdrawalsRelay - * @notice The WithdrawalsRelay is inherited by the OptimismPortal on L1, and faciliates finalizing - * withdrawals between L2 and L1. - */ -abstract contract WithdrawalsRelay { - /********** - * Errors * - **********/ - - /// @notice Error emitted when attempting to finalize a withdrawal too early. - error NotYetFinal(); - - /// @notice Error emitted when the output root proof is invalid. - error InvalidOutputRootProof(); - - /// @notice Error emitted when the withdrawal inclusion proof is invalid. - error InvalidWithdrawalInclusionProof(); - - /// @notice Error emitted when a withdrawal has already been finalized. - error WithdrawalAlreadyFinalized(); - - /********** - * Events * - **********/ - - /// @notice Emitted when a withdrawal is finalized - event WithdrawalFinalized(bytes32 indexed); - - /// @notice Value used to reset the l2Sender, this is more efficient than setting it to zero. - address internal constant DEFAULT_L2_SENDER = 0x000000000000000000000000000000000000dEaD; - - /********************** - * Contract Variables * - **********************/ - - /// @notice Minimum time that must elapse before a withdrawal can be finalized. - uint256 public immutable FINALIZATION_PERIOD; - - /// @notice Address of the L2OutputOracle. - L2OutputOracle public immutable L2_ORACLE; - - /** - * @notice Public variable which can be used to read the address of the L2 account which - * initated the withdrawal. Can also be used to determine whether or not execution is occuring - * downstream of a call to finalizeWithdrawalTransaction(). - */ - address public l2Sender = DEFAULT_L2_SENDER; - - /** - * @notice A list of withdrawal hashes which have been successfully finalized. - * Used for replay protection. - */ - mapping(bytes32 => bool) public finalizedWithdrawals; - - /*************** - * Constructor * - ***************/ - - constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriod) { - L2_ORACLE = _l2Oracle; - FINALIZATION_PERIOD = _finalizationPeriod; - } - - /********************** - * External Functions * - **********************/ - - /** - * @notice Finalizes a withdrawal transaction. - * @param _nonce Nonce for the provided message. - * @param _sender Message sender address on L2. - * @param _target Target address on L1. - * @param _value ETH to send to the target. - * @param _gasLimit Gas to be forwarded to the target. - * @param _data Data to send to the target. - * @param _timestamp L2 timestamp of the outputRoot. - * @param _outputRootProof Inclusion proof of the withdrawer contracts storage root. - * @param _withdrawalProof Inclusion proof for the given withdrawal in the withdrawer contract. - */ - function finalizeWithdrawalTransaction( - uint256 _nonce, - address _sender, - address _target, - uint256 _value, - uint256 _gasLimit, - bytes calldata _data, - uint256 _timestamp, - WithdrawalVerifier.OutputRootProof calldata _outputRootProof, - bytes calldata _withdrawalProof - ) external { - // Check that the timestamp is sufficiently finalized. - // The timestamp corresponds to a particular L2 output, - // so it is safe to be passed in by a user. - unchecked { - if (block.timestamp < _timestamp + FINALIZATION_PERIOD) { - revert NotYetFinal(); - } - } - - // Get the output root. - bytes32 outputRoot = L2_ORACLE.getL2Output(_timestamp); - - // Verify that the output root can be generated with the elements in the proof. - if (outputRoot != WithdrawalVerifier._deriveOutputRoot(_outputRootProof)) { - revert InvalidOutputRootProof(); - } - - // Verify that the hash of the withdrawal transaction's arguments are included in the - // storage hash of the withdrawer contract. - bytes32 withdrawalHash = WithdrawalVerifier._deriveWithdrawalHash( - _nonce, - _sender, - _target, - _value, - _gasLimit, - _data - ); - if ( - WithdrawalVerifier._verifyWithdrawalInclusion( - withdrawalHash, - _outputRootProof.withdrawerStorageRoot, - _withdrawalProof - ) == false - ) { - revert InvalidWithdrawalInclusionProof(); - } - - // Check that this withdrawal has not already been finalized. - if (finalizedWithdrawals[withdrawalHash] == true) { - revert WithdrawalAlreadyFinalized(); - } - finalizedWithdrawals[withdrawalHash] = true; - - l2Sender = _sender; - // Make the call. - (bool s, ) = _target.call{ value: _value, gas: _gasLimit }(_data); - s; // Silence the compiler's "Return value of low-level calls not used" warning. - l2Sender = DEFAULT_L2_SENDER; - - // All withdrawals are immediately finalized. If the ability to replay a transaction is - // required, that support can be provided in external contracts. - emit WithdrawalFinalized(withdrawalHash); - } -} diff --git a/packages/contracts/contracts/L1/messaging/IL1CrossDomainMessenger.sol b/packages/contracts/contracts/L1/messaging/IL1CrossDomainMessenger.sol deleted file mode 100644 index b3ef8e0c..00000000 --- a/packages/contracts/contracts/L1/messaging/IL1CrossDomainMessenger.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -/* Library Imports */ -import { Lib_OVMCodec } from "@eth-optimism/contracts/libraries/codec/Lib_OVMCodec.sol"; - -/* Interface Imports */ -import { - ICrossDomainMessenger -} from "@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol"; - -/** - * @title IL1CrossDomainMessenger - */ -interface IL1CrossDomainMessenger is ICrossDomainMessenger { - /******************* - * Data Structures * - *******************/ - - struct L2MessageInclusionProof { - bytes32 stateRoot; - Lib_OVMCodec.ChainBatchHeader stateRootBatchHeader; - Lib_OVMCodec.ChainInclusionProof stateRootProof; - bytes stateTrieWitness; - bytes storageTrieWitness; - } - - /******************** - * Public Functions * - ********************/ - - /** - * Relays a cross domain message to a contract. - * @param _target Target contract address. - * @param _sender Message sender address. - * @param _message Message to send to the target. - * @param _messageNonce Nonce for the provided message. - */ - function relayMessage( - address _target, - address _sender, - bytes memory _message, - uint256 _messageNonce - ) external; -} diff --git a/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol b/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol deleted file mode 100644 index 910880a5..00000000 --- a/packages/contracts/contracts/L1/messaging/L1CrossDomainMessenger.sol +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -// solhint-disable max-line-length -/* Library Imports */ -import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; -import { Lib_OVMCodec } from "@eth-optimism/contracts/libraries/codec/Lib_OVMCodec.sol"; -import { - Lib_SecureMerkleTrie -} from "@eth-optimism/contracts/libraries/trie/Lib_SecureMerkleTrie.sol"; -import { - Lib_DefaultValues -} from "@eth-optimism/contracts/libraries/constants/Lib_DefaultValues.sol"; -import { - Lib_PredeployAddresses -} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; -import { - Lib_CrossDomainUtils -} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol"; - -/* Interface Imports */ -import { IL1CrossDomainMessenger } from "./IL1CrossDomainMessenger.sol"; -import { OptimismPortal } from "../OptimismPortal.sol"; - -/* External Imports */ -import { - OwnableUpgradeable -} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import { - PausableUpgradeable -} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; -import { - ReentrancyGuardUpgradeable -} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; - -// solhint-enable max-line-length - -/** - * @title L1CrossDomainMessenger - * @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages - * from L2 onto L1. In the event that a message sent from L1 to L2 is rejected for exceeding the L2 - * epoch gas limit, it can be resubmitted via this contract's replay function. - * - */ -contract L1CrossDomainMessenger is - IL1CrossDomainMessenger, - OwnableUpgradeable, - PausableUpgradeable, - ReentrancyGuardUpgradeable -{ - /********** - * Events * - **********/ - - event MessageBlocked(bytes32 indexed _xDomainCalldataHash); - - event MessageAllowed(bytes32 indexed _xDomainCalldataHash); - - /********************** - * Contract Variables * - **********************/ - OptimismPortal public optimismPortal; - address public l2CrossDomainMessenger; - - // Bedrock upgrade note: the nonce must be initialized to greater than the last value of - // CanonicalTransactionChain.queueElements.length. Otherwise it will be possible to have - // messages which cannot be relayed on L2. - uint256 public messageNonce; - - mapping(bytes32 => bool) public blockedMessages; - mapping(bytes32 => bool) public relayedMessages; - mapping(bytes32 => bool) public successfulMessages; - - address internal xDomainMsgSender = Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER; - - /*************** - * Constructor * - ***************/ - - /** - * This contract is intended to be behind a delegate proxy. - * We still need to set this value in initialize(). - */ - constructor() {} - - /******************** - * Public Functions * - ********************/ - - /** - * @param _optimismPortal Address of the OptimismPortal. - */ - function initialize(OptimismPortal _optimismPortal, address _l2CrossDomainMessenger) - external - initializer - { - require( - address(optimismPortal) == address(0), - "L1CrossDomainMessenger already intialized." - ); - optimismPortal = _optimismPortal; - l2CrossDomainMessenger = _l2CrossDomainMessenger; - xDomainMsgSender = Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER; - - // Initialize upgradable OZ contracts - __Context_init_unchained(); // Context is a dependency for both Ownable and Pausable - __Ownable_init_unchained(); - __Pausable_init_unchained(); - __ReentrancyGuard_init_unchained(); - } - - /** - * Pause relaying. - */ - function pause() external onlyOwner { - _pause(); - } - - /** - * Block a message. - * @param _xDomainCalldataHash Hash of the message to block. - */ - function blockMessage(bytes32 _xDomainCalldataHash) external onlyOwner { - blockedMessages[_xDomainCalldataHash] = true; - emit MessageBlocked(_xDomainCalldataHash); - } - - /** - * Allow a message. - * @param _xDomainCalldataHash Hash of the message to block. - */ - function allowMessage(bytes32 _xDomainCalldataHash) external onlyOwner { - blockedMessages[_xDomainCalldataHash] = false; - emit MessageAllowed(_xDomainCalldataHash); - } - - /** - * Get the xDomainMessageSender - */ - function xDomainMessageSender() external view returns (address) { - require( - xDomainMsgSender != Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER, - "xDomainMessageSender is not set" - ); - return xDomainMsgSender; - } - - /** - * Sends a cross domain message to the target messenger. - * This function faciliates L1 to L2 communication. - * @param _target Target contract address. - * @param _message Message to send to the target. - * @param _gasLimit Gas limit for the provided message. - */ - function sendMessage( - address _target, - bytes memory _message, - uint32 _gasLimit - ) external { - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - _target, - msg.sender, - _message, - messageNonce - ); - - emit SentMessage(_target, msg.sender, _message, messageNonce, _gasLimit); - - _sendXDomainMessage(xDomainCalldata, _gasLimit); - } - - /** - * Relays a cross domain message to a contract. - * This function faciliates L2 to L1 communication. - * Calls WithdrawalsRelay.finalizeWithdrawalTransaction - * @inheritdoc IL1CrossDomainMessenger - */ - function relayMessage( - address _target, - address _sender, - bytes memory _message, - uint256 _messageNonce - ) external nonReentrant whenNotPaused { - require( - msg.sender == address(optimismPortal), - "Messages must be relayed by first calling the Optimism Portal" - ); - require( - optimismPortal.l2Sender() == l2CrossDomainMessenger, - "Message must be sent from the L2 Cross Domain Messenger" - ); - - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - _target, - _sender, - _message, - _messageNonce - ); - - bytes32 xDomainCalldataHash = keccak256(xDomainCalldata); - - require( - successfulMessages[xDomainCalldataHash] == false, - "Provided message has already been received." - ); - - require( - blockedMessages[xDomainCalldataHash] == false, - "Provided message has been blocked." - ); - - require( - _target != address(optimismPortal), - "Cannot send L2->L1 messages to L1 system contracts." - ); - - xDomainMsgSender = _sender; - // slither-disable-next-line reentrancy-no-eth, reentrancy-events, reentrancy-benign - (bool success, ) = _target.call(_message); - // slither-disable-next-line reentrancy-benign - xDomainMsgSender = Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER; - - // Mark the message as received if the call was successful. Ensures that a message can be - // relayed multiple times in the case that the call reverted. - if (success == true) { - // slither-disable-next-line reentrancy-no-eth - successfulMessages[xDomainCalldataHash] = true; - // slither-disable-next-line reentrancy-events - emit RelayedMessage(xDomainCalldataHash); - } else { - // slither-disable-next-line reentrancy-events - emit FailedRelayedMessage(xDomainCalldataHash); - } - - // Store an identifier that can be used to prove that the given message was relayed by some - // user. Gives us an easy way to pay relayers for their work. - bytes32 relayId = keccak256(abi.encodePacked(xDomainCalldata, msg.sender, block.number)); - // slither-disable-next-line reentrancy-benign - relayedMessages[relayId] = true; - } - - /********************** - * Internal Functions * - **********************/ - - /** - * Sends a cross domain message. To preserve backwards compatibility, - * the gasLimit is a uint256 so that the function selector stays the same. - * TODO: revert on overflow when converting to uint64? - * @param _message Message to send. - * @param _gasLimit L2 gas limit for the message. - */ - function _sendXDomainMessage(bytes memory _message, uint256 _gasLimit) internal { - optimismPortal.depositTransaction( - Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, - 0, - uint64(_gasLimit), - false, - _message - ); - } -} diff --git a/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol b/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol deleted file mode 100644 index 2cb22988..00000000 --- a/packages/contracts/contracts/L1/messaging/L1StandardBridge.sol +++ /dev/null @@ -1,272 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -/* Interface Imports */ -import { IL1StandardBridge } from "@eth-optimism/contracts/L1/messaging/IL1StandardBridge.sol"; -import { IL1ERC20Bridge } from "@eth-optimism/contracts/L1/messaging/IL1ERC20Bridge.sol"; -import { IL2ERC20Bridge } from "@eth-optimism/contracts/L2/messaging/IL2ERC20Bridge.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { OptimismPortal } from "../OptimismPortal.sol"; - -/* Library Imports */ -import { - Lib_PredeployAddresses -} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; -import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -/** - * @title L1StandardBridge - * @dev The L1 ETH and ERC20 Bridge is a contract which stores deposited L1 funds and standard - * tokens that are in use on L2. It synchronizes a corresponding L2 Bridge, informing it of deposits - * and listening to it for newly finalized withdrawals. - * - */ -contract L1StandardBridge is IL1StandardBridge { - using SafeERC20 for IERC20; - - /******************************** - * External Contract References * - ********************************/ - - OptimismPortal public optimismPortal; - address public l2TokenBridge; - - // Maps L1 token to L2 token to balance of the L1 token deposited - mapping(address => mapping(address => uint256)) public deposits; - - /*************** - * Constructor * - ***************/ - - // This contract lives behind a proxy, so the constructor parameters will go unused. - constructor() {} - - /****************** - * Initialization * - ******************/ - - /** - * @param _optimismPortal OptimismPortal address. - * @param _l2TokenBridge L2 standard bridge address. - */ - // slither-disable-next-line external-function - function initialize(OptimismPortal _optimismPortal, address _l2TokenBridge) public { - require(address(optimismPortal) == address(0), "Contract has already been initialized."); - optimismPortal = _optimismPortal; - l2TokenBridge = _l2TokenBridge; - } - - /************** - * Depositing * - **************/ - - /** @dev Modifier requiring sender to be EOA. This check could be bypassed by a malicious - * contract via initcode, but it takes care of the user error we want to avoid. - */ - modifier onlyEOA() { - // Used to stop deposits from contracts (avoid accidentally lost tokens) - require(!Address.isContract(msg.sender), "Account not EOA"); - _; - } - - /** - * @dev This function can be called with no data - * to deposit an amount of ETH to the caller's balance on L2. - * Since the receive function doesn't take data, a conservative - * default amount is forwarded to L2. - */ - receive() external payable onlyEOA { - _initiateETHDeposit(msg.sender, msg.sender, 200_000, bytes("")); - } - - /** - * @inheritdoc IL1StandardBridge - */ - function depositETH(uint32 _l2Gas, bytes calldata _data) external payable onlyEOA { - _initiateETHDeposit(msg.sender, msg.sender, _l2Gas, _data); - } - - /** - * @inheritdoc IL1StandardBridge - */ - function depositETHTo( - address _to, - uint32 _l2Gas, - bytes calldata _data - ) external payable { - _initiateETHDeposit(msg.sender, _to, _l2Gas, _data); - } - - /** - * @dev Performs the logic for deposits by storing the ETH and informing the L2 ETH Gateway of - * the deposit. - * @param _from Account to pull the deposit from on L1. - * @param _to Account to give the deposit to on L2. - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. - */ - function _initiateETHDeposit( - address _from, - address _to, - uint32 _l2Gas, - bytes memory _data - ) internal { - emit ETHDepositInitiated(_from, _to, msg.value, _data); - - // Send calldata into L2 - optimismPortal.depositTransaction{ value: msg.value }(_to, msg.value, _l2Gas, false, _data); - } - - /** - * @inheritdoc IL1ERC20Bridge - */ - function depositERC20( - address _l1Token, - address _l2Token, - uint256 _amount, - uint32 _l2Gas, - bytes calldata _data - ) external virtual onlyEOA { - _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, msg.sender, _amount, _l2Gas, _data); - } - - /** - * @inheritdoc IL1ERC20Bridge - */ - function depositERC20To( - address _l1Token, - address _l2Token, - address _to, - uint256 _amount, - uint32 _l2Gas, - bytes calldata _data - ) external virtual { - _initiateERC20Deposit(_l1Token, _l2Token, msg.sender, _to, _amount, _l2Gas, _data); - } - - /** - * @dev Performs the logic for deposits by informing the L2 Deposited Token - * contract of the deposit and calling a handler to lock the L1 funds. (e.g. transferFrom) - * - * @param _l1Token Address of the L1 ERC20 we are depositing - * @param _l2Token Address of the L1 respective L2 ERC20 - * @param _from Account to pull the deposit from on L1 - * @param _to Account to give the deposit to on L2 - * @param _amount Amount of the ERC20 to deposit. - * @param _l2Gas Gas limit required to complete the deposit on L2. - * @param _data Optional data to forward to L2. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. - */ - function _initiateERC20Deposit( - address _l1Token, - address _l2Token, - address _from, - address _to, - uint256 _amount, - uint32 _l2Gas, - bytes calldata _data - ) internal { - // When a deposit is initiated on L1, the L1 Bridge transfers the funds to itself for future - // withdrawals. safeTransferFrom also checks if the contract has code, so this will fail if - // from is an EOA or address(0). - // slither-disable-next-line reentrancy-events, reentrancy-benign - IERC20(_l1Token).safeTransferFrom(_from, address(this), _amount); - - // Construct calldata for _l2Token.finalizeDeposit(_to, _amount) - bytes memory message = abi.encodeWithSelector( - IL2ERC20Bridge.finalizeDeposit.selector, - _l1Token, - _l2Token, - _from, - _to, - _amount, - _data - ); - - // slither-disable-next-line reentrancy-benign - deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] + _amount; - - // slither-disable-next-line reentrancy-events - emit ERC20DepositInitiated(_l1Token, _l2Token, _from, _to, _amount, _data); - - // Send calldata into L2 - optimismPortal.depositTransaction( - Lib_PredeployAddresses.L2_STANDARD_BRIDGE, - 0, - _l2Gas, - false, - message - ); - } - - /************************* - * Cross-chain Functions * - *************************/ - - /** - * @notice Ensures that the caller is the portal, and that it has the l2Sender value - * set to the address of the L2 Token Bridge. - */ - modifier onlyL2Bridge() { - require( - msg.sender == address(optimismPortal), - "Messages must be relayed by first calling the Optimism Portal" - ); - require( - optimismPortal.l2Sender() == l2TokenBridge, - "Message must be sent from the L2 Token Bridge" - ); - _; - } - - /** - * @inheritdoc IL1StandardBridge - */ - function finalizeETHWithdrawal( - address _from, - address _to, - uint256 _amount, - bytes calldata _data - ) external onlyL2Bridge { - emit ETHWithdrawalFinalized(_from, _to, _amount, _data); - - (bool success, ) = _to.call{ value: _amount }(new bytes(0)); - require(success, "TransferHelper::safeTransferETH: ETH transfer failed"); - } - - /** - * @inheritdoc IL1ERC20Bridge - */ - function finalizeERC20Withdrawal( - address _l1Token, - address _l2Token, - address _from, - address _to, - uint256 _amount, - bytes calldata _data - ) external onlyL2Bridge { - deposits[_l1Token][_l2Token] = deposits[_l1Token][_l2Token] - _amount; - emit ERC20WithdrawalFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); - - // When a withdrawal is finalized on L1, the L1 Bridge transfers the funds to the withdrawer - IERC20(_l1Token).safeTransfer(_to, _amount); - } - - /***************************** - * Temporary - Migrating ETH * - *****************************/ - - /** - * @dev Adds ETH balance to the account. This is meant to allow for ETH - * to be migrated from an old gateway to a new gateway. - * NOTE: This is left for one upgrade only so we are able to receive the migrated ETH from the - * old contract - */ - // Bedrock upgrade note: we may use this if this contract gets merged into the portal, - // or even if we just start using a new proxy. - function donateETH() external payable {} -} diff --git a/packages/contracts/contracts/L2/Burner.sol b/packages/contracts/contracts/L2/Burner.sol index f922f0a2..36b2ef1c 100644 --- a/packages/contracts/contracts/L2/Burner.sol +++ b/packages/contracts/contracts/L2/Burner.sol @@ -1,6 +1,11 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; +/** + * @title Burner + * @dev This contract is used to remove ETH from + * the L2 circulating supply as it is withdrawn. + */ contract Burner { constructor() payable { selfdestruct(payable(address(this))); diff --git a/packages/contracts/contracts/L2/IWithdrawer.sol b/packages/contracts/contracts/L2/IWithdrawer.sol deleted file mode 100644 index d2f3f9b9..00000000 --- a/packages/contracts/contracts/L2/IWithdrawer.sol +++ /dev/null @@ -1,25 +0,0 @@ -pragma solidity ^0.8.0; - -interface IWithdrawer { - event WithdrawalInitiated( - uint256 indexed nonce, - address indexed sender, - address indexed target, - uint256 value, - uint256 gasLimit, - bytes data - ); - event WithdrawerBalanceBurnt(uint256 indexed amount); - - function burn() external; - - function initiateWithdrawal( - address _target, - uint256 _gasLimit, - bytes memory _data - ) external payable; - - function nonce() external view returns (uint256); - - function withdrawals(bytes32) external view returns (bool); -} diff --git a/packages/contracts/contracts/L2/L1Block.sol b/packages/contracts/contracts/L2/L1Block.sol index a25011dc..8b6b5fc4 100644 --- a/packages/contracts/contracts/L2/L1Block.sol +++ b/packages/contracts/contracts/L2/L1Block.sol @@ -3,21 +3,56 @@ pragma solidity 0.8.10; /** * @title L1Block + * @dev This is an L2 predeploy contract that holds values from the L1 + * chain. It can only be updated by a special account that has no private + * key managed by the L2 system. Transactions sent to this contract can + * be thought of as "L2 system transactions". */ contract L1Block { /** - * Only the Depositor account may call setL1BlockValues(). + * @notice Only the Depositor account may call setL1BlockValues(). */ error OnlyDepositor(); + /** + * @notice The depositor account is a special account that sends + * transactions to this contract. + */ address public constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001; + /** + * @notice The latest L1 block number known by the L2 system + */ uint64 public number; + + /** + * @notice The latest L1 timestamp known by the L2 system + */ uint64 public timestamp; + + /** + * @notice The latest L1 basefee + */ uint256 public basefee; + + /** + * @notice The latest L1 blockhash + */ bytes32 public hash; + + /** + * @notice The number of L2 blocks in the same epoch + */ uint64 public sequenceNumber; + /** + * @notice Sets the L1 values + * @param _number L1 blocknumber + * @param _timestamp L1 timestamp + * @param _basefee L1 basefee + * @param _hash L1 blockhash + * @param _sequenceNumber Number of L2 blocks since epoch start + */ function setL1BlockValues( uint64 _number, uint64 _timestamp, diff --git a/packages/contracts/contracts/L2/L2CrossDomainMessenger.sol b/packages/contracts/contracts/L2/L2CrossDomainMessenger.sol new file mode 100644 index 00000000..7a713a91 --- /dev/null +++ b/packages/contracts/contracts/L2/L2CrossDomainMessenger.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; +import { + Lib_PredeployAddresses +} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; +import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol"; +import { L2ToL1MessagePasser } from "./L2ToL1MessagePasser.sol"; + +contract L2CrossDomainMessenger is CrossDomainMessenger { + /******************** + * Public Functions * + ********************/ + + /** + * @notice initialize the L2CrossDomainMessenger by giving + * it the address of the L1CrossDomainMessenger on L1 + */ + function initialize(address _l1CrossDomainMessenger) external { + address[] memory blockedSystemAddresses = new address[](2); + blockedSystemAddresses[0] = address(this); + blockedSystemAddresses[1] = Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER; + + _initialize(_l1CrossDomainMessenger, blockedSystemAddresses); + } + + /********************** + * Internal Functions * + **********************/ + + /** + * @notice Only the L1CrossDomainMessenger can call the + * L2CrossDomainMessenger + */ + function _isSystemMessageSender() internal view override returns (bool) { + return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == otherMessenger; + } + + /** + * @notice Sending a message from L2 to L1 involves calling the L2ToL1MessagePasser + * where it stores in a storage slot a commitment to the message being + * sent to L1. A proof is then verified against that storage slot on L1. + */ + function _sendMessage( + address _to, + uint64 _gasLimit, + uint256 _value, + bytes memory _data + ) internal override { + L2ToL1MessagePasser(payable(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER)) + .initiateWithdrawal{ value: _value }(_to, _gasLimit, _data); + } +} diff --git a/packages/contracts/contracts/L2/L2StandardBridge.sol b/packages/contracts/contracts/L2/L2StandardBridge.sol new file mode 100644 index 00000000..578a9c98 --- /dev/null +++ b/packages/contracts/contracts/L2/L2StandardBridge.sol @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { + Lib_PredeployAddresses +} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; +import { StandardBridge } from "../universal/StandardBridge.sol"; +import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol"; + +/** + * @title L2StandardBridge + * @dev This contract is an L2 predeploy that is responsible for facilitating + * deposits of tokens from L1 to L2. + * TODO: ensure that this has 1:1 backwards compatibility + */ +contract L2StandardBridge is StandardBridge { + /********** + * Events * + **********/ + + event WithdrawalInitiated( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event DepositFinalized( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event DepositFailed( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + /******************** + * Public Functions * + ********************/ + + /** + * @notice Initialize the L2StandardBridge. This must only be callable + * once. `_initialize` ensures this. + */ + function initialize(address payable _otherBridge) public { + _initialize(payable(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER), _otherBridge); + } + + /** + * @notice Withdraw tokens to self on L1 + * @param _l2Token The L2 token address to withdraw + * @param _amount The amount of L2 token to withdraw + * @param _minGasLimit The min gas limit in the withdrawing call + * @param _data Additional calldata to pass along + */ + function withdraw( + address _l2Token, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) external payable virtual { + _initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _minGasLimit, _data); + } + + /** + * @notice Withdraw tokens to an address on L1 + * @param _l2Token The L2 token address to withdraw + * @param _to The L1 account to withdraw to + * @param _amount The amount of L2 token to withdraw + * @param _minGasLimit The min gas limit in the withdrawing call + * @param _data Additional calldata to pass along + */ + function withdrawTo( + address _l2Token, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) external payable virtual { + _initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _minGasLimit, _data); + } + + /** + * @notice Finalize the L1 to L2 deposit. This should only be callable by + * a deposit through the L1StandardBridge. + * @param _l1Token The L1 token address + * @param _l2Token The corresponding L2 token address + * @param _from The sender of the tokens + * @param _to The recipient of the tokens + * @param _amount The amount of tokens + * @param _data Additional calldata + */ + function finalizeDeposit( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable virtual { + // Check to see if the bridge is being used to deposit ETH. + // The `msg.value` must match the `_amount` to prevent + // ETH from getting stuck in the contract + if ( + _l1Token == address(0) && + _l2Token == Lib_PredeployAddresses.OVM_ETH && + msg.value == _amount + ) { + // An ETH deposit is being made via the Token Bridge. + // We simply forward it on. If this call fails, ETH will be stuck, but the L1Bridge + // uses onlyEOA on the receive function, so anyone sending to a contract knows + // what they are doing. + finalizeBridgeETH(_from, _to, _amount, _data); + emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); + } else if ( + _isOptimismMintable(_l2Token, _l1Token) + ) // Check the target token is compliant and + // verify the deposited token on L1 matches the L2 deposited token representation here + // slither-disable-next-line reentrancy-events + { + // When a deposit is finalized, we credit the account on L2 with the same amount of + // tokens. + // slither-disable-next-line reentrancy-events + finalizeBridgeERC20(_l2Token, _l1Token, _from, _to, _amount, _data); + // slither-disable-next-line reentrancy-events + emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); + } else { + // Either the L2 token which is being deposited-into disagrees about the correct address + // of its L1 token, or does not support the correct interface. + // This should only happen if there is a malicious L2 token, or if a user somehow + // specified the wrong L2 token address to deposit into. + // In either case, we stop the process here and construct a withdrawal + // message so that users can get their funds out in some cases. + // There is no way to prevent malicious token contracts altogether, but this does limit + // user error and mitigate some forms of malicious contract behavior. + emit DepositFailed(_l1Token, _l2Token, _from, _to, _amount, _data); + + // Withdraw ETH in the case that the user submitted a bad ETH + // deposit to prevent ETH from getting stuck + // TODO: can this be wrapped into _initiateWithdrawal? + // need to handle using `msg.value` here instead of `_value` + if (_l2Token == Lib_PredeployAddresses.OVM_ETH) { + _initiateBridgeETH(_from, _to, msg.value, 0, _data); + } else { + _initiateBridgeERC20(_l2Token, _l1Token, _from, _to, _amount, 0, _data); + } + } + } + + /********************** + * Internal Functions * + **********************/ + + /** + * @notice Handle withdrawals, taking into account the legacy form of ETH + * when it was represented as an ERC20 at the OVM_ETH contract. + * TODO: require(msg.value == _value) for OVM_ETH case? + */ + function _initiateWithdrawal( + address _l2Token, + address _from, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) internal { + address l1Token = OptimismMintableERC20(_l2Token).l1Token(); + emit WithdrawalInitiated(l1Token, _l2Token, msg.sender, _to, _amount, _data); + if (_l2Token == Lib_PredeployAddresses.OVM_ETH) { + _initiateBridgeETH(_from, _to, _amount, _minGasLimit, _data); + } else { + _initiateBridgeERC20(_l2Token, l1Token, _from, _to, _amount, _minGasLimit, _data); + } + } +} diff --git a/packages/contracts/contracts/L2/Withdrawer.sol b/packages/contracts/contracts/L2/L2ToL1MessagePasser.sol similarity index 62% rename from packages/contracts/contracts/L2/Withdrawer.sol rename to packages/contracts/contracts/L2/L2ToL1MessagePasser.sol index 0d7147c3..99eeb21e 100644 --- a/packages/contracts/contracts/L2/Withdrawer.sol +++ b/packages/contracts/contracts/L2/L2ToL1MessagePasser.sol @@ -1,19 +1,14 @@ -//SPDX-License-Identifier: MIT -pragma solidity 0.8.10; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; -/* Library Imports */ -import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; - -/* Interaction imports */ import { Burner } from "./Burner.sol"; /** - * @title Withdrawer - * @notice The Withdrawer contract facilitates sending both ETH value and data from L2 to L1. - * It is predeployed in the L2 state at address 0x4200000000000000000000000000000000000016. + * @title L2ToL1MessagePasser + * TODO: should this be renamed to L2OptimismPortal? */ -contract Withdrawer { +contract L2ToL1MessagePasser { /********** * Events * **********/ @@ -36,25 +31,42 @@ contract Withdrawer { bytes data ); - /// @notice Emitted when the balance of this contract is burned. + /** + * @notice Emitted when the balance of this contract is burned. + */ event WithdrawerBalanceBurnt(uint256 indexed amount); - /********************** - * Contract Variables * - **********************/ + /************* + * Variables * + *************/ - /// @notice A unique value hashed with each withdrawal. + /** + * @notice Includes the message hashes for all withdrawals + */ + mapping(bytes32 => bool) public sentMessages; + + /** + * @notice A unique value hashed with each withdrawal. + */ uint256 public nonce; - /// @notice A mapping listing withdrawals which have been initiated herein. - mapping(bytes32 => bool) public withdrawals; + /******************** + * Public Functions * + ********************/ - /********************** - * External Functions * - **********************/ + /** + * @notice Allow users to withdraw by sending ETH + * directly to this contract. + * TODO: maybe this should be only EOA + */ + receive() external payable { + initiateWithdrawal(msg.sender, 100000, bytes("")); + } /** * @notice Initiates a withdrawal to execute on L1. + * TODO: message hashes must be migrated since the legacy + * hashes are computed differently * @param _target Address to call on L1 execution. * @param _gasLimit GasLimit to provide on L1. * @param _data Data to forward to L1 target. @@ -62,11 +74,9 @@ contract Withdrawer { function initiateWithdrawal( address _target, uint256 _gasLimit, - bytes calldata _data - ) external payable { - address from = msg.sender; - - bytes32 withdrawalHash = WithdrawalVerifier._deriveWithdrawalHash( + bytes memory _data + ) public payable { + bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash( nonce, msg.sender, _target, @@ -74,9 +84,10 @@ contract Withdrawer { _gasLimit, _data ); - withdrawals[withdrawalHash] = true; - emit WithdrawalInitiated(nonce, from, _target, msg.value, _gasLimit, _data); + sentMessages[withdrawalHash] = true; + + emit WithdrawalInitiated(nonce, msg.sender, _target, msg.value, _gasLimit, _data); unchecked { ++nonce; } diff --git a/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol b/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol deleted file mode 100644 index 3fa345d6..00000000 --- a/packages/contracts/contracts/L2/messaging/IL2ERC20Bridge.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -/** - * @title IL2ERC20Bridge - */ -interface IL2ERC20Bridge { - /********** - * Events * - **********/ - - event WithdrawalInitiated( - address indexed _l1Token, - address indexed _l2Token, - address indexed _from, - address _to, - uint256 _amount, - bytes _data - ); - - event DepositFinalized( - address indexed _l1Token, - address indexed _l2Token, - address indexed _from, - address _to, - uint256 _amount, - bytes _data - ); - - event DepositFailed( - address indexed _l1Token, - address indexed _l2Token, - address indexed _from, - address _to, - uint256 _amount, - bytes _data - ); - - /******************** - * Public Functions * - ********************/ - - /** - * @dev get the address of the corresponding L1 bridge contract. - * @return Address of the corresponding L1 bridge contract. - */ - function l1TokenBridge() external returns (address); - - /** - * @dev initiate a withdraw of some tokens to the caller's account on L1 - * @param _l2Token Address of L2 token where withdrawal was initiated. - * @param _amount Amount of the token to withdraw. - * param _l1Gas Unused, but included for potential forward compatibility considerations. - * @param _data Optional data to forward to L1. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. - */ - function withdraw( - address _l2Token, - uint256 _amount, - uint32 _l1Gas, - bytes calldata _data - ) external; - - /** - * @dev initiate a withdraw of some token to a recipient's account on L1. - * @param _l2Token Address of L2 token where withdrawal is initiated. - * @param _to L1 adress to credit the withdrawal to. - * @param _amount Amount of the token to withdraw. - * param _l1Gas Unused, but included for potential forward compatibility considerations. - * @param _data Optional data to forward to L1. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. - */ - function withdrawTo( - address _l2Token, - address _to, - uint256 _amount, - uint32 _l1Gas, - bytes calldata _data - ) external; - - /************************* - * Cross-chain Functions * - *************************/ - - /** - * @dev Complete a deposit from L1 to L2, and credits funds to the recipient's balance of this - * L2 token. This call will fail if it did not originate from a corresponding deposit in - * L1StandardTokenBridge. - * @param _l1Token Address for the l1 token this is called with - * @param _l2Token Address for the l2 token this is called with - * @param _from Account to pull the deposit from on L2. - * @param _to Address to receive the withdrawal at - * @param _amount Amount of the token to withdraw - * @param _data Data provider by the sender on L1. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. - */ - function finalizeDeposit( - address _l1Token, - address _l2Token, - address _from, - address _to, - uint256 _amount, - bytes calldata _data - ) external; -} diff --git a/packages/contracts/contracts/L2/messaging/IL2StandardTokenFactory.sol b/packages/contracts/contracts/L2/messaging/IL2StandardTokenFactory.sol deleted file mode 100644 index d3a5e7d4..00000000 --- a/packages/contracts/contracts/L2/messaging/IL2StandardTokenFactory.sol +++ /dev/null @@ -1,11 +0,0 @@ -pragma solidity ^0.8.10; - -interface IL2StandardTokenFactory { - event StandardL2TokenCreated(address indexed _l1Token, address indexed _l2Token); - - function createStandardL2Token( - address _l1Token, - string memory _name, - string memory _symbol - ) external; -} diff --git a/packages/contracts/contracts/L2/messaging/L2CrossDomainMessenger.sol b/packages/contracts/contracts/L2/messaging/L2CrossDomainMessenger.sol deleted file mode 100644 index a7517600..00000000 --- a/packages/contracts/contracts/L2/messaging/L2CrossDomainMessenger.sol +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -// solhint-disable max-line-length -/* Library Imports */ -import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; -import { - Lib_CrossDomainUtils -} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol"; -import { - Lib_DefaultValues -} from "@eth-optimism/contracts/libraries/constants/Lib_DefaultValues.sol"; -import { Lib_BedrockPredeployAddresses } from "../../libraries/Lib_BedrockPredeployAddresses.sol"; - -/* Interface Imports */ -import { - IL2CrossDomainMessenger -} from "@eth-optimism/contracts/L2/messaging/IL2CrossDomainMessenger.sol"; -import { Withdrawer } from "../Withdrawer.sol"; - -// solhint-enable max-line-length - -/** - * @title L2CrossDomainMessenger - * @dev The L2 Cross Domain Messenger contract sends messages from L2 to L1, and is the entry point - * for L2 messages sent via the L1 Cross Domain Messenger. - * - */ -contract L2CrossDomainMessenger is IL2CrossDomainMessenger { - /************* - * Variables * - *************/ - - mapping(bytes32 => bool) public relayedMessages; - mapping(bytes32 => bool) public successfulMessages; - mapping(bytes32 => bool) public sentMessages; - uint256 public messageNonce; - address internal xDomainMsgSender = Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER; - address public l1CrossDomainMessenger; - - /*************** - * Constructor * - ***************/ - - constructor(address _l1CrossDomainMessenger) { - l1CrossDomainMessenger = _l1CrossDomainMessenger; - } - - /******************** - * Public Functions * - ********************/ - - function xDomainMessageSender() external view returns (address) { - require( - xDomainMsgSender != Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER, - "xDomainMessageSender is not set" - ); - return xDomainMsgSender; - } - - /** - * Sends a cross domain message to the target messenger. - * @param _target Target contract address. - * @param _message Message to send to the target. - * @param _gasLimit Gas limit for the provided message. - */ - function sendMessage( - address _target, - bytes memory _message, - uint32 _gasLimit - ) external { - // Temp note: I think we might be able to remove xDomainCallData from this contract - // entirely. - // Possibly also from the L1xDM as well, but needs considerations. - // Rationale: the proof no longer occurs in the L1 messenger, but rather in the - // WithdrawalRelayer logic (which is inherited into the Portal). This proof relies on - // the value returned by WithdrawalVerifier._deriveWithdrawalHash(), which takes in - // more arguments. - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - _target, - msg.sender, - _message, - messageNonce - ); - - // Temp note: Further to my notes above, afaict this mapping is not used for anything. - // It'd be great if we can save an SSTORE by removing it. - sentMessages[keccak256(xDomainCalldata)] = true; - - // Emit an event before we bump the nonce or the nonce will be off by one. - emit SentMessage(_target, msg.sender, _message, messageNonce, _gasLimit); - unchecked { - ++messageNonce; - } - - // Actually send the message. - Withdrawer(Lib_BedrockPredeployAddresses.WITHDRAWER).initiateWithdrawal( - l1CrossDomainMessenger, - _gasLimit, - _message - ); - } - - /** - * Relays a cross domain message to a contract. - * @inheritdoc IL2CrossDomainMessenger - */ - function relayMessage( - address _target, - address _sender, - bytes memory _message, - uint256 _messageNonce - ) external { - // Since it is impossible to deploy a contract to an address on L2 which matches - // the alias of the L1CrossDomainMessenger, this check can only pass when it is called in - // the first call frame of a deposit transaction. Thus reentrancy is prevented here. - require( - AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1CrossDomainMessenger, - "Provided message could not be verified." - ); - - // Temp note: Here we would need to keep the xDomainCalldata hashing and - // storage, because it allows replays when the call fails, and prevents - // them when it succeeds. - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - _target, - _sender, - _message, - _messageNonce - ); - - bytes32 xDomainCalldataHash = keccak256(xDomainCalldata); - - require( - successfulMessages[xDomainCalldataHash] == false, - "Provided message has already been received." - ); - - // Prevent calls to WITHDRAWER, which would enable - // an attacker to maliciously craft the _message to spoof - // a call from any L2 account. - // Todo: evaluate if this attack is still relevant - if (_target == Lib_BedrockPredeployAddresses.WITHDRAWER) { - // Write to the successfulMessages mapping and return immediately. - successfulMessages[xDomainCalldataHash] = true; - return; - } - - xDomainMsgSender = _sender; - // slither-disable-next-line reentrancy-no-eth, reentrancy-events, reentrancy-benign - (bool success, ) = _target.call(_message); - // slither-disable-next-line reentrancy-benign - xDomainMsgSender = Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER; - - // Mark the message as received if the call was successful. Ensures that a message can be - // relayed multiple times in the case that the call reverted. - if (success == true) { - // slither-disable-next-line reentrancy-no-eth - successfulMessages[xDomainCalldataHash] = true; - // slither-disable-next-line reentrancy-events - emit RelayedMessage(xDomainCalldataHash); - } else { - // slither-disable-next-line reentrancy-events - emit FailedRelayedMessage(xDomainCalldataHash); - } - - // Store an identifier that can be used to prove that the given message was relayed by some - // user. Gives us an easy way to pay relayers for their work. - // TODO: consider unaliasing `msg.sender` here - bytes32 relayId = keccak256(abi.encodePacked(xDomainCalldata, msg.sender, block.number)); - - // slither-disable-next-line reentrancy-benign - relayedMessages[relayId] = true; - } -} diff --git a/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol b/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol deleted file mode 100644 index b3671f30..00000000 --- a/packages/contracts/contracts/L2/messaging/L2StandardBridge.sol +++ /dev/null @@ -1,191 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -/* Interface Imports */ -import { IL1StandardBridge } from "@eth-optimism/contracts/L1/messaging/IL1StandardBridge.sol"; -import { IL1ERC20Bridge } from "@eth-optimism/contracts/L1/messaging/IL1ERC20Bridge.sol"; -import { IL2ERC20Bridge } from "./IL2ERC20Bridge.sol"; - -/* Library Imports */ -import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; -import { - Lib_PredeployAddresses -} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; -import { Lib_BedrockPredeployAddresses } from "../../libraries/Lib_BedrockPredeployAddresses.sol"; -import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; - -/* Contract Imports */ -import { IL2StandardERC20 } from "@eth-optimism/contracts/standards/IL2StandardERC20.sol"; -import { Withdrawer } from "../Withdrawer.sol"; - -/** - * @title L2StandardBridge - * @dev The L2 Standard bridge is a contract which works together with the L1 Standard bridge to - * enable ETH and ERC20 transitions between L1 and L2. - * This contract acts as a minter for new tokens when it hears about deposits into the L1 Standard - * bridge. - * This contract also acts as a burner of the tokens intended for withdrawal, informing the L1 - * bridge to release L1 funds. - */ -contract L2StandardBridge is IL2ERC20Bridge { - /******************************** - * External Contract References * - ********************************/ - - address public l1TokenBridge; - - /*************** - * Constructor * - ***************/ - - /** - * @param _l1TokenBridge Address of the L1 bridge deployed to the main chain. - */ - constructor(address _l1TokenBridge) { - l1TokenBridge = _l1TokenBridge; - } - - /*************** - * Withdrawing * - ***************/ - - /** - * @inheritdoc IL2ERC20Bridge - */ - function withdraw( - address _l2Token, - uint256 _amount, - uint32 _l1Gas, - bytes calldata _data - ) external virtual { - _initiateWithdrawal(_l2Token, msg.sender, msg.sender, _amount, _l1Gas, _data); - } - - /** - * @inheritdoc IL2ERC20Bridge - */ - function withdrawTo( - address _l2Token, - address _to, - uint256 _amount, - uint32 _l1Gas, - bytes calldata _data - ) external virtual { - _initiateWithdrawal(_l2Token, msg.sender, _to, _amount, _l1Gas, _data); - } - - /** - * @dev Performs the logic for withdrawals by burning the token and informing - * the L1 token Gateway of the withdrawal. - * @param _l2Token Address of L2 token where withdrawal is initiated. - * @param _from Account to pull the withdrawal from on L2. - * @param _to Account to give the withdrawal to on L1. - * @param _amount Amount of the token to withdraw. - * @param _l1Gas Unused, but included for potential forward compatibility considerations. - * @param _data Optional data to forward to L1. This data is provided - * solely as a convenience for external contracts. Aside from enforcing a maximum - * length, these contracts provide no guarantees about its content. - */ - function _initiateWithdrawal( - address _l2Token, - address _from, - address _to, - uint256 _amount, - uint32 _l1Gas, - bytes calldata _data - ) internal { - // When a withdrawal is initiated, we burn the withdrawer's funds to prevent subsequent L2 - // usage - // slither-disable-next-line reentrancy-events - IL2StandardERC20(_l2Token).burn(msg.sender, _amount); - - // Construct calldata for l1TokenBridge.finalizeERC20Withdrawal(_to, _amount) - // slither-disable-next-line reentrancy-events - address l1Token = IL2StandardERC20(_l2Token).l1Token(); - bytes memory message = abi.encodeWithSelector( - IL1ERC20Bridge.finalizeERC20Withdrawal.selector, - l1Token, - _l2Token, - _from, - _to, - _amount, - _data - ); - - // slither-disable-next-line reentrancy-events - emit WithdrawalInitiated(l1Token, _l2Token, msg.sender, _to, _amount, _data); - - // Send message up to L1 bridge - Withdrawer(Lib_BedrockPredeployAddresses.WITHDRAWER).initiateWithdrawal( - l1TokenBridge, - _l1Gas, - message - ); - } - - /************************************ - * Cross-chain Function: Depositing * - ************************************/ - - /** - * @inheritdoc IL2ERC20Bridge - */ - function finalizeDeposit( - address _l1Token, - address _l2Token, - address _from, - address _to, - uint256 _amount, - bytes calldata _data - ) external virtual { - // Since it is impossible to deploy a contract to an address on L2 which matches - // the alias of the l1TokenBridge, this check can only pass when it is called in - // the first call frame of a deposit transaction. Thus reentrancy is prevented here. - require( - AddressAliasHelper.undoL1ToL2Alias(msg.sender) == l1TokenBridge, - "Can only be called by a the l1TokenBridge" - ); - - if ( - // Check the target token is compliant and - // verify the deposited token on L1 matches the L2 deposited token representation here - // slither-disable-next-line reentrancy-events - ERC165Checker.supportsInterface(_l2Token, 0x1d1d8b63) && - _l1Token == IL2StandardERC20(_l2Token).l1Token() - ) { - // When a deposit is finalized, we credit the account on L2 with the same amount of - // tokens. - // slither-disable-next-line reentrancy-events - IL2StandardERC20(_l2Token).mint(_to, _amount); - // slither-disable-next-line reentrancy-events - emit DepositFinalized(_l1Token, _l2Token, _from, _to, _amount, _data); - } else { - // Either the L2 token which is being deposited-into disagrees about the correct address - // of its L1 token, or does not support the correct interface. - // This should only happen if there is a malicious L2 token, or if a user somehow - // specified the wrong L2 token address to deposit into. - // In either case, we stop the process here and construct a withdrawal - // message so that users can get their funds out in some cases. - // There is no way to prevent malicious token contracts altogether, but this does limit - // user error and mitigate some forms of malicious contract behavior. - bytes memory message = abi.encodeWithSelector( - IL1ERC20Bridge.finalizeERC20Withdrawal.selector, - _l1Token, - _l2Token, - _to, // switched the _to and _from here to bounce back the deposit to the sender - _from, - _amount, - _data - ); - - emit DepositFailed(_l1Token, _l2Token, _from, _to, _amount, _data); - - // Send message up to L1 bridge - Withdrawer(Lib_BedrockPredeployAddresses.WITHDRAWER).initiateWithdrawal( - l1TokenBridge, - 0, - message - ); - } - } -} diff --git a/packages/contracts/contracts/L2/messaging/L2StandardTokenFactory.sol b/packages/contracts/contracts/L2/messaging/L2StandardTokenFactory.sol deleted file mode 100644 index c9380746..00000000 --- a/packages/contracts/contracts/L2/messaging/L2StandardTokenFactory.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -/* Contract Imports */ -import { L2StandardERC20 } from "../tokens/L2StandardERC20.sol"; -import { - Lib_PredeployAddresses -} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; - -/** - * @title L2StandardTokenFactory - * @dev Factory contract for creating standard L2 token representations of L1 ERC20s - * compatible with and working on the standard bridge. - */ -contract L2StandardTokenFactory { - event StandardL2TokenCreated(address indexed _l1Token, address indexed _l2Token); - - /** - * @dev Creates an instance of the standard ERC20 token on L2. - * @param _l1Token Address of the corresponding L1 token. - * @param _name ERC20 name. - * @param _symbol ERC20 symbol. - */ - function createStandardL2Token( - address _l1Token, - string memory _name, - string memory _symbol - ) external { - require(_l1Token != address(0), "Must provide L1 token address"); - - L2StandardERC20 l2Token = new L2StandardERC20( - Lib_PredeployAddresses.L2_STANDARD_BRIDGE, - _l1Token, - _name, - _symbol - ); - - emit StandardL2TokenCreated(_l1Token, address(l2Token)); - } -} diff --git a/packages/contracts/contracts/L2/tokens/IL2StandardERC20.sol b/packages/contracts/contracts/L2/tokens/IL2StandardERC20.sol deleted file mode 100644 index f9e98184..00000000 --- a/packages/contracts/contracts/L2/tokens/IL2StandardERC20.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -interface IL2StandardERC20 is IERC20, IERC165 { - function l1Token() external returns (address); - - function l2Bridge() external returns (address); - - function mint(address _to, uint256 _amount) external; - - function burn(address _from, uint256 _amount) external; - - event Mint(address indexed _account, uint256 _amount); - event Burn(address indexed _account, uint256 _amount); -} diff --git a/packages/contracts/contracts/L2/tokens/L2StandardERC20.sol b/packages/contracts/contracts/L2/tokens/L2StandardERC20.sol deleted file mode 100644 index 06144d7c..00000000 --- a/packages/contracts/contracts/L2/tokens/L2StandardERC20.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.9; - -// import { ERC20 } from "solmate/tokens/ERC20.sol"; -// import { ERC20 } from "solmate/tokens/ERC20.sol"; -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "./IL2StandardERC20.sol"; - -contract L2StandardERC20 is IL2StandardERC20, ERC20 { - address public l1Token; - address public l2Bridge; - - /** - * @param _l2Bridge Address of the L2 standard bridge. - * @param _l1Token Address of the corresponding L1 token. - * @param _name ERC20 name. - * @param _symbol ERC20 symbol. - */ - constructor( - address _l2Bridge, - address _l1Token, - string memory _name, - string memory _symbol - ) ERC20(_name, _symbol) { - l1Token = _l1Token; - l2Bridge = _l2Bridge; - } - - modifier onlyL2Bridge() { - require(msg.sender == l2Bridge, "Only L2 Bridge can mint and burn"); - _; - } - - // slither-disable-next-line external-function - function supportsInterface(bytes4 _interfaceId) public pure returns (bool) { - bytes4 firstSupportedInterface = bytes4(keccak256("supportsInterface(bytes4)")); // ERC165 - bytes4 secondSupportedInterface = IL2StandardERC20.l1Token.selector ^ - IL2StandardERC20.mint.selector ^ - IL2StandardERC20.burn.selector; - return _interfaceId == firstSupportedInterface || _interfaceId == secondSupportedInterface; - } - - // slither-disable-next-line external-function - function mint(address _to, uint256 _amount) public virtual onlyL2Bridge { - _mint(_to, _amount); - - emit Mint(_to, _amount); - } - - // slither-disable-next-line external-function - function burn(address _from, uint256 _amount) public virtual onlyL2Bridge { - _burn(_from, _amount); - - emit Burn(_from, _amount); - } -} diff --git a/packages/contracts/contracts/libraries/ExcessivelySafeCall.sol b/packages/contracts/contracts/libraries/ExcessivelySafeCall.sol new file mode 100644 index 00000000..6d94ebd9 --- /dev/null +++ b/packages/contracts/contracts/libraries/ExcessivelySafeCall.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.9; + +// FROM: https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol +// TODO: Just use the original once we get our PR merged. +library ExcessivelySafeCall { + /// @notice Use when you _really_ really _really_ don't trust the called + /// contract. This prevents the called contract from causing reversion of + /// the caller in as many ways as we can. + /// @dev The main difference between this and a solidity low-level call is + /// that we limit the number of bytes that the callee can cause to be + /// copied to caller memory. This prevents stupid things like malicious + /// contracts returning 10,000,000 bytes causing a local OOG when copying + /// to memory. + /// @param _target The address to call + /// @param _gas The amount of gas to forward to the remote contract + /// @param _value Ether value to send with the call + /// @param _maxCopy The maximum number of bytes of returndata to copy + /// to memory. + /// @param _calldata The data to send to the remote contract + /// @return success and returndata, as `.call()`. Returndata is capped to + /// `_maxCopy` bytes. + function excessivelySafeCall( + address _target, + uint256 _gas, + uint256 _value, + uint16 _maxCopy, + bytes memory _calldata + ) internal returns (bool, bytes memory) { + // set up for assembly call + uint256 _toCopy; + bool _success; + bytes memory _returnData = new bytes(_maxCopy); + // dispatch message to recipient + // by assembly calling "handle" function + // we call via assembly to avoid memcopying a very large returndata + // returned by a malicious contract + assembly { + _success := call( + _gas, // gas + _target, // recipient + _value, // ether value + add(_calldata, 0x20), // inloc + mload(_calldata), // inlen + 0, // outloc + 0 // outlen + ) + // limit our copy to 256 bytes + _toCopy := returndatasize() + if gt(_toCopy, _maxCopy) { + _toCopy := _maxCopy + } + // Store the length of the copied bytes + mstore(_returnData, _toCopy) + // copy the bytes from returndata[0:_toCopy] + returndatacopy(add(_returnData, 0x20), 0, _toCopy) + } + return (_success, _returnData); + } +} diff --git a/packages/contracts/contracts/libraries/Lib_BedrockPredeployAddresses.sol b/packages/contracts/contracts/libraries/Lib_BedrockPredeployAddresses.sol index 07513b05..8a64bc6b 100644 --- a/packages/contracts/contracts/libraries/Lib_BedrockPredeployAddresses.sol +++ b/packages/contracts/contracts/libraries/Lib_BedrockPredeployAddresses.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.9; /** * @title Lib_BedrockPredeployAddresses + * TODO: just merge this value into the monorepo */ library Lib_BedrockPredeployAddresses { address internal constant L1_BLOCK_ATTRIBUTES = 0x4200000000000000000000000000000000000015; - address internal constant WITHDRAWER = 0x4200000000000000000000000000000000000016; } diff --git a/packages/contracts/contracts/libraries/Lib_CrossDomainHashing.sol b/packages/contracts/contracts/libraries/Lib_CrossDomainHashing.sol new file mode 100644 index 00000000..703ba64d --- /dev/null +++ b/packages/contracts/contracts/libraries/Lib_CrossDomainHashing.sol @@ -0,0 +1,249 @@ +//SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import { + Lib_CrossDomainUtils +} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol"; +import { Lib_RLPWriter } from "@eth-optimism/contracts/libraries/rlp/Lib_RLPWriter.sol"; + +/** + * @title CrossDomainHashing + * This library is responsible for holding cross domain utility + * functions. + * TODO(tynes): merge with Lib_CrossDomainUtils + * TODO(tynes): fill out more devdocs + */ +library CrossDomainHashing { + /** + * @notice Compute the L2 transaction hash given + * data about an L1 deposit transaction. This is useful for + * environments that do not have access to arbitrary + * RLP encoding functionality but have access to the + * standard web3 API + * TODO: rearrange args in a sane way + * @param _l1BlockHash The L1 block hash corresponding to the block + * the deposit was included in + * @param _logIndex The log index of the event that the deposit was + * created from. This can be found on the transaction receipt + * @param _from The sender of the deposit + * @param _to The L2 contract to be called by the deposit transaction + * @param _isCreate Indicates if the deposit creates a contract + * @param _mint The amount of ETH being minted by the transaction + * @param _value The amount of ETH send in the L2 call + * @param _gas The gas limit for the L2 call + */ + function L2TransactionHash( + bytes32 _l1BlockHash, + uint256 _logIndex, + address _from, + address _to, + bool _isCreate, + uint256 _mint, + uint256 _value, + uint256 _gas, + bytes memory _data + ) internal pure returns (bytes32) { + bytes memory raw = L2Transaction( + _l1BlockHash, + _logIndex, + _from, + _to, + _isCreate, + _mint, + _value, + _gas, + _data + ); + + return keccak256(raw); + } + + /** + * @notice Compute the deposit transaction source hash. + * This value ensures that the L2 transaction hash is unique + * and deterministic based on L1 execution + * @param l1BlockHash The L1 blockhash corresponding to the block including + * the deposit + * @param logIndex The index of the log that created the deposit transaction + */ + function sourceHash(bytes32 l1BlockHash, uint256 logIndex) internal pure returns (bytes32) { + bytes32 depositId = keccak256(abi.encode(l1BlockHash, logIndex)); + return keccak256(abi.encode(bytes32(0), depositId)); + } + + /** + * @notice RLP encode a deposit transaction + * This only works for user deposits, not system deposits + * TODO: better name + rearrange the input param ordering? + */ + function L2Transaction( + bytes32 _l1BlockHash, + uint256 _logIndex, + address _from, + address _to, + bool _isCreate, + uint256 _mint, + uint256 _value, + uint256 _gas, + bytes memory _data + ) internal pure returns (bytes memory) { + bytes32 source = sourceHash(_l1BlockHash, _logIndex); + + bytes[] memory raw = new bytes[](7); + + raw[0] = Lib_RLPWriter.writeBytes(bytes32ToBytes(source)); + raw[1] = Lib_RLPWriter.writeAddress(_from); + + if (_isCreate == true) { + require(_to == address(0)); + raw[2] = Lib_RLPWriter.writeBytes(""); + } else { + raw[2] = Lib_RLPWriter.writeAddress(_to); + } + + raw[3] = Lib_RLPWriter.writeUint(_mint); + raw[4] = Lib_RLPWriter.writeUint(_value); + raw[5] = Lib_RLPWriter.writeUint(_gas); + raw[6] = Lib_RLPWriter.writeBytes(_data); + + bytes memory encoded = Lib_RLPWriter.writeList(raw); + return abi.encodePacked(uint8(0x7e), encoded); + } + + /** + * @notice Helper function to turn bytes32 into bytes + */ + function bytes32ToBytes(bytes32 input) internal pure returns (bytes memory) { + bytes memory b = new bytes(32); + assembly { + mstore(add(b, 32), input) // set the bytes data + } + return b; + } + + /** + * @notice Adds the version to the nonce + */ + function addVersionToNonce(uint256 _nonce, uint16 _version) + internal + pure + returns (uint256 nonce) + { + assembly { + nonce := or(shl(240, _version), _nonce) + } + } + + /** + * @notice Gets the version out of the nonce + */ + function getVersionFromNonce(uint256 _nonce) internal pure returns (uint16 version) { + assembly { + version := shr(240, _nonce) + } + } + + /** + * @notice Encodes the cross domain message based on the version that + * is encoded in the nonce + */ + function getVersionedEncoding( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) internal pure returns (bytes memory) { + uint16 version = getVersionFromNonce(_nonce); + if (version == 0) { + return getEncodingV0(_target, _sender, _data, _nonce); + } else if (version == 1) { + return getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data); + } + + revert("Unknown version."); + } + + /** + * @notice Compute the cross domain hash based on the versioned nonce + */ + function getVersionedHash( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) internal pure returns (bytes32) { + uint16 version = getVersionFromNonce(_nonce); + if (version == 0) { + return getHashV0(_target, _sender, _data, _nonce); + } else if (version == 1) { + return getHashV1(_nonce, _sender, _target, _value, _gasLimit, _data); + } + + revert("Unknown version."); + } + + /** + * @notice Compute the legacy cross domain serialization + */ + function getEncodingV0( + address _target, + address _sender, + bytes memory _data, + uint256 _nonce + ) internal pure returns (bytes memory) { + return Lib_CrossDomainUtils.encodeXDomainCalldata(_target, _sender, _data, _nonce); + } + + /** + * @notice Compute the V1 cross domain serialization + */ + function getEncodingV1( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) internal pure returns (bytes memory) { + return + abi.encodeWithSignature( + "relayMessage(uint256,address,address,uint256,uint256,bytes)", + _nonce, + _sender, + _target, + _value, + _gasLimit, + _data + ); + } + + /** + * @notice Compute the legacy hash of a cross domain message + */ + function getHashV0( + address _target, + address _sender, + bytes memory _data, + uint256 _nonce + ) internal pure returns (bytes32) { + return keccak256(getEncodingV0(_target, _sender, _data, _nonce)); + } + + /** + * @notice Compute the V1 hash of a cross domain message + */ + function getHashV1( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _gasLimit, + bytes memory _data + ) internal pure returns (bytes32) { + return keccak256(getEncodingV1(_nonce, _sender, _target, _value, _gasLimit, _data)); + } +} diff --git a/packages/contracts/contracts/libraries/Lib_WithdrawalVerifier.sol b/packages/contracts/contracts/libraries/Lib_WithdrawalVerifier.sol index 9d998afb..afee6537 100644 --- a/packages/contracts/contracts/libraries/Lib_WithdrawalVerifier.sol +++ b/packages/contracts/contracts/libraries/Lib_WithdrawalVerifier.sol @@ -5,6 +5,9 @@ pragma solidity 0.8.10; import { Lib_SecureMerkleTrie } from "@eth-optimism/contracts/libraries/trie/Lib_SecureMerkleTrie.sol"; +import { + Lib_CrossDomainUtils +} from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol"; /** * @title WithdrawalVerifier @@ -28,13 +31,13 @@ library WithdrawalVerifier { * @param _gasLimit Gas to be forwarded to the target. * @param _data Data to send to the target. */ - function _deriveWithdrawalHash( + function withdrawalHash( uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _gasLimit, - bytes calldata _data + bytes memory _data ) internal pure returns (bytes32) { return keccak256(abi.encode(_nonce, _sender, _target, _value, _gasLimit, _data)); } @@ -44,7 +47,7 @@ library WithdrawalVerifier { * @param _outputRootProof The elements which were hashed together to generate the output root. * @return Whether or not the output root matches the hashed output of the proof. */ - function _deriveOutputRoot(OutputRootProof calldata _outputRootProof) + function _deriveOutputRoot(OutputRootProof memory _outputRootProof) internal pure returns (bytes32) @@ -71,12 +74,12 @@ library WithdrawalVerifier { function _verifyWithdrawalInclusion( bytes32 _withdrawalHash, bytes32 _withdrawerStorageRoot, - bytes calldata _withdrawalProof + bytes memory _withdrawalProof ) internal pure returns (bool) { bytes32 storageKey = keccak256( abi.encode( _withdrawalHash, - uint256(1) // The withdrawals mapping is at the second slot in the layout. + uint256(0) // The withdrawals mapping is at the first slot in the layout. ) ); diff --git a/packages/contracts/contracts/test/CommonTest.t.sol b/packages/contracts/contracts/test/CommonTest.t.sol index f3442f14..353ce619 100644 --- a/packages/contracts/contracts/test/CommonTest.t.sol +++ b/packages/contracts/contracts/test/CommonTest.t.sol @@ -3,9 +3,29 @@ pragma solidity 0.8.10; /* Testing utilities */ import { Test } from "forge-std/Test.sol"; -import { Vm } from "forge-std/Vm.sol"; +import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; +import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; +import { L1StandardBridge } from "../L1/L1StandardBridge.sol"; +import { L2StandardBridge } from "../L2/L2StandardBridge.sol"; +import { OptimismMintableTokenFactory } from "../universal/OptimismMintableTokenFactory.sol"; +import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol"; +import { OptimismPortal } from "../L1/OptimismPortal.sol"; +import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; +import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol"; +import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol"; +import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; + +import { + Lib_PredeployAddresses +} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { console } from "forge-std/console.sol"; contract CommonTest is Test { + address alice = address(128); + address bob = address(256); + address immutable ZERO_ADDRESS = address(0); address immutable NON_ZERO_ADDRESS = address(1); uint256 immutable NON_ZERO_VALUE = 100; @@ -13,4 +33,276 @@ contract CommonTest is Test { uint64 immutable NON_ZERO_GASLIMIT = 50000; bytes32 nonZeroHash = keccak256(abi.encode("NON_ZERO")); bytes NON_ZERO_DATA = hex"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000"; + + function _setUp() public { + // Give alice and bob some ETH + vm.deal(alice, 1 << 16); + vm.deal(bob, 1 << 16); + + vm.label(alice, "alice"); + vm.label(bob, "bob"); + } +} +contract L2OutputOracle_Initializer is CommonTest { + // Test target + L2OutputOracle oracle; + + // Constructor arguments + address sequencer = 0x000000000000000000000000000000000000AbBa; + uint256 submissionInterval = 1800; + uint256 l2BlockTime = 2; + bytes32 genesisL2Output = keccak256(abi.encode(0)); + uint256 historicalTotalBlocks = 100; + + // Cache of the initial L2 timestamp + uint256 startingBlockTimestamp; + + // By default the first block has timestamp zero, which will cause underflows in the tests + uint256 initTime = 1000; + + function setUp() public virtual { + _setUp(); + + // Move time forward so we have a non-zero starting timestamp + vm.warp(initTime); + // Deploy the L2OutputOracle and transfer owernship to the sequencer + oracle = new L2OutputOracle( + submissionInterval, + l2BlockTime, + genesisL2Output, + historicalTotalBlocks, + initTime, + sequencer + ); + startingBlockTimestamp = block.timestamp; + } +} + +contract Messenger_Initializer is L2OutputOracle_Initializer { + OptimismPortal op; + L1CrossDomainMessenger L1Messenger; + L2CrossDomainMessenger L2Messenger; + L2ToL1MessagePasser messagePasser; + + event SentMessage( + address indexed target, + address sender, + bytes message, + uint256 messageNonce, + uint256 gasLimit + ); + + event WithdrawalInitiated( + uint256 indexed nonce, + address indexed sender, + address indexed target, + uint256 value, + uint256 gasLimit, + bytes data + ); + + event RelayedMessage(bytes32 indexed msgHash); + + event TransactionDeposited( + address indexed from, + address indexed to, + uint256 mint, + uint256 value, + uint64 gasLimit, + bool isCreation, + bytes data + ); + + event WithdrawalFinalized(bytes32 indexed, bool success); + + function setUp() public virtual override { + super.setUp(); + + // Deploy the OptimismPortal + op = new OptimismPortal(oracle, 100); + vm.label(address(op), "OptimismPortal"); + + L1Messenger = new L1CrossDomainMessenger(); + L1Messenger.initialize(op); + + L2CrossDomainMessenger l2m = new L2CrossDomainMessenger(); + vm.etch(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, address(l2m).code); + L2Messenger = L2CrossDomainMessenger(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER); + + L2Messenger.initialize(address(L1Messenger)); + + // Set the L2ToL1MessagePasser at the correct address + L2ToL1MessagePasser mp = new L2ToL1MessagePasser(); + vm.etch(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER, address(mp).code); + messagePasser = L2ToL1MessagePasser(payable(Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER)); + + vm.label( + Lib_PredeployAddresses.L2_TO_L1_MESSAGE_PASSER, + "L2ToL1MessagePasser" + ); + + vm.label( + Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, + "L2CrossDomainMessenger" + ); + + vm.label( + AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)), + "L1CrossDomainMessenger_aliased" + ); + } } + +contract Bridge_Initializer is Messenger_Initializer { + L1StandardBridge L1Bridge; + L2StandardBridge L2Bridge; + OptimismMintableTokenFactory L2TokenFactory; + OptimismMintableTokenFactory L1TokenFactory; + ERC20 L1Token; + OptimismMintableERC20 L2Token; + ERC20 NativeL2Token; + OptimismMintableERC20 RemoteL1Token; + + event ETHDepositInitiated( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); + + event ETHWithdrawalFinalized( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); + + event ERC20DepositInitiated( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event ERC20WithdrawalFinalized( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event WithdrawalInitiated( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event DepositFinalized( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event DepositFailed( + address indexed _l1Token, + address indexed _l2Token, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event ETHBridgeInitiated( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); + + event ETHBridgeFinalized( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); + + event ERC20BridgeInitiated( + address indexed _localToken, + address indexed _remoteToken, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event ERC20BridgeFinalized( + address indexed _localToken, + address indexed _remoteToken, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + function setUp() public virtual override { + super.setUp(); + + vm.label( + Lib_PredeployAddresses.L2_STANDARD_BRIDGE, + "L2StandardBridge" + ); + vm.label( + Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, + "L2StandardTokenFactory" + ); + + // Deploy the L1 bridge and initialize it with the address of the + // L1CrossDomainMessenger + L1Bridge = new L1StandardBridge(); + L1Bridge.initialize(payable(address(L1Messenger))); + vm.label(address(L1Bridge), "L1StandardBridge"); + + // Deploy the L2StandardBridge, move it to the correct predeploy + // address and then initialize it + L2StandardBridge l2B = new L2StandardBridge(); + vm.etch(Lib_PredeployAddresses.L2_STANDARD_BRIDGE, address(l2B).code); + L2Bridge = L2StandardBridge(payable(Lib_PredeployAddresses.L2_STANDARD_BRIDGE)); + L2Bridge.initialize(payable(address(L1Bridge))); + + // Set up the L2 mintable token factory + OptimismMintableTokenFactory factory = new OptimismMintableTokenFactory(); + vm.etch(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, address(factory).code); + L2TokenFactory = OptimismMintableTokenFactory(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY); + L2TokenFactory.initialize(Lib_PredeployAddresses.L2_STANDARD_BRIDGE); + + L1Token = new ERC20("Native L1 Token", "L1T"); + + // Deploy the L2 ERC20 now + L2Token = OptimismMintableERC20(L2TokenFactory.createStandardL2Token( + address(L1Token), + string(abi.encodePacked("L2-", L1Token.name())), + string(abi.encodePacked("L2-", L1Token.symbol())) + )); + + NativeL2Token = new ERC20("Native L2 Token", "L2T"); + L1TokenFactory = new OptimismMintableTokenFactory(); + L1TokenFactory.initialize(address(L1Bridge)); + + RemoteL1Token = OptimismMintableERC20(L1TokenFactory.createStandardL2Token( + address(NativeL2Token), + string(abi.encodePacked("L1-", NativeL2Token.name())), + string(abi.encodePacked("L1-", NativeL2Token.symbol())) + )); + } +} + diff --git a/packages/contracts/contracts/test/CrossDomainHashing.t.sol b/packages/contracts/contracts/test/CrossDomainHashing.t.sol new file mode 100644 index 00000000..bbb14b57 --- /dev/null +++ b/packages/contracts/contracts/test/CrossDomainHashing.t.sol @@ -0,0 +1,71 @@ +//SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import { CommonTest } from "./CommonTest.t.sol"; +import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol"; + +contract CrossDomainHashing_Test is CommonTest { + function test_nonceVersioning(uint240 _nonce, uint16 _version) external { + uint256 nonce = CrossDomainHashing.addVersionToNonce(uint256(_nonce), _version); + uint16 version = CrossDomainHashing.getVersionFromNonce(nonce); + assertEq(version, _version); + } + + // TODO(tynes): turn this into differential fuzzing + // it is very easy to do so with the typescript + function test_l2TransactionHash() external { + bytes32 l1BlockHash = 0xd1a498e053451fc90bd8a597051a1039010c8e55e2659b940d3070b326e4f4c5; + uint256 logIndex = 0x0; + address from = address(0xDe3829A23DF1479438622a08a116E8Eb3f620BB5); + address to = address(0xB7e390864a90b7b923C9f9310C6F98aafE43F707); + bool isCreate = false; + uint256 mint = 0xe043da617250000; + uint256 value = 0xde0b6b3a7640000; + uint256 gas = 0x2dc6c0; + bytes memory data = hex""; + + bytes32 sourceHash = CrossDomainHashing.sourceHash( + l1BlockHash, + logIndex + ); + + assertEq( + sourceHash, + 0x77fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd0735 + ); + + bytes memory raw = CrossDomainHashing.L2Transaction( + l1BlockHash, + logIndex, + from, + to, + isCreate, + mint, + value, + gas, + data + ); + + assertEq( + raw, + hex"7ef862a077fc5994647d128a4d131d273a5e89e0306aac472494068a4f1fceab83dd073594de3829a23df1479438622a08a116e8eb3f620bb594b7e390864a90b7b923c9f9310c6f98aafe43f707880e043da617250000880de0b6b3a7640000832dc6c080" + ); + + bytes32 digest = CrossDomainHashing.L2TransactionHash( + l1BlockHash, + logIndex, + from, + to, + isCreate, + mint, + value, + gas, + data + ); + + assertEq( + digest, + 0xf5f97d03e8be48a4b20ed70c9d8b11f1c851bf949bf602b7580985705bb09077 + ); + } +} diff --git a/packages/contracts/contracts/test/DepositFeed.t.sol b/packages/contracts/contracts/test/DepositFeed.t.sol deleted file mode 100644 index 4cc4ec87..00000000 --- a/packages/contracts/contracts/test/DepositFeed.t.sol +++ /dev/null @@ -1,218 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity 0.8.10; - -/* Testing utilities */ -import { CommonTest } from "./CommonTest.t.sol"; - -/* Library Imports */ -import { - AddressAliasHelper -} from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; - -/* Target contract */ -import { DepositFeed } from "../L1/abstracts/DepositFeed.sol"; - -contract Target is DepositFeed {} - -contract DepositFeedTest is CommonTest { - - Target df; - - event TransactionDeposited( - address indexed from, - address indexed to, - uint256 mint, - uint256 value, - uint64 gasLimit, - bool isCreation, - bytes data - ); - - function setUp() external { - df = new Target(); - } - - // Test: depositTransaction fails when contract creation has a non-zero destination address - function test_depositTransaction_ContractCreationReverts() external { - vm.expectRevert(abi.encodeWithSignature("NonZeroCreationTarget()")); - df.depositTransaction(NON_ZERO_ADDRESS, NON_ZERO_VALUE, NON_ZERO_GASLIMIT, true, hex""); - } - - // Test: depositTransaction should emit the correct log when an EOA deposits a tx with 0 value - function test_depositTransaction_NoValueEOA() external { - // EOA emulation - vm.prank(address(this), address(this)); - vm.expectEmit(true, true, false, true); - emit TransactionDeposited( - address(this), - NON_ZERO_ADDRESS, - ZERO_VALUE, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - false, - NON_ZERO_DATA - ); - - df.depositTransaction( - NON_ZERO_ADDRESS, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - false, - NON_ZERO_DATA - ); - } - - // Test: depositTransaction should emit the correct log when a contract deposits a tx with 0 value - function test_depositTransaction_NoValueContract() external { - vm.expectEmit(true, true, false, true); - emit TransactionDeposited( - AddressAliasHelper.applyL1ToL2Alias(address(this)), - NON_ZERO_ADDRESS, - ZERO_VALUE, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - false, - NON_ZERO_DATA - ); - - df.depositTransaction( - NON_ZERO_ADDRESS, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - false, - NON_ZERO_DATA - ); - } - - // Test: depositTransaction should emit the correct log when an EOA deposits a contract creation with 0 value - function test_depositTransaction_createWithZeroValueForEOA() external { - // EOA emulation - vm.prank(address(this), address(this)); - - vm.expectEmit(true, true, false, true); - emit TransactionDeposited( - address(this), - ZERO_ADDRESS, - ZERO_VALUE, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - true, - NON_ZERO_DATA - ); - - df.depositTransaction(ZERO_ADDRESS, ZERO_VALUE, NON_ZERO_GASLIMIT, true, NON_ZERO_DATA); - } - - // Test: depositTransaction should emit the correct log when a contract deposits a contract creation with 0 value - function test_depositTransaction_createWithZeroValueForContract() external { - vm.expectEmit(true, true, false, true); - emit TransactionDeposited( - AddressAliasHelper.applyL1ToL2Alias(address(this)), - ZERO_ADDRESS, - ZERO_VALUE, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - true, - NON_ZERO_DATA - ); - - df.depositTransaction(ZERO_ADDRESS, ZERO_VALUE, NON_ZERO_GASLIMIT, true, NON_ZERO_DATA); - } - - // Test: depositTransaction should increase its eth balance when an EOA deposits a transaction with ETH - function test_depositTransaction_withEthValueFromEOA() external { - // EOA emulation - vm.prank(address(this), address(this)); - - vm.expectEmit(true, true, false, true); - emit TransactionDeposited( - address(this), - NON_ZERO_ADDRESS, - NON_ZERO_VALUE, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - false, - NON_ZERO_DATA - ); - - df.depositTransaction{ value: NON_ZERO_VALUE }( - NON_ZERO_ADDRESS, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - false, - NON_ZERO_DATA - ); - assertEq(address(df).balance, NON_ZERO_VALUE); - } - - // Test: depositTransaction should increase its eth balance when a contract deposits a transaction with ETH - function test_depositTransaction_withEthValueFromContract() external { - vm.expectEmit(true, true, false, true); - emit TransactionDeposited( - AddressAliasHelper.applyL1ToL2Alias(address(this)), - NON_ZERO_ADDRESS, - NON_ZERO_VALUE, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - false, - NON_ZERO_DATA - ); - - df.depositTransaction{ value: NON_ZERO_VALUE }( - NON_ZERO_ADDRESS, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - false, - NON_ZERO_DATA - ); - } - - // Test: depositTransaction should increase its eth balance when an EOA deposits a contract creation with ETH - function test_depositTransaction_withEthValueAndEOAContractCreation() external { - // EOA emulation - vm.prank(address(this), address(this)); - - vm.expectEmit(true, true, false, true); - emit TransactionDeposited( - address(this), - ZERO_ADDRESS, - NON_ZERO_VALUE, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - true, - hex"" - ); - - df.depositTransaction{ value: NON_ZERO_VALUE }( - ZERO_ADDRESS, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - true, - hex"" - ); - assertEq(address(df).balance, NON_ZERO_VALUE); - } - - // Test: depositTransaction should increase its eth balance when a contract deposits a contract creation with ETH - function test_depositTransaction_withEthValueAndContractContractCreation() external { - vm.expectEmit(true, true, false, true); - emit TransactionDeposited( - AddressAliasHelper.applyL1ToL2Alias(address(this)), - ZERO_ADDRESS, - NON_ZERO_VALUE, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - true, - NON_ZERO_DATA - ); - - df.depositTransaction{ value: NON_ZERO_VALUE }( - ZERO_ADDRESS, - ZERO_VALUE, - NON_ZERO_GASLIMIT, - true, - NON_ZERO_DATA - ); - assertEq(address(df).balance, NON_ZERO_VALUE); - } -} diff --git a/packages/contracts/contracts/test/L1Block.t.sol b/packages/contracts/contracts/test/L1Block.t.sol index 2707a18c..12278001 100644 --- a/packages/contracts/contracts/test/L1Block.t.sol +++ b/packages/contracts/contracts/test/L1Block.t.sol @@ -1,12 +1,10 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; -import { DSTest } from "forge-std/Test.sol"; -import { Vm } from "forge-std/Vm.sol"; +import { CommonTest } from "./CommonTest.t.sol"; import { L1Block } from "../L2/L1Block.sol"; -contract L1BLockTest is DSTest { - Vm vm = Vm(HEVM_ADDRESS); +contract L1BLockTest is CommonTest { L1Block lb; address depositor; bytes32 immutable NON_ZERO_HASH = keccak256(abi.encode(1)); @@ -18,6 +16,16 @@ contract L1BLockTest is DSTest { lb.setL1BlockValues(uint64(1), uint64(2), 3, NON_ZERO_HASH, uint64(4)); } + function test_updatesValues(uint64 n, uint64 t, uint256 b, bytes32 h, uint64 s) external { + vm.prank(depositor); + lb.setL1BlockValues(n, t, b, h, s); + assertEq(lb.number(), n); + assertEq(lb.timestamp(), t); + assertEq(lb.basefee(), b); + assertEq(lb.hash(), h); + assertEq(lb.sequenceNumber(), s); + } + function test_number() external { assertEq(lb.number(), uint64(1)); } diff --git a/packages/contracts/contracts/test/L1CrossDomainMessenger.t.sol b/packages/contracts/contracts/test/L1CrossDomainMessenger.t.sol index bd86d37c..b3eaf07f 100644 --- a/packages/contracts/contracts/test/L1CrossDomainMessenger.t.sol +++ b/packages/contracts/contracts/test/L1CrossDomainMessenger.t.sol @@ -2,10 +2,13 @@ pragma solidity 0.8.10; /* Testing utilities */ -import { CommonTest } from "./CommonTest.t.sol"; +import { Messenger_Initializer } from "./CommonTest.t.sol"; import { L2OutputOracle_Initializer } from "./L2OutputOracle.t.sol"; /* Libraries */ +import { + AddressAliasHelper +} from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; import { Lib_DefaultValues } from "@eth-optimism/contracts/libraries/constants/Lib_DefaultValues.sol"; @@ -21,81 +24,116 @@ import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol"; +import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol"; + /* Target contract */ -import { L1CrossDomainMessenger } from "../L1/messaging/L1CrossDomainMessenger.sol"; -import { IDepositFeed } from "../L1/abstracts/IDepositFeed.sol"; +import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol"; import { ICrossDomainMessenger } from "@eth-optimism/contracts/libraries/bridge/ICrossDomainMessenger.sol"; -contract L1CrossDomainMessenger_Test is CommonTest, L2OutputOracle_Initializer { - // Dependencies - OptimismPortal op; - // 'L2OutputOracle oracle' is declared in L2OutputOracle_Initializer - - event SentMessage( - address indexed target, - address sender, - bytes message, - uint256 messageNonce, - uint256 gasLimit - ); - event RelayedMessage(bytes32 indexed msgHash); - - // Contract under test - L1CrossDomainMessenger messenger; - +contract L1CrossDomainMessenger_Test is Messenger_Initializer { // Receiver address for testing address recipient = address(0xabbaacdc); - function setUp() external { - // new portal with small finalization window - op = new OptimismPortal(oracle, 100); - messenger = new L1CrossDomainMessenger(); - messenger.initialize(op, Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER); + function setUp() public override { + super.setUp(); } // pause: should pause the contract when called by the current owner - function test_pause() external { - messenger.pause(); - assert(messenger.paused()); + function test_L1MessengerPause() external { + L1Messenger.pause(); + assert(L1Messenger.paused()); } // pause: should not pause the contract when called by account other than the owner - function testCannot_pause() external { + function testCannot_L1MessengerPause() external { vm.expectRevert("Ownable: caller is not the owner"); vm.prank(address(0xABBA)); - messenger.pause(); + L1Messenger.pause(); } - // sendMessage: should be able to send a single message - function test_sendMessage() external { - uint256 messageNonce = messenger.messageNonce(); - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - recipient, - address(this), - NON_ZERO_DATA, - messageNonce + // the version is encoded in the nonce + function test_L1MessengerMessageVersion() external { + assertEq( + CrossDomainHashing.getVersionFromNonce(L1Messenger.messageNonce()), + L1Messenger.MESSAGE_VERSION() ); + } + + // sendMessage: should be able to send a single message + // TODO: this same test needs to be done with the legacy message type + // by setting the message version to 0 + function test_L1MessengerSendMessage() external { + // deposit transaction on the optimism portal should be called vm.expectCall( address(op), abi.encodeWithSelector( - IDepositFeed.depositTransaction.selector, + OptimismPortal.depositTransaction.selector, Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, 0, - NON_ZERO_GASLIMIT, + 100, false, - xDomainCalldata + CrossDomainHashing.getVersionedEncoding( + L1Messenger.messageNonce(), + alice, + recipient, + 0, + 100, + hex"ff" + ) ) ); - messenger.sendMessage(recipient, NON_ZERO_DATA, uint32(NON_ZERO_GASLIMIT)); + + // TransactionDeposited event + vm.expectEmit(true, true, true, true); + emit TransactionDeposited( + AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)), + Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER, + 0, + 0, + 100, + false, + CrossDomainHashing.getVersionedEncoding( + L1Messenger.messageNonce(), + alice, + recipient, + 0, + 100, + hex"ff" + ) + ); + + // SentMessage event + vm.expectEmit(true, true, true, true); + emit SentMessage( + recipient, + alice, + hex"ff", + L1Messenger.messageNonce(), + 100 + ); + + vm.prank(alice); + L1Messenger.sendMessage(recipient, hex"ff", uint32(100)); } // sendMessage: should be able to send the same message twice - function test_sendMessageTwice() external { - messenger.sendMessage(recipient, NON_ZERO_DATA, uint32(NON_ZERO_GASLIMIT)); - messenger.sendMessage(recipient, NON_ZERO_DATA, uint32(NON_ZERO_GASLIMIT)); + function test_L1MessengerTwiceSendMessage() external { + uint256 nonce = L1Messenger.messageNonce(); + L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000)); + L1Messenger.sendMessage(recipient, hex"aa", uint32(500_000)); + // the nonce increments for each message sent + assertEq( + nonce + 2, + L1Messenger.messageNonce() + ); + } + + function test_L1MessengerXDomainSenderReverts() external { + vm.expectRevert("xDomainMessageSender is not set"); + L1Messenger.xDomainMessageSender(); } // xDomainMessageSender: should return the xDomainMsgSender address @@ -103,216 +141,81 @@ contract L1CrossDomainMessenger_Test is CommonTest, L2OutputOracle_Initializer { // function test_xDomainSenderSetCorrectly() external {} // relayMessage: should send a successful call to the target contract - function test_relayMessageSucceeds() external { + function test_L1MessengerRelayMessageSucceeds() external { address target = address(0xabcd); address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER; - bytes memory message = hex"1111"; - uint256 messageNonce = 42; - // The encoding we'll use to verify that the message was successful relayed - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - target, - sender, - message, - messageNonce - ); - // ensure that both the messenger and target receive a call - vm.expectCall( - address(messenger), - abi.encodeWithSelector( - L1CrossDomainMessenger.relayMessage.selector, - target, - sender, - message, - messageNonce - ) - ); - vm.expectCall(address(0xabcd), hex"1111"); + vm.expectCall(target, hex"1111"); + // set the value of op.l2Sender() to be the L2 Cross Domain Messenger. vm.store(address(op), 0, bytes32(abi.encode(sender))); vm.prank(address(op)); + vm.expectEmit(true, true, true, true); - emit RelayedMessage(keccak256(xDomainCalldata)); - messenger.relayMessage(target, sender, message, messageNonce); - // Ensure the hash of the xDomainCalldata was stored in the successfulMessages mapping. - bytes32 messageHash = keccak256(xDomainCalldata); - assert(messenger.successfulMessages(messageHash)); - } + bytes32 hash = CrossDomainHashing.getVersionedHash( + 0, + sender, + target, + 0, + 0, + hex"1111" + ); + emit RelayedMessage(hash); - // relayMessage: should revert if still inside the fraud proof window - function test_relayMessageInsideFraudProofWindow() external { - bytes memory cd = abi.encodeWithSelector( - L1CrossDomainMessenger.relayMessage.selector, - address(42), - address(this), - hex"1111", - 0 + L1Messenger.relayMessage( + 0, // nonce + sender, + target, + 0, // value + 0, + hex"1111" ); - WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier.OutputRootProof({ - version: bytes32(0), - stateRoot: bytes32(0), - withdrawerStorageRoot: bytes32(0), - latestBlockhash:bytes32(0) - }); - - bytes memory withdrawProof = bytes(hex""); - - // get the finalization window - uint256 window = op.FINALIZATION_PERIOD(); - assert(window != 0); - // set block.timestamp to be one less than the finalization window. - // the timestamp 0 is passed into `finalizeWithdrawalTransaction` - vm.warp(window - 1); - - // The OptimismPortal is responsible for keeping track - // of the finalization window - vm.expectRevert(abi.encodeWithSignature("NotYetFinal()")); - op.finalizeWithdrawalTransaction( - 0, // nonce - address(this), // sender - address(42), // target - 0, // value - 100000, // gasLimit - cd, // calldata - 0, // timestamp - outputRootProof, // outputRootProof - withdrawProof // withdrawProof - ); + // the message hash is in the successfulMessages mapping + assert(L1Messenger.successfulMessages(hash)); + // it is not in the received messages mapping + assertEq(L1Messenger.receivedMessages(hash), false); } // relayMessage: should revert if attempting to relay a message sent to an L1 system contract - function test_relayMessageToSystemContract() external { + function test_L1MessengerRelayMessageToSystemContract() external { // set the target to be the OptimismPortal address target = address(op); address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER; bytes memory message = hex"1111"; - uint256 messageNonce = 42; // set the value of op.l2Sender() to be the L2 Cross Domain Messenger. - vm.store(address(op), 0, bytes32(abi.encode(sender))); vm.prank(address(op)); - vm.expectRevert("Cannot send L2->L1 messages to L1 system contracts."); - messenger.relayMessage(target, sender, message, messageNonce); - } + vm.expectRevert("Message cannot be replayed."); + L1Messenger.relayMessage(0, sender, target, 0, 0, message); - // relayMessage: should revert if provided an invalid output root proof - function test_revertOnInvalidOutputRootProof() external { - // create an invalid output root proof - WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier.OutputRootProof({ - version: bytes32(0), - stateRoot: bytes32(0), - withdrawerStorageRoot: bytes32(0), - latestBlockhash:bytes32(0) - }); - bytes memory withdrawProof = bytes(hex""); - - vm.expectRevert(abi.encodeWithSignature("InvalidOutputRootProof()")); - op.finalizeWithdrawalTransaction( - 0, // nonce - address(this), // sender - address(42), // target - 0, // value - 100000, // gasLimit - bytes(""), // calldata - 0, // timestamp - outputRootProof, // outputRootProof - withdrawProof // withdrawProof - ); + vm.store(address(op), 0, bytes32(abi.encode(sender))); + vm.expectRevert("Message cannot be replayed."); + L1Messenger.relayMessage(0, sender, target, 0, 0, message); } // relayMessage: the xDomainMessageSender is reset to the original value - function test_xDomainMessageSenderResets() external { + function test_L1MessengerxDomainMessageSenderResets() external { vm.expectRevert("xDomainMessageSender is not set"); - messenger.xDomainMessageSender(); + L1Messenger.xDomainMessageSender(); address sender = Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER; - bytes memory message = hex"1111"; - uint256 messageNonce = 42; - vm.store(address(op), 0, bytes32(abi.encode(sender))); vm.prank(address(op)); - messenger.relayMessage(address(0), sender, message, messageNonce); + L1Messenger.relayMessage(0, address(0), address(0), 0, 0, hex""); vm.expectRevert("xDomainMessageSender is not set"); - messenger.xDomainMessageSender(); - } - - // relayMessage: should revert if trying to send the same message twice - function test_relayShouldRevertSendingSameMessageTwice() external { - // TODO: this is a test on the L2CrossDomainMessenger + L1Messenger.xDomainMessageSender(); } // relayMessage: should revert if paused - function test_relayShouldRevertIfPaused() external { - vm.prank(messenger.owner()); - messenger.pause(); + function test_L1MessengerRelayShouldRevertIfPaused() external { + vm.prank(L1Messenger.owner()); + L1Messenger.pause(); vm.expectRevert("Pausable: paused"); - messenger.relayMessage(address(0), address(0), hex"", 0); - } - - // blockMessage and allowMessage: should revert if called by an account other than the owner - function test_relayMessageBlockingAuth() external { - bytes32 msgHash = bytes32(hex"ff"); - - vm.prank(address(0)); - vm.expectRevert("Ownable: caller is not the owner"); - messenger.blockMessage(msgHash); - assert(messenger.blockedMessages(msgHash) == false); - - vm.prank(address(0)); - vm.expectRevert("Ownable: caller is not the owner"); - messenger.allowMessage(msgHash); - assert(messenger.blockedMessages(msgHash) == false); - } - - // blockMessage and allowMessage: should revert if the message is blocked - function test_relayRevertOnBlockedMessage() external { - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - address(0), - address(0), - hex"ff", - 0 - ); - bytes32 msgHash = keccak256(xDomainCalldata); - - vm.prank(messenger.owner()); - messenger.blockMessage(msgHash); - - vm.store(address(op), 0, bytes32(abi.encode(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER))); - vm.prank(address(op)); - vm.expectRevert("Provided message has been blocked."); - messenger.relayMessage(address(0), address(0), hex"ff", 0); - } - - // blockMessage and allowMessage: should succeed if the message is blocked, then unblocked - function test_blockAndUnblockSuccessfulMessage() external { - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - address(0), - address(0), - hex"ff", - 0 - ); - bytes32 msgHash = keccak256(xDomainCalldata); - - vm.prank(messenger.owner()); - messenger.blockMessage(msgHash); - - vm.store(address(op), 0, bytes32(abi.encode(Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER))); - vm.prank(address(op)); - vm.expectRevert("Provided message has been blocked."); - messenger.relayMessage(address(0), address(0), hex"ff", 0); - - vm.prank(messenger.owner()); - messenger.allowMessage(msgHash); - - vm.prank(address(op)); - - vm.expectEmit(true, true, true, true); - emit RelayedMessage(msgHash); - messenger.relayMessage(address(0), address(0), hex"ff", 0); + L1Messenger.relayMessage(0, address(0), address(0), 0, 0, hex""); } } diff --git a/packages/contracts/contracts/test/L1StandardBridge.t.sol b/packages/contracts/contracts/test/L1StandardBridge.t.sol index 89e709c5..6eeb1537 100644 --- a/packages/contracts/contracts/test/L1StandardBridge.t.sol +++ b/packages/contracts/contracts/test/L1StandardBridge.t.sol @@ -1,102 +1,406 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; +import { Bridge_Initializer } from "./CommonTest.t.sol"; +import { StandardBridge } from "../universal/StandardBridge.sol"; +import { L2StandardBridge } from "../L2/L2StandardBridge.sol"; +import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol"; import { Lib_PredeployAddresses } from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; - -import { IWithdrawer } from "../L2/IWithdrawer.sol"; -import { Withdrawer } from "../L2/Withdrawer.sol"; -import { L2StandardBridge } from "../L2/messaging/L2StandardBridge.sol"; -import { L1StandardBridge } from "../L1/messaging/L1StandardBridge.sol"; -import { OptimismPortal } from "../L1/OptimismPortal.sol"; -import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol"; -import { L2StandardTokenFactory } from "../L2/messaging/L2StandardTokenFactory.sol"; -import { IL2StandardTokenFactory } from "../L2/messaging/IL2StandardTokenFactory.sol"; -import { L2StandardERC20 } from "../L2/tokens/L2StandardERC20.sol"; -import { IL2StandardERC20 } from "../L2/tokens/IL2StandardERC20.sol"; - +import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { CommonTest } from "./CommonTest.t.sol"; -import { L2OutputOracle_Initializer } from "./L2OutputOracle.t.sol"; -import { LibRLP } from "./Lib_RLP.t.sol"; - -contract L1StandardBridge_Test is CommonTest, L2OutputOracle_Initializer { - OptimismPortal op; +import { stdStorage, StdStorage } from "forge-std/Test.sol"; - IWithdrawer W; - L1StandardBridge L1Bridge; - L2StandardBridge L2Bridge; - IL2StandardTokenFactory L2TokenFactory; - IL2StandardERC20 L2Token; +import { console } from "forge-std/console.sol"; - function setUp() external { - L1Bridge = new L1StandardBridge(); - L2Bridge = new L2StandardBridge(address(L1Bridge)); - op = new OptimismPortal(oracle, 100); +contract L1StandardBridge_Test is Bridge_Initializer { + using stdStorage for StdStorage; - L1Bridge.initialize(op, address(L2Bridge)); - - Withdrawer w = new Withdrawer(); - vm.etch(Lib_BedrockPredeployAddresses.WITHDRAWER, address(w).code); - W = IWithdrawer(Lib_BedrockPredeployAddresses.WITHDRAWER); - - L2StandardTokenFactory factory = new L2StandardTokenFactory(); - vm.etch(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, address(factory).code); - L2TokenFactory = IL2StandardTokenFactory(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY); + function setUp() public override { + super.setUp(); + } - ERC20 token = new ERC20("Test Token", "TT"); + function test_initialize() external { + assertEq( + address(L1Bridge.messenger()), + address(L1Messenger) + ); - // Deploy the L2 ERC20 now - L2TokenFactory.createStandardL2Token( - address(token), - string(abi.encodePacked("L2-", token.name())), - string(abi.encodePacked("L2-", token.symbol())) + assertEq( + address(L1Bridge.otherBridge()), + Lib_PredeployAddresses.L2_STANDARD_BRIDGE ); - L2Token = IL2StandardERC20( - LibRLP.computeAddress(address(L2TokenFactory), 0) + assertEq( + address(L2Bridge), + Lib_PredeployAddresses.L2_STANDARD_BRIDGE ); } - function test_L1BridgeSetsPortalAndL2Bridge() external { - OptimismPortal portal = L1Bridge.optimismPortal(); - address bridge = L1Bridge.l2TokenBridge(); + // receive + // - can accept ETH + function test_receive() external { + assertEq(address(op).balance, 0); + + vm.expectEmit(true, true, true, true); + emit ETHBridgeInitiated(alice, alice, 100, hex""); + + vm.expectCall( + address(L1Messenger), + abi.encodeWithSelector( + CrossDomainMessenger.sendMessage.selector, + address(L2Bridge), + abi.encodeWithSelector( + StandardBridge.finalizeBridgeETH.selector, + alice, + alice, + 100, + hex"" + ), + 200_000 + ) + ); - assertEq(address(portal), address(op)); - assertEq(bridge, address(L2Bridge)); + vm.prank(alice, alice); + address(L1Bridge).call{ value: 100 }(hex""); + assertEq(address(op).balance, 100); } - // receive - // - can accept ETH // depositETH // - emits ETHDepositInitiated // - calls optimismPortal.depositTransaction // - only EOA // - ETH ends up in the optimismPortal + function test_depositETH() external { + assertEq(address(op).balance, 0); + + vm.expectEmit(true, true, true, true); + emit ETHBridgeInitiated(alice, alice, 500, hex"ff"); + + vm.expectCall( + address(L1Messenger), + abi.encodeWithSelector( + CrossDomainMessenger.sendMessage.selector, + address(L2Bridge), + abi.encodeWithSelector( + StandardBridge.finalizeBridgeETH.selector, + alice, + alice, + 500, + hex"ff" + ), + 50000 + ) + ); + + vm.prank(alice, alice); + L1Bridge.depositETH{ value: 500 }(50000, hex"ff"); + assertEq(address(op).balance, 500); + } + + function test_onlyEOADepositETH() external { + // turn alice into a contract + vm.etch(alice, address(L1Token).code); + + vm.expectRevert("Account not EOA"); + vm.prank(alice); + L1Bridge.depositETH{ value: 1 }(300, hex""); + } + // depositETHTo // - emits ETHDepositInitiated // - calls optimismPortal.depositTransaction // - EOA or contract can call // - ETH ends up in the optimismPortal + function test_depositETHTo() external { + assertEq(address(op).balance, 0); + + vm.expectEmit(true, true, true, true); + emit ETHDepositInitiated(alice, bob, 600, hex"dead"); + + vm.expectEmit(true, true, true, true); + emit ETHBridgeInitiated(alice, bob, 600, hex"dead"); + + // depositETHTo on the L1 bridge should be called + vm.expectCall( + address(L1Bridge), + abi.encodeWithSelector( + L1Bridge.depositETHTo.selector, + bob, + 1000, + hex"dead" + ) + ); + + // the L1 bridge should call + // L1CrossDomainMessenger.sendMessage + vm.expectCall( + address(L1Messenger), + abi.encodeWithSelector( + CrossDomainMessenger.sendMessage.selector, + address(L2Bridge), + abi.encodeWithSelector( + StandardBridge.finalizeBridgeETH.selector, + alice, + bob, + 600, + hex"dead" + ), + 1000 + ) + ); + + // TODO: assert on OptimismPortal being called + // and the event being emitted correctly + + // deposit eth to bob + vm.prank(alice, alice); + L1Bridge.depositETHTo{ value: 600 }(bob, 1000, hex"dead"); + } + // depositERC20 // - updates bridge.deposits // - emits ERC20DepositInitiated // - calls optimismPortal.depositTransaction // - only callable by EOA + function test_depositERC20() external { + vm.expectEmit(true, true, true, true); + emit ERC20DepositInitiated( + address(L1Token), + address(L2Token), + alice, + alice, + 100, + hex"" + ); + + deal(address(L1Token), alice, 100000, true); + + vm.prank(alice); + L1Token.approve(address(L1Bridge), type(uint256).max); + + // The L1Bridge should transfer alice's tokens + // to itself + vm.expectCall( + address(L1Token), + abi.encodeWithSelector( + ERC20.transferFrom.selector, + alice, + address(L1Bridge), + 100 + ) + ); + + // TODO: optimismPortal.depositTransaction call + event + + vm.prank(alice); + L1Bridge.depositERC20( + address(L1Token), + address(L2Token), + 100, + 10000, + hex"" + ); + + assertEq(L1Bridge.deposits(address(L1Token), address(L2Token)), 100); + } + + function test_onlyEOADepositERC20() external { + // turn alice into a contract + vm.etch(alice, hex"ffff"); + + vm.expectRevert("Account not EOA"); + vm.prank(alice, alice); + L1Bridge.depositERC20( + address(0), + address(0), + 100, + 100, + hex"" + ); + } + // depositERC20To // - updates bridge.deposits // - emits ERC20DepositInitiated // - calls optimismPortal.depositTransaction - // - reverts if called by EOA // - callable by a contract + function test_depositERC20To() external { + vm.expectEmit(true, true, true, true); + emit ERC20DepositInitiated( + address(L1Token), + address(L2Token), + alice, + bob, + 1000, + hex"" + ); + + deal(address(L1Token), alice, 100000, true); + + vm.prank(alice); + L1Token.approve(address(L1Bridge), type(uint256).max); + + vm.expectCall( + address(L1Token), + abi.encodeWithSelector( + ERC20.transferFrom.selector, + alice, + address(L1Bridge), + 1000 + ) + ); + + vm.prank(alice); + L1Bridge.depositERC20To( + address(L1Token), + address(L2Token), + bob, + 1000, + 10000, + hex"" + ); + + assertEq(L1Bridge.deposits(address(L1Token), address(L2Token)), 1000); + } + // finalizeETHWithdrawal // - emits ETHWithdrawalFinalized // - only callable by L2 bridge + function test_finalizeETHWithdrawal() external { + uint256 aliceBalance = alice.balance; + + vm.expectEmit(true, true, true, true); + emit ETHWithdrawalFinalized( + alice, + alice, + 100, + hex"" + ); + + vm.expectCall( + alice, + hex"" + ); + + vm.mockCall( + address(L1Bridge.messenger()), + abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), + abi.encode(address(L1Bridge.otherBridge())) + ); + // ensure that the messenger has ETH to call with + vm.deal(address(L1Bridge.messenger()), 100); + vm.prank(address(L1Bridge.messenger())); + L1Bridge.finalizeETHWithdrawal{ value: 100 }( + alice, + alice, + 100, + hex"" + ); + + assertEq(address(L1Bridge.messenger()).balance, 0); + assertEq(aliceBalance + 100, alice.balance); + } + // finalizeERC20Withdrawal // - updates bridge.deposits // - emits ERC20WithdrawalFinalized // - only callable by L2 bridge + function test_finalizeERC20Withdrawal() external { + deal(address(L1Token), address(L1Bridge), 100, true); + + uint256 slot = stdstore + .target(address(L1Bridge)) + .sig("deposits(address,address)") + .with_key(address(L1Token)) + .with_key(address(L2Token)) + .find(); + + // Give the L1 bridge some ERC20 tokens + vm.store(address(L1Bridge), bytes32(slot), bytes32(uint256(100))); + assertEq(L1Bridge.deposits(address(L1Token), address(L2Token)), 100); + + vm.expectEmit(true, true, true, true); + emit ERC20WithdrawalFinalized( + address(L1Token), + address(L2Token), + alice, + alice, + 100, + hex"" + ); + + vm.expectCall( + address(L1Token), + abi.encodeWithSelector( + ERC20.transfer.selector, + alice, + 100 + ) + ); + + vm.mockCall( + address(L1Bridge.messenger()), + abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), + abi.encode(address(L1Bridge.otherBridge())) + ); + vm.prank(address(L1Bridge.messenger())); + L1Bridge.finalizeERC20Withdrawal( + address(L1Token), + address(L2Token), + alice, + alice, + 100, + hex"" + ); + + assertEq(L1Token.balanceOf(address(L1Bridge)), 0); + assertEq(L1Token.balanceOf(address(alice)), 100); + } + + function test_onlyPortalFinalizeERC20Withdrawal() external { + vm.mockCall( + address(L1Bridge.messenger()), + abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), + abi.encode(address(L1Bridge.otherBridge())) + ); + vm.prank(address(28)); + vm.expectRevert("Could not authenticate bridge message."); + L1Bridge.finalizeERC20Withdrawal( + address(L1Token), + address(L2Token), + alice, + alice, + 100, + hex"" + ); + } + + function test_onlyL2BridgeFinalizeERC20Withdrawal() external { + vm.mockCall( + address(L1Bridge.messenger()), + abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), + abi.encode(address(address(0))) + ); + vm.prank(address(L1Bridge.messenger())); + vm.expectRevert("Could not authenticate bridge message."); + L1Bridge.finalizeERC20Withdrawal( + address(L1Token), + address(L2Token), + alice, + alice, + 100, + hex"" + ); + } + // donateETH // - can send ETH to the contract + function test_donateETH() external { + assertEq(address(L1Bridge).balance, 0); + vm.prank(alice); + L1Bridge.donateETH{ value: 1000 }(); + assertEq(address(L1Bridge).balance, 1000); + } } diff --git a/packages/contracts/contracts/test/L2CrossDomainMessenger.t.sol b/packages/contracts/contracts/test/L2CrossDomainMessenger.t.sol index 708839aa..2c427b71 100644 --- a/packages/contracts/contracts/test/L2CrossDomainMessenger.t.sol +++ b/packages/contracts/contracts/test/L2CrossDomainMessenger.t.sol @@ -1,8 +1,7 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; -import { CommonTest } from "./CommonTest.t.sol"; -import { L2OutputOracle_Initializer } from "./L2OutputOracle.t.sol"; +import { Messenger_Initializer } from "./CommonTest.t.sol"; import { Lib_PredeployAddresses @@ -12,13 +11,12 @@ import { } from "@eth-optimism/contracts/libraries/bridge/Lib_CrossDomainUtils.sol"; import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; +import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; -import { OptimismPortal } from "../L1/OptimismPortal.sol"; -import { L2CrossDomainMessenger } from "../L2/messaging/L2CrossDomainMessenger.sol"; -import { L1CrossDomainMessenger } from "../L1/messaging/L1CrossDomainMessenger.sol"; -import { Withdrawer } from "../L2/Withdrawer.sol"; -import { IWithdrawer } from "../L2/IWithdrawer.sol"; +import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol"; +import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol"; import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol"; +import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol"; import { Lib_DefaultValues @@ -26,180 +24,156 @@ import { import { console } from "forge-std/console.sol"; -contract L2CrossDomainMessenger_Test is CommonTest, L2OutputOracle_Initializer { - - // Dependencies - OptimismPortal op; - - IWithdrawer W; - L1CrossDomainMessenger L1Messenger; - L2CrossDomainMessenger L2Messenger; - - event SentMessage( - address indexed target, - address sender, - bytes message, - uint256 messageNonce, - uint256 gasLimit - ); - - event WithdrawalInitiated( - uint256 indexed nonce, - address indexed sender, - address indexed target, - uint256 value, - uint256 gasLimit, - bytes data - ); - - function setUp() external { - op = new OptimismPortal(oracle, 100); - L1Messenger = new L1CrossDomainMessenger(); - L1Messenger.initialize(op, Lib_PredeployAddresses.L2_CROSS_DOMAIN_MESSENGER); - - L2Messenger = new L2CrossDomainMessenger(address(L1Messenger)); - - // Deploy the Withdrawer and then get its code to set at the - // correct address - Withdrawer w = new Withdrawer(); - bytes memory code = address(w).code; - vm.etch(Lib_BedrockPredeployAddresses.WITHDRAWER, code); - W = IWithdrawer(Lib_BedrockPredeployAddresses.WITHDRAWER); +contract L2CrossDomainMessenger_Test is Messenger_Initializer { + // Receiver address for testing + address recipient = address(0xabbaacdc); + + function setUp() public override { + super.setUp(); } - // xDomainMessageSender: should return correct L1Messenger address - function test_L2MessengerCorrectL1Messenger() external { - address l1 = L2Messenger.l1CrossDomainMessenger(); - assertEq(l1, address(L1Messenger)); + function test_L2MessengerPause() external { + L2Messenger.pause(); + assert(L2Messenger.paused()); } - // xDomainMessageSender: should return the xDomainMsgSender address - function test_L2MessengerxDomainMsgSender() external { - vm.expectRevert("xDomainMessageSender is not set"); - L2Messenger.xDomainMessageSender(); + function testCannot_L2MessengerPause() external { + vm.expectRevert("Ownable: caller is not the owner"); + vm.prank(address(0xABBA)); + L2Messenger.pause(); + } - bytes32 slot = vm.load(address(L2Messenger), bytes32(uint256(4))); - assertEq(address(uint160(uint256(slot))), Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER); + function test_L2MessengerMessageVersion() external { + assertEq( + CrossDomainHashing.getVersionFromNonce(L2Messenger.messageNonce()), + L2Messenger.MESSAGE_VERSION() + ); } - // sendMessage: should be able to send a single message function test_L2MessengerSendMessage() external { - address target = address(0); - bytes memory message = hex""; - uint32 gasLimit = 1000; - - uint256 nonce = W.nonce(); - address sender = address(L2Messenger); + vm.expectCall( + address(messagePasser), + abi.encodeWithSelector( + L2ToL1MessagePasser.initiateWithdrawal.selector, + address(L1Messenger), + 100, + CrossDomainHashing.getVersionedEncoding( + L2Messenger.messageNonce(), + alice, + recipient, + 0, + 100, + hex"ff" + ) + ) + ); + // WithdrawalInitiated event vm.expectEmit(true, true, true, true); - emit SentMessage(target, address(this), message, nonce, gasLimit); - vm.expectEmit(true, true, true, true); - emit WithdrawalInitiated(nonce, sender, address(L1Messenger), 0, gasLimit, message); + emit WithdrawalInitiated( + messagePasser.nonce(), + address(L2Messenger), + address(L1Messenger), + 0, + 100, + CrossDomainHashing.getVersionedEncoding( + L2Messenger.messageNonce(), + alice, + recipient, + 0, + 100, + hex"ff" + ) + ); - L2Messenger.sendMessage(target, message, gasLimit); + vm.prank(alice); + L2Messenger.sendMessage(recipient, hex"ff", uint32(100)); } - // sendMessage: should be able to send the same message twice - function test_L2MessengerSendSameMessageTwice() external { - L2Messenger.sendMessage(address(0), hex"", 1000); - L2Messenger.sendMessage(address(0), hex"", 1000); - // TODO: assertion on events, nonce increments + function test_L2MessengerTwiceSendMessage() external { + uint256 nonce = L2Messenger.messageNonce(); + L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000)); + L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000)); + // the nonce increments for each message sent + assertEq( + nonce + 2, + L2Messenger.messageNonce() + ); } - // relayMessage: should revert if the L1 message sender is not the L1CrossDomainMessenger - function test_L2MessengerRevertInvalidL1XDomainMessenger() external { - vm.expectRevert("Provided message could not be verified."); - vm.prank(address(0)); - L2Messenger.relayMessage( - address(0), - address(0), - hex"", - 0 - ); + function test_L2MessengerXDomainSenderReverts() external { + vm.expectRevert("xDomainMessageSender is not set"); + L2Messenger.xDomainMessageSender(); } - // relayMessage: should send a call to the target contract - function test_L2MessengerCallsTarget() external { - address target = address(4); + function test_L2MessengerRelayMessageSucceeds() external { + address target = address(0xabcd); + address sender = address(L1Messenger); + address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)); - vm.expectCall(target, hex"ff"); - vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger))); - L2Messenger.relayMessage( + vm.expectCall(target, hex"1111"); + + vm.prank(caller); + + vm.expectEmit(true, true, true, true); + + bytes32 hash = CrossDomainHashing.getVersionedHash( + 0, + sender, target, - address(this), - hex"ff", - 1000 + 0, + 0, + hex"1111" ); - } - // relayMessage: the xDomainMessageSender is reset to the original value - function test_L2MessengerXDomainMessageSenderReset() external { - vm.expectRevert("xDomainMessageSender is not set"); - L2Messenger.xDomainMessageSender(); + emit RelayedMessage(hash); - vm.expectCall(address(4), hex"ff"); - vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger))); L2Messenger.relayMessage( - address(4), - address(this), - hex"ff", - 1000 + 0, // nonce + sender, + target, + 0, // value + 0, + hex"1111" ); - vm.expectRevert("xDomainMessageSender is not set"); - L2Messenger.xDomainMessageSender(); - bytes32 slot = vm.load(address(L2Messenger), bytes32(uint256(4))); - assertEq(address(uint160(uint256(slot))), Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER); + // the message hash is in the successfulMessages mapping + assert(L2Messenger.successfulMessages(hash)); + // it is not in the received messages mapping + assertEq(L2Messenger.receivedMessages(hash), false); } - // relayMessage: should revert if trying to send the same message twice - function test_L2MessengerCannotRelaySameMessageTwice() external { - vm.expectCall(address(4), hex"ff"); - vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger))); - L2Messenger.relayMessage( - address(4), - address(this), - hex"ff", - 1000 - ); + // relayMessage: should revert if attempting to relay a message sent to an L1 system contract + function test_L2MessengerRelayMessageToSystemContract() external { + address target = address(messagePasser); + address sender = address(L1Messenger); + address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)); + bytes memory message = hex"1111"; - vm.expectRevert("Provided message has already been received."); - vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger))); - L2Messenger.relayMessage( - address(4), - address(this), - hex"ff", - 1000 - ); + vm.prank(caller); + vm.expectRevert("Message cannot be replayed."); + L1Messenger.relayMessage(0, sender, target, 0, 0, message); } - // relayMessage: should not make a call if the target is the L2 MessagePasser - function test_L2MessengerCannotCallL2MessagePasser() external { - address target = Lib_BedrockPredeployAddresses.WITHDRAWER; + // relayMessage: the xDomainMessageSender is reset to the original value + function test_L2MessengerxDomainMessageSenderResets() external { + vm.expectRevert("xDomainMessageSender is not set"); + L2Messenger.xDomainMessageSender(); - vm.prank(AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger))); - L2Messenger.relayMessage( - target, - address(this), - hex"ff", - 1000 - ); + address caller = AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)); + vm.prank(caller); + L2Messenger.relayMessage(0, address(0), address(0), 0, 0, hex""); - bytes memory xDomainCalldata = Lib_CrossDomainUtils.encodeXDomainCalldata( - target, - address(this), - hex"ff", - 1000 - ); - bytes32 hash = keccak256(xDomainCalldata); - assert(L2Messenger.successfulMessages(hash) == true); + vm.expectRevert("xDomainMessageSender is not set"); + L2Messenger.xDomainMessageSender(); + } - bytes32 relayId = keccak256(abi.encodePacked( - xDomainCalldata, - AddressAliasHelper.applyL1ToL2Alias(address(L1Messenger)), - block.number - )); + // relayMessage: should revert if paused + function test_L2MessengerRelayShouldRevertIfPaused() external { + vm.prank(L2Messenger.owner()); + L2Messenger.pause(); - assert(L2Messenger.relayedMessages(relayId) == false); + vm.expectRevert("Pausable: paused"); + L2Messenger.relayMessage(0, address(0), address(0), 0, 0, hex""); } } diff --git a/packages/contracts/contracts/test/L2OutputOracle.t.sol b/packages/contracts/contracts/test/L2OutputOracle.t.sol index 3a45339e..b012189c 100644 --- a/packages/contracts/contracts/test/L2OutputOracle.t.sol +++ b/packages/contracts/contracts/test/L2OutputOracle.t.sol @@ -1,49 +1,16 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; -/* Testing utilities */ -import { CommonTest } from "./CommonTest.t.sol"; - +import { L2OutputOracle_Initializer } from "./CommonTest.t.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; -contract L2OutputOracle_Initializer is CommonTest { - // Utility variables - uint256 appendedTimestamp; - - // Test target - L2OutputOracle oracle; - - // Constructor arguments - address sequencer = 0x000000000000000000000000000000000000AbBa; - uint256 submissionInterval = 1800; - uint256 l2BlockTime = 2; - bytes32 genesisL2Output = keccak256(abi.encode(0)); - uint256 historicalTotalBlocks = 100; - - // Cache of the initial L2 timestamp - uint256 startingBlockTimestamp; - - // By default the first block has timestamp zero, which will cause underflows in the tests - uint256 initTime = 1000; - - constructor() { - // Move time forward so we have a non-zero starting timestamp - vm.warp(initTime); - // Deploy the L2OutputOracle and transfer owernship to the sequencer - oracle = new L2OutputOracle( - submissionInterval, - l2BlockTime, - genesisL2Output, - historicalTotalBlocks, - initTime, - sequencer - ); - startingBlockTimestamp = block.timestamp; +contract L2OutputOracleTest is L2OutputOracle_Initializer { + bytes32 appendedOutput1 = keccak256(abi.encode(1)); + + function setUp() public override { + super.setUp(); } -} -// Define this test in a standalone contract to ensure it runs immediately after the constructor. -contract L2OutputOracleTest_Constructor is L2OutputOracle_Initializer { function test_constructor() external { assertEq(oracle.owner(), sequencer); assertEq(oracle.SUBMISSION_INTERVAL(), submissionInterval); @@ -51,20 +18,10 @@ contract L2OutputOracleTest_Constructor is L2OutputOracle_Initializer { assertEq(oracle.HISTORICAL_TOTAL_BLOCKS(), historicalTotalBlocks); assertEq(oracle.latestBlockTimestamp(), startingBlockTimestamp); assertEq(oracle.STARTING_BLOCK_TIMESTAMP(), startingBlockTimestamp); - assertEq(oracle.getL2Output(startingBlockTimestamp), genesisL2Output); - } -} - -contract L2OutputOracleTest is L2OutputOracle_Initializer { - bytes32 appendedOutput1 = keccak256(abi.encode(1)); - - constructor() { - appendedTimestamp = oracle.nextTimestamp(); - // Warp to after the timestamp we'll append - vm.warp(appendedTimestamp + 1); - vm.prank(sequencer); - oracle.appendL2Output(appendedOutput1, appendedTimestamp, 0, 0); + L2OutputOracle.OutputProposal memory proposal = oracle.getL2Output(startingBlockTimestamp); + assertEq(proposal.outputRoot, genesisL2Output); + assertEq(proposal.timestamp, initTime); } /**************** @@ -73,13 +30,30 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer { // Test: latestBlockTimestamp() should return the correct value function test_latestBlockTimestamp() external { + uint256 appendedTimestamp = oracle.nextTimestamp(); + + // Warp to after the timestamp we'll append + vm.warp(appendedTimestamp + 1); + vm.prank(sequencer); + oracle.appendL2Output(appendedOutput1, appendedTimestamp, 0, 0); assertEq(oracle.latestBlockTimestamp(), appendedTimestamp); } // Test: getL2Output() should return the correct value function test_getL2Output() external { - assertEq(oracle.getL2Output(appendedTimestamp), appendedOutput1); - assertEq(oracle.getL2Output(appendedTimestamp + 1), 0); + uint256 nextTimestamp = oracle.nextTimestamp(); + + vm.warp(nextTimestamp + 1); + vm.prank(sequencer); + oracle.appendL2Output(appendedOutput1, nextTimestamp, 0, 0); + + L2OutputOracle.OutputProposal memory proposal = oracle.getL2Output(nextTimestamp); + assertEq(proposal.outputRoot, appendedOutput1); + assertEq(proposal.timestamp, nextTimestamp + 1); + + L2OutputOracle.OutputProposal memory proposal2 = oracle.getL2Output(0); + assertEq(proposal2.outputRoot, bytes32(0)); + assertEq(proposal2.timestamp, 0); } @@ -88,7 +62,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer { assertEq( oracle.nextTimestamp(), // The return value should match this arithmetic - initTime + submissionInterval * 2 + oracle.latestBlockTimestamp() + oracle.SUBMISSION_INTERVAL() ); } @@ -109,6 +83,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer { expected = historicalTotalBlocks + 1 + (33 / l2BlockTime); assertEq(oracle.computeL2BlockNumber(argTimestamp), expected); } + // Test: computeL2BlockNumber() fails with a blockNumber from before the startingBlockTimestamp function testCannot_computePreHistoricalL2BlockNumber() external { bytes memory expectedError = "Timestamp prior to startingBlockTimestamp"; @@ -123,10 +98,12 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer { // Test: appendL2Output succeeds when given valid input, and no block hash and number are // specified. - function test_appendingAnotherOutput() external { + function test_appendingAnotherOutput() public { bytes32 appendedOutput2 = keccak256(abi.encode(2)); uint256 nextTimestamp = oracle.nextTimestamp(); + uint256 appendedTimestamp = oracle.latestBlockTimestamp(); + // Ensure the submissionInterval is enforced assertEq(nextTimestamp, appendedTimestamp + submissionInterval); @@ -164,6 +141,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer { function testCannot_appendOutputIfNotSequencer() external { uint256 nextTimestamp = oracle.nextTimestamp(); + vm.prank(address(128)); vm.warp(nextTimestamp + 1); vm.expectRevert("Ownable: caller is not the owner"); oracle.appendL2Output(nonZeroHash, nextTimestamp, 0, 0); @@ -230,16 +208,27 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer { * Delete Tests * ****************/ - event l2OutputDeleted(bytes32 indexed _l2Output, uint256 indexed _l2timestamp); + event l2OutputDeleted( + bytes32 indexed _l2Output, + uint256 indexed _l1Timestamp, + uint256 indexed _l2timestamp + ); + function test_deleteL2Output() external { + test_appendingAnotherOutput(); + uint256 latestBlockTimestamp = oracle.latestBlockTimestamp(); - bytes32 outputToDelete = oracle.getL2Output(latestBlockTimestamp); - bytes32 newLatestOutput = oracle.getL2Output(latestBlockTimestamp - submissionInterval); + L2OutputOracle.OutputProposal memory proposalToDelete = oracle.getL2Output(latestBlockTimestamp); + L2OutputOracle.OutputProposal memory newLatestOutput = oracle.getL2Output(latestBlockTimestamp - submissionInterval); vm.prank(sequencer); vm.expectEmit(true, true, false, false); - emit l2OutputDeleted(outputToDelete, latestBlockTimestamp); - oracle.deleteL2Output(outputToDelete); + emit l2OutputDeleted( + proposalToDelete.outputRoot, + proposalToDelete.timestamp, + latestBlockTimestamp + ); + oracle.deleteL2Output(proposalToDelete); // validate latestBlockTimestamp has been reduced uint256 latestBlockTimestampAfter = oracle.latestBlockTimestamp(); @@ -248,27 +237,28 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer { latestBlockTimestampAfter ); + L2OutputOracle.OutputProposal memory proposal = oracle.getL2Output(latestBlockTimestampAfter); // validate that the new latest output is as expected. - assertEq( - newLatestOutput, - oracle.getL2Output(latestBlockTimestampAfter) - ); + assertEq(newLatestOutput.outputRoot, proposal.outputRoot); + assertEq(newLatestOutput.timestamp, proposal.timestamp); } function testCannot_deleteL2Output_ifNotSequencer() external { uint256 latestBlockTimestamp = oracle.latestBlockTimestamp(); - bytes32 outputToDelete = oracle.getL2Output(latestBlockTimestamp); + L2OutputOracle.OutputProposal memory proposal = oracle.getL2Output(latestBlockTimestamp); vm.expectRevert("Ownable: caller is not the owner"); - oracle.deleteL2Output(outputToDelete); + oracle.deleteL2Output(proposal); } - function testCannot_deleteL2Output_ifWrongOutput() external { + function testCannot_deleteWrongL2Output() external { + test_appendingAnotherOutput(); + uint256 previousBlockTimestamp = oracle.latestBlockTimestamp() - submissionInterval; - bytes32 outputToDelete = oracle.getL2Output(previousBlockTimestamp); + L2OutputOracle.OutputProposal memory proposalToDelete = oracle.getL2Output(previousBlockTimestamp); vm.prank(sequencer); vm.expectRevert("Can only delete the most recent output."); - oracle.deleteL2Output(outputToDelete); + oracle.deleteL2Output(proposalToDelete); } } diff --git a/packages/contracts/contracts/test/L2StandardBridge.t.sol b/packages/contracts/contracts/test/L2StandardBridge.t.sol index 777c10d0..2ce6a036 100644 --- a/packages/contracts/contracts/test/L2StandardBridge.t.sol +++ b/packages/contracts/contracts/test/L2StandardBridge.t.sol @@ -1,83 +1,113 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; -import { - Lib_PredeployAddresses -} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; - -import { IWithdrawer } from "../L2/IWithdrawer.sol"; -import { Withdrawer } from "../L2/Withdrawer.sol"; -import { L2StandardBridge } from "../L2/messaging/L2StandardBridge.sol"; -import { L1StandardBridge } from "../L1/messaging/L1StandardBridge.sol"; -import { OptimismPortal } from "../L1/OptimismPortal.sol"; -import { Lib_BedrockPredeployAddresses } from "../libraries/Lib_BedrockPredeployAddresses.sol"; -import { L2StandardTokenFactory } from "../L2/messaging/L2StandardTokenFactory.sol"; -import { IL2StandardTokenFactory } from "../L2/messaging/IL2StandardTokenFactory.sol"; -import { L2StandardERC20 } from "../L2/tokens/L2StandardERC20.sol"; -import { IL2StandardERC20 } from "../L2/tokens/IL2StandardERC20.sol"; - -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { CommonTest } from "./CommonTest.t.sol"; -import { L2OutputOracle_Initializer } from "./L2OutputOracle.t.sol"; -import { LibRLP } from "./Lib_RLP.t.sol"; - +import { Bridge_Initializer } from "./CommonTest.t.sol"; +import { stdStorage, StdStorage } from "forge-std/Test.sol"; +import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol"; import { console } from "forge-std/console.sol"; -contract L2StandardBridge_Test is CommonTest, L2OutputOracle_Initializer { - OptimismPortal op; - - IWithdrawer W; - L1StandardBridge L1Bridge; - L2StandardBridge L2Bridge; - IL2StandardTokenFactory L2TokenFactory; - IL2StandardERC20 L2Token; - - function setUp() external { - L1Bridge = new L1StandardBridge(); - L2Bridge = new L2StandardBridge(address(L1Bridge)); - op = new OptimismPortal(oracle, 100); - - L1Bridge.initialize(op, address(L2Bridge)); - - Withdrawer w = new Withdrawer(); - vm.etch(Lib_BedrockPredeployAddresses.WITHDRAWER, address(w).code); - W = IWithdrawer(Lib_BedrockPredeployAddresses.WITHDRAWER); - - L2StandardTokenFactory factory = new L2StandardTokenFactory(); - vm.etch(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY, address(factory).code); - L2TokenFactory = IL2StandardTokenFactory(Lib_PredeployAddresses.L2_STANDARD_TOKEN_FACTORY); +contract L2StandardBridge_Test is Bridge_Initializer { + using stdStorage for StdStorage; - ERC20 token = new ERC20("Test Token", "TT"); + function setUp() public override { + super.setUp(); + } - // Deploy the L2 ERC20 now - L2TokenFactory.createStandardL2Token( - address(token), - string(abi.encodePacked("L2-", token.name())), - string(abi.encodePacked("L2-", token.symbol())) + function test_initialize() external { + assertEq( + address(L2Bridge.messenger()), + address(L2Messenger) ); - L2Token = IL2StandardERC20( - LibRLP.computeAddress(address(L2TokenFactory), 0) + assertEq( + address(L2Bridge.otherBridge()), + address(L1Bridge) ); } - function test_L2BridgeCorrectL1Bridge() external { - address l1Bridge = L2Bridge.l1TokenBridge(); - assertEq(address(L1Bridge), l1Bridge); + // receive + // - can accept ETH + function test_receive() external { + assertEq(address(messagePasser).balance, 0); + + vm.expectEmit(true, true, true, true); + emit ETHBridgeInitiated(alice, alice, 100, hex""); + + // TODO: L2Messenger should be called + // TODO: L2ToL1MessagePasser should be called + // TODO: withdrawal hash should be computed correctly + // TODO: events from each contract + + vm.prank(alice, alice); + address(L2Bridge).call{ value: 100 }(hex""); + assertEq(address(messagePasser).balance, 100); } // withdraw // - token is burned // - emits WithdrawalInitiated // - calls Withdrawer.initiateWithdrawal + function test_withdraw() external { + // Alice has 100 L2Token + deal(address(L2Token), alice, 100, true); + assertEq(L2Token.balanceOf(alice), 100); + + vm.prank(alice, alice); + L2Bridge.withdraw( + address(L2Token), + 100, + 1000, + hex"" + ); + + // TODO: events and calls + + assertEq(L2Token.balanceOf(alice), 0); + } + // withdrawTo // - token is burned // - emits WithdrawalInitiated w/ correct recipient // - calls Withdrawer.initiateWithdrawal + function test_withdrawTo() external { + deal(address(L2Token), alice, 100, true); + + vm.prank(alice, alice); + L2Bridge.withdrawTo( + address(L2Token), + bob, + 100, + 1000, + hex"" + ); + + // TODO: events and calls + + assertEq(L2Token.balanceOf(alice), 0); + } + // finalizeDeposit // - only callable by l1TokenBridge // - supported token pair emits DepositFinalized // - invalid deposit emits DepositFailed // - invalid deposit calls Withdrawer.initiateWithdrawal + function test_finalizeDeposit() external { + // TODO: events and calls + + vm.mockCall( + address(L2Bridge.messenger()), + abi.encodeWithSelector(CrossDomainMessenger.xDomainMessageSender.selector), + abi.encode(address(L2Bridge.otherBridge())) + ); + vm.prank(address(L2Messenger)); + L2Bridge.finalizeDeposit( + address(L1Token), + address(L2Token), + alice, + alice, + 100, + hex"" + ); + } } diff --git a/packages/contracts/contracts/test/L2ToL1MessagePasser.t.sol b/packages/contracts/contracts/test/L2ToL1MessagePasser.t.sol new file mode 100644 index 00000000..d07aa103 --- /dev/null +++ b/packages/contracts/contracts/test/L2ToL1MessagePasser.t.sol @@ -0,0 +1,104 @@ +//SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import { CommonTest } from "./CommonTest.t.sol"; +import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; +import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; + +contract L2ToL1MessagePasserTest is CommonTest { + L2ToL1MessagePasser messagePasser; + + event WithdrawalInitiated( + uint256 indexed nonce, + address indexed sender, + address indexed target, + uint256 value, + uint256 gasLimit, + bytes data + ); + + event WithdrawerBalanceBurnt(uint256 indexed amount); + + function setUp() virtual public { + messagePasser = new L2ToL1MessagePasser(); + } + + // Test: initiateWithdrawal should emit the correct log when called by a contract + function test_initiateWithdrawal_fromContract() external { + vm.expectEmit(true, true, true, true); + emit WithdrawalInitiated( + messagePasser.nonce(), + address(this), + address(4), + 100, + 64000, + hex"" + ); + + vm.deal(address(this), 2**64); + messagePasser.initiateWithdrawal{ value: 100 }( + address(4), + 64000, + hex"" + ); + } + + // Test: initiateWithdrawal should emit the correct log when called by an EOA + function test_initiateWithdrawal_fromEOA() external { + uint256 gasLimit = 64000; + address target = address(4); + uint256 value = 100; + bytes memory data = hex"ff"; + uint256 nonce = messagePasser.nonce(); + + // EOA emulation + vm.prank(alice, alice); + vm.deal(alice, 2**64); + vm.expectEmit(true, true, true, true); + emit WithdrawalInitiated( + nonce, + alice, + target, + value, + gasLimit, + data + ); + + bytes32 withdrawalHash = WithdrawalVerifier.withdrawalHash( + nonce, + alice, + target, + value, + gasLimit, + data + ); + + messagePasser.initiateWithdrawal{ value: value }( + target, + gasLimit, + data + ); + + // the sent messages mapping is filled + assertEq(messagePasser.sentMessages(withdrawalHash), true); + // the nonce increments + assertEq(nonce + 1, messagePasser.nonce()); + } + + // Test: burn should destroy the ETH held in the contract + function test_burn() external { + messagePasser.initiateWithdrawal{ value: NON_ZERO_VALUE }( + NON_ZERO_ADDRESS, + NON_ZERO_GASLIMIT, + NON_ZERO_DATA + ); + + assertEq(address(messagePasser).balance, NON_ZERO_VALUE); + vm.expectEmit(true, false, false, false); + emit WithdrawerBalanceBurnt(NON_ZERO_VALUE); + messagePasser.burn(); + + // The Withdrawer should have no balance + assertEq(address(messagePasser).balance, 0); + } +} diff --git a/packages/contracts/contracts/test/OptimismMintableERC20.t.sol b/packages/contracts/contracts/test/OptimismMintableERC20.t.sol new file mode 100644 index 00000000..3dd49cf8 --- /dev/null +++ b/packages/contracts/contracts/test/OptimismMintableERC20.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import { Bridge_Initializer } from "./CommonTest.t.sol"; +import { LibRLP } from "./Lib_RLP.t.sol"; + +contract OptimismMintableTokenFactory_Test is Bridge_Initializer { + event Mint(address indexed _account, uint256 _amount); + event Burn(address indexed _account, uint256 _amount); + + function setUp() public override { + super.setUp(); + } + + function test_remoteToken() external { + assertEq(L2Token.remoteToken(), address(L1Token)); + } + + function test_bridge() external { + assertEq(L2Token.bridge(), address(L2Bridge)); + } + + function test_l1Token() external { + assertEq(L2Token.l1Token(), address(L1Token)); + } + + function test_l2Bridge() external { + assertEq(L2Token.l2Bridge(), address(L2Bridge)); + } + + function test_mint() external { + vm.expectEmit(true, true, true, true); + emit Mint(alice, 100); + + vm.prank(address(L2Bridge)); + L2Token.mint(alice, 100); + + assertEq(L2Token.balanceOf(alice), 100); + } + + function test_mintRevertsFromNotBridge() external { + // NOT the bridge + vm.expectRevert("Only L2 Bridge can mint and burn"); + vm.prank(address(alice)); + L2Token.mint(alice, 100); + } + + function test_burn() external { + vm.prank(address(L2Bridge)); + L2Token.mint(alice, 100); + + vm.expectEmit(true, true, true, true); + emit Burn(alice, 100); + + vm.prank(address(L2Bridge)); + L2Token.burn(alice, 100); + + assertEq(L2Token.balanceOf(alice), 0); + } + + function test_burnRevertsFromNotBridge() external { + // NOT the bridge + vm.expectRevert("Only L2 Bridge can mint and burn"); + vm.prank(address(alice)); + L2Token.burn(alice, 100); + } +} diff --git a/packages/contracts/contracts/test/OptimismMintableTokenFactory.t.sol b/packages/contracts/contracts/test/OptimismMintableTokenFactory.t.sol new file mode 100644 index 00000000..2b458a22 --- /dev/null +++ b/packages/contracts/contracts/test/OptimismMintableTokenFactory.t.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.10; + +import { Bridge_Initializer } from "./CommonTest.t.sol"; +import { LibRLP } from "./Lib_RLP.t.sol"; + +contract OptimismMintableTokenFactory_Test is Bridge_Initializer { + event StandardL2TokenCreated(address indexed _remoteToken, address indexed _localToken); + event OptimismMintableTokenCreated( + address indexed _localToken, + address indexed _remoteToken, + address _deployer + ); + + function setUp() public override { + super.setUp(); + } + + function test_initializeShouldRevert() external { + vm.expectRevert("Already initialized."); + L2TokenFactory.initialize(address(L1Bridge)); + } + + function test_bridge() external { + assertEq(address(L2TokenFactory.bridge()), address(L2Bridge)); + } + + function test_createStandardL2Token() external { + address remote = address(4); + address local = LibRLP.computeAddress(address(L2TokenFactory), 1); + + vm.expectEmit(true, true, true, true); + emit StandardL2TokenCreated( + remote, + local + ); + + vm.expectEmit(true, true, true, true); + emit OptimismMintableTokenCreated( + remote, + local, + alice + ); + + vm.prank(alice); + L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP"); + } + + function test_createStandardL2TokenSameTwice() external { + address remote = address(4); + + vm.prank(alice); + L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP"); + + address local = LibRLP.computeAddress(address(L2TokenFactory), 2); + + vm.expectEmit(true, true, true, true); + emit StandardL2TokenCreated( + remote, + local + ); + + vm.expectEmit(true, true, true, true); + emit OptimismMintableTokenCreated( + remote, + local, + alice + ); + + vm.prank(alice); + L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP"); + } + + function test_createStandardL2TokenShouldRevertIfRemoteIsZero() external { + address remote = address(0); + vm.expectRevert("Must provide L1 token address"); + L2TokenFactory.createStandardL2Token(remote, "Beep", "BOOP"); + } +} diff --git a/packages/contracts/contracts/test/OptimismPortal.t.sol b/packages/contracts/contracts/test/OptimismPortal.t.sol index c77b91e0..00dd74c0 100644 --- a/packages/contracts/contracts/test/OptimismPortal.t.sol +++ b/packages/contracts/contracts/test/OptimismPortal.t.sol @@ -1,14 +1,13 @@ //SPDX-License-Identifier: MIT pragma solidity 0.8.10; -/* Testing utilities */ +import { AddressAliasHelper } from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; + import { CommonTest } from "./CommonTest.t.sol"; -/* Target contract dependencies */ import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; - -/* Target contract */ import { OptimismPortal } from "../L1/OptimismPortal.sol"; +import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; contract OptimismPortal_Test is CommonTest { event TransactionDeposited( @@ -23,24 +22,279 @@ contract OptimismPortal_Test is CommonTest { // Dependencies L2OutputOracle oracle; - OptimismPortal op; function setUp() external { - // Oracle value is zero, but this test does not depend on it. + oracle = new L2OutputOracle( + 1800, + 2, + keccak256(abi.encode(0)), + 100, + 1, + address(666) + ); op = new OptimismPortal(oracle, 7 days); } - function test_receive_withEthValueFromEOA() external { - // EOA emulation - vm.prank(address(this), address(this)); + function test_OptimismPortalConstructor() external { + assertEq(op.FINALIZATION_PERIOD(), 7 days); + assertEq(address(op.L2_ORACLE()), address(oracle)); + assertEq(op.l2Sender(), 0x000000000000000000000000000000000000dEaD); + } + function test_OptimismPortalReceiveEth() external { vm.expectEmit(true, true, false, true); - emit TransactionDeposited(address(this), address(this), 100, 100, 30_000, false, hex""); + emit TransactionDeposited( + alice, + alice, + 100, + 100, + 100_000, + false, + hex"" + ); + // give alice money and send as an eoa + vm.deal(alice, 2**64); + vm.prank(alice, alice); (bool s, ) = address(op).call{ value: 100 }(hex""); - s; // Silence the compiler's "Return value of low-level calls not used" warning. + assert(s); assertEq(address(op).balance, 100); } + + // function test_OptimismPortalDepositTransaction() external {} + + // Test: depositTransaction fails when contract creation has a non-zero destination address + function test_OptimismPortalContractCreationReverts() external { + // contract creation must have a target of address(0) + vm.expectRevert(abi.encodeWithSignature("NonZeroCreationTarget()")); + op.depositTransaction(address(1), 1, 0, true, hex""); + } + + // Test: depositTransaction should emit the correct log when an EOA deposits a tx with 0 value + function test_depositTransaction_NoValueEOA() external { + // EOA emulation + vm.prank(address(this), address(this)); + vm.expectEmit(true, true, false, true); + emit TransactionDeposited( + address(this), + NON_ZERO_ADDRESS, + ZERO_VALUE, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + false, + NON_ZERO_DATA + ); + + op.depositTransaction( + NON_ZERO_ADDRESS, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + false, + NON_ZERO_DATA + ); + } + + // Test: depositTransaction should emit the correct log when a contract deposits a tx with 0 value + function test_depositTransaction_NoValueContract() external { + vm.expectEmit(true, true, false, true); + emit TransactionDeposited( + AddressAliasHelper.applyL1ToL2Alias(address(this)), + NON_ZERO_ADDRESS, + ZERO_VALUE, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + false, + NON_ZERO_DATA + ); + + op.depositTransaction( + NON_ZERO_ADDRESS, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + false, + NON_ZERO_DATA + ); + } + + // Test: depositTransaction should emit the correct log when an EOA deposits a contract creation with 0 value + function test_depositTransaction_createWithZeroValueForEOA() external { + // EOA emulation + vm.prank(address(this), address(this)); + + vm.expectEmit(true, true, false, true); + emit TransactionDeposited( + address(this), + ZERO_ADDRESS, + ZERO_VALUE, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + true, + NON_ZERO_DATA + ); + + op.depositTransaction(ZERO_ADDRESS, ZERO_VALUE, NON_ZERO_GASLIMIT, true, NON_ZERO_DATA); + } + + // Test: depositTransaction should emit the correct log when a contract deposits a contract creation with 0 value + function test_depositTransaction_createWithZeroValueForContract() external { + vm.expectEmit(true, true, false, true); + emit TransactionDeposited( + AddressAliasHelper.applyL1ToL2Alias(address(this)), + ZERO_ADDRESS, + ZERO_VALUE, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + true, + NON_ZERO_DATA + ); + + op.depositTransaction(ZERO_ADDRESS, ZERO_VALUE, NON_ZERO_GASLIMIT, true, NON_ZERO_DATA); + } + + // Test: depositTransaction should increase its eth balance when an EOA deposits a transaction with ETH + function test_depositTransaction_withEthValueFromEOA() external { + // EOA emulation + vm.prank(address(this), address(this)); + + vm.expectEmit(true, true, false, true); + emit TransactionDeposited( + address(this), + NON_ZERO_ADDRESS, + NON_ZERO_VALUE, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + false, + NON_ZERO_DATA + ); + + op.depositTransaction{ value: NON_ZERO_VALUE }( + NON_ZERO_ADDRESS, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + false, + NON_ZERO_DATA + ); + assertEq(address(op).balance, NON_ZERO_VALUE); + } + + // Test: depositTransaction should increase its eth balance when a contract deposits a transaction with ETH + function test_depositTransaction_withEthValueFromContract() external { + vm.expectEmit(true, true, false, true); + emit TransactionDeposited( + AddressAliasHelper.applyL1ToL2Alias(address(this)), + NON_ZERO_ADDRESS, + NON_ZERO_VALUE, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + false, + NON_ZERO_DATA + ); + + op.depositTransaction{ value: NON_ZERO_VALUE }( + NON_ZERO_ADDRESS, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + false, + NON_ZERO_DATA + ); + } + + // Test: depositTransaction should increase its eth balance when an EOA deposits a contract creation with ETH + function test_depositTransaction_withEthValueAndEOAContractCreation() external { + // EOA emulation + vm.prank(address(this), address(this)); + + vm.expectEmit(true, true, false, true); + emit TransactionDeposited( + address(this), + ZERO_ADDRESS, + NON_ZERO_VALUE, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + true, + hex"" + ); + + op.depositTransaction{ value: NON_ZERO_VALUE }( + ZERO_ADDRESS, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + true, + hex"" + ); + assertEq(address(op).balance, NON_ZERO_VALUE); + } + + // Test: depositTransaction should increase its eth balance when a contract deposits a contract creation with ETH + function test_depositTransaction_withEthValueAndContractContractCreation() external { + vm.expectEmit(true, true, false, true); + emit TransactionDeposited( + AddressAliasHelper.applyL1ToL2Alias(address(this)), + ZERO_ADDRESS, + NON_ZERO_VALUE, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + true, + NON_ZERO_DATA + ); + + op.depositTransaction{ value: NON_ZERO_VALUE }( + ZERO_ADDRESS, + ZERO_VALUE, + NON_ZERO_GASLIMIT, + true, + NON_ZERO_DATA + ); + assertEq(address(op).balance, NON_ZERO_VALUE); + } + + // TODO: test this deeply + // function test_verifyWithdrawal() external {} + + function test_cannotVerifyRecentWithdrawal() external { + WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier.OutputRootProof({ + version: bytes32(0), + stateRoot: bytes32(0), + withdrawerStorageRoot: bytes32(0), + latestBlockhash: bytes32(0) + }); + + vm.expectRevert("Proposal is not yet finalized."); + op.finalizeWithdrawalTransaction( + 0, + alice, + alice, + 0, + 0, + hex"", + 0, + outputRootProof, + hex"" + ); + } + + function test_invalidWithdrawalProof() external { + WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier.OutputRootProof({ + version: bytes32(0), + stateRoot: bytes32(0), + withdrawerStorageRoot: bytes32(0), + latestBlockhash: bytes32(0) + }); + + vm.warp(oracle.nextTimestamp() + op.FINALIZATION_PERIOD()); + vm.expectRevert(abi.encodeWithSignature("InvalidOutputRootProof()")); + op.finalizeWithdrawalTransaction( + 0, + alice, + alice, + 0, + 0, + hex"", + 0, + outputRootProof, + hex"" + ); + } } diff --git a/packages/contracts/contracts/test/WithdrawalsRelay.t.sol b/packages/contracts/contracts/test/WithdrawalsRelay.t.sol deleted file mode 100644 index 7031c123..00000000 --- a/packages/contracts/contracts/test/WithdrawalsRelay.t.sol +++ /dev/null @@ -1,174 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity 0.8.10; - -/* Testing utilities */ -import { CommonTest } from "./CommonTest.t.sol"; - -/* Target contract dependencies */ -import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; -import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; - -/* Target contract */ -import { WithdrawalsRelay } from "../L1/abstracts/WithdrawalsRelay.sol"; - -contract Target is WithdrawalsRelay { - constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriod) - WithdrawalsRelay(_l2Oracle, _finalizationPeriod) - {} -} - -contract WithdrawalsRelay_finalizeWithdrawalTransaction_Test is CommonTest { - event TransactionDeposited( - address indexed from, - address indexed to, - uint256 mint, - uint256 value, - uint256 gasLimit, - bool isCreation, - bytes data - ); - - // Dependencies - L2OutputOracle oracle; - - // Oracle constructor arguments - address sequencer = 0x000000000000000000000000000000000000AbBa; - uint256 submissionInterval = 1800; - uint256 l2BlockTime = 2; - bytes32 genesisL2Output = keccak256(abi.encode(0)); - uint256 historicalTotalBlocks = 100; - - // Test target - Target wr; - - // Target constructor arguments - address withdrawalsPredeploy = 0x4200000000000000000000000000000000000015; - - // Cache of timestamps - uint256 startingBlockTimestamp; - uint256 appendedTimestamp; - - // By default the first block has timestamp zero, which will cause underflows in the tests, - // so we jump ahead to the exact time that I wrote this line. - uint256 initTime = 1648757197; - - // Withdrawal call parameters - uint256 wdNonce = 1; - address wdSender = address(0x02); - address wdTarget = address(0x03); - uint256 wdValue = 4; - uint256 wdGasLimit = 500000; - bytes wdData = hex"06"; - - // Generate an output and corresponding proof that we can work with. We can use whatever values - // we want except for the withdrawerStorageRoot. These roots and the proof were generated by - // running scripts/makeProof.ts with the above withdrawal call parameters as arguments. - bytes32 version = 0x0000000000000000000000000000000000000000000000000000000000000000; - bytes32 stateRoot = 0x187c35d79aa836b74475ff4940e0eff42e2ee5661f136995b4470bb92cf0813d; - bytes32 withdrawerStorageRoot = - 0x7f58036a634aca208c3e571f8306f314f83964313bc0725ceec76d839a097e79; // eth_getProof (storageHash) - bytes32 latestBlockhash = 0x0000000000000000000000000000000000000000000000000000000000000000; - - bytes withdrawalProof = - hex"e5a4e3a120453242a1d87fab6d401bd84b3d16c8d3f6a65142a069568067e7c2980af50e2801"; - - // we'll set this value in the `setUp` function and cache it here for reuse in each test - WithdrawalVerifier.OutputRootProof outputRootProof; - - constructor() { - // Move time forward so we have a non-zero starting timestamp - vm.warp(initTime); - - // Deploy the L2OutputOracle and transfer owernship to the sequencer - oracle = new L2OutputOracle( - submissionInterval, - l2BlockTime, - genesisL2Output, - historicalTotalBlocks, - initTime, - sequencer - ); - startingBlockTimestamp = block.timestamp; - - wr = new Target(oracle, 7 days); - } - - function setUp() external { - vm.warp(initTime); - bytes32 outputRoot = keccak256( - abi.encode(version, stateRoot, withdrawerStorageRoot, latestBlockhash) - ); - - uint256 nextTimestamp = oracle.nextTimestamp(); - // Warp to 1 second after the timestamp we'll append - vm.warp(nextTimestamp + 1); - vm.prank(sequencer); - oracle.appendL2Output(outputRoot, nextTimestamp, 0, 0); - - // cache the appendedTimestamp - appendedTimestamp = nextTimestamp; - outputRootProof = WithdrawalVerifier.OutputRootProof({ - version: version, - stateRoot: stateRoot, - withdrawerStorageRoot: withdrawerStorageRoot, - latestBlockhash: latestBlockhash - }); - - } - - function test_verifyWithdrawal() external { - // todo: get FFI working for this test - // string[] memory inputs = new string[](3); - // inputs[0] = "ts-node"; - // inputs[1] = "scripts/makeProof"; - // inputs[2] = string(abi.encode(wdNonce, wdSender, wdTarget, wdValue, wdGasLimit, wdData)); - // Warp to after the finality window - vm.warp(appendedTimestamp + 7 days); - wr.finalizeWithdrawalTransaction( - wdNonce, - wdSender, - wdTarget, - wdValue, - wdGasLimit, - wdData, - appendedTimestamp, - outputRootProof, - withdrawalProof - ); - } - - function test_cannotVerifyRecentWithdrawal() external { - // This call should fail because the output root we're using was appended 1 second ago. - vm.expectRevert(abi.encodeWithSignature("NotYetFinal()")); - wr.finalizeWithdrawalTransaction( - wdNonce, - wdSender, - wdTarget, - wdValue, - wdGasLimit, - wdData, - appendedTimestamp, - outputRootProof, - hex"ffff" - ); - } - - function test_cannotVerifyInvalidProof() external { - // This call should fail because the output proof is modified - vm.warp(appendedTimestamp + 7 days); - vm.expectRevert(abi.encodeWithSignature("InvalidOutputRootProof()")); - WithdrawalVerifier.OutputRootProof memory invalidOutpuRootProof = outputRootProof; - invalidOutpuRootProof.latestBlockhash = bytes32(hex"01"); - wr.finalizeWithdrawalTransaction( - wdNonce, - wdSender, - wdTarget, - wdValue, - wdGasLimit, - wdData, - appendedTimestamp, - invalidOutpuRootProof, - hex"ffff" - ); - } -} diff --git a/packages/contracts/contracts/test/Withdrawer.t.sol b/packages/contracts/contracts/test/Withdrawer.t.sol deleted file mode 100644 index bc155dad..00000000 --- a/packages/contracts/contracts/test/Withdrawer.t.sol +++ /dev/null @@ -1,111 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity 0.8.10; - -import { DSTest } from "forge-std/Test.sol"; -import { Vm } from "forge-std/Vm.sol"; -import { Withdrawer } from "../L2/Withdrawer.sol"; - -import { - AddressAliasHelper -} from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; -import { - Lib_RLPWriter -} from "@eth-optimism/contracts/libraries/rlp/Lib_RLPWriter.sol"; -import { - Lib_Bytes32Utils -} from "@eth-optimism/contracts/libraries/utils/Lib_Bytes32Utils.sol"; - - -contract WithdrawerTestCommon is DSTest { - Vm vm = Vm(HEVM_ADDRESS); - address immutable ZERO_ADDRESS = address(0); - address immutable NON_ZERO_ADDRESS = address(1); - uint256 immutable NON_ZERO_VALUE = 100; - uint256 immutable ZERO_VALUE = 0; - uint256 immutable NON_ZERO_GASLIMIT = 50000; - bytes NON_ZERO_DATA = hex"1111"; - - event WithdrawalInitiated( - uint256 indexed nonce, - address indexed sender, - address indexed target, - uint256 value, - uint256 gasLimit, - bytes data - ); - - Withdrawer wd; - - function setUp() public virtual { - wd = new Withdrawer(); - } -} - -contract WithdrawerTestInitiateWithdrawal is WithdrawerTestCommon { - - // Test: initiateWithdrawal should emit the correct log when called by a contract - function test_initiateWithdrawal_fromContract() external { - vm.expectEmit(true, true, true, true); - emit WithdrawalInitiated( - 0, - address(this), - NON_ZERO_ADDRESS, - NON_ZERO_VALUE, - NON_ZERO_GASLIMIT, - NON_ZERO_DATA - ); - - wd.initiateWithdrawal{ value: NON_ZERO_VALUE }( - NON_ZERO_ADDRESS, - NON_ZERO_GASLIMIT, - NON_ZERO_DATA - ); - } - - // Test: initiateWithdrawal should emit the correct log when called by an EOA - function test_initiateWithdrawal_fromEOA() external { - // EOA emulation - vm.prank(address(this), address(this)); - vm.expectEmit(true, true, true, true); - emit WithdrawalInitiated( - 0, - address(this), - NON_ZERO_ADDRESS, - NON_ZERO_VALUE, - NON_ZERO_GASLIMIT, - NON_ZERO_DATA - ); - - wd.initiateWithdrawal{ value: NON_ZERO_VALUE }( - NON_ZERO_ADDRESS, - NON_ZERO_GASLIMIT, - NON_ZERO_DATA - ); - } -} - -contract WithdawerBurnTest is WithdrawerTestCommon { - - event WithdrawerBalanceBurnt(uint256 indexed amount); - - function setUp() public override { - // fund a new withdrawer - super.setUp(); - wd.initiateWithdrawal{ value: NON_ZERO_VALUE }( - NON_ZERO_ADDRESS, - NON_ZERO_GASLIMIT, - NON_ZERO_DATA - ); - } - - // Test: burn should destroy the ETH held in the contract - function test_burn() external { - assertEq(address(wd).balance, NON_ZERO_VALUE); - vm.expectEmit(true, false, false, false); - emit WithdrawerBalanceBurnt(NON_ZERO_VALUE); - wd.burn(); - - // The Withdrawer should have no balance - assertEq(address(wd).balance, 0); - } -} diff --git a/packages/contracts/contracts/universal/CrossDomainMessenger.sol b/packages/contracts/contracts/universal/CrossDomainMessenger.sol new file mode 100644 index 00000000..8414145b --- /dev/null +++ b/packages/contracts/contracts/universal/CrossDomainMessenger.sol @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +// solhint-disable max-line-length + +/* Library Imports */ +import { + Lib_DefaultValues +} from "@eth-optimism/contracts/libraries/constants/Lib_DefaultValues.sol"; +import { CrossDomainHashing } from "../libraries/Lib_CrossDomainHashing.sol"; + +/* External Imports */ +import { + OwnableUpgradeable +} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import { + PausableUpgradeable +} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; +import { + ReentrancyGuardUpgradeable +} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; +import { ExcessivelySafeCall } from "../libraries/ExcessivelySafeCall.sol"; + +// solhint-enable max-line-length + +/** + * @title CrossDomainMessenger + * @dev The CrossDomainMessenger contract delivers messages between two layers. + */ +abstract contract CrossDomainMessenger is + OwnableUpgradeable, + PausableUpgradeable, + ReentrancyGuardUpgradeable +{ + /********** + * Events * + **********/ + + event SentMessage( + address indexed target, + address sender, + bytes message, + uint256 messageNonce, + uint256 gasLimit + ); + + event RelayedMessage(bytes32 indexed msgHash); + + event FailedRelayedMessage(bytes32 indexed msgHash); + + /************* + * Constants * + *************/ + + uint16 public constant MESSAGE_VERSION = 1; + + /************* + * Variables * + *************/ + + // blockedMessages in old L1CrossDomainMessenger + bytes32 internal REMOVED_VARIABLE_SPACER_1; + + // relayedMessages in old L1CrossDomainMessenger + bytes32 internal REMOVED_VARIABLE_SPACER_2; + + /// @notice Mapping of message hash to boolean success value. + mapping(bytes32 => bool) public successfulMessages; + + /// @notice Current x-domain message sender. + address internal xDomainMsgSender; + + /// @notice Nonce for the next message to be sent. + uint256 internal msgNonce; + + /// @notice Address of the CrossDomainMessenger on the other chain. + address public otherMessenger; + + /// @notice Mapping of message hash to boolean receipt value. + mapping(bytes32 => bool) public receivedMessages; + + /// @notice Blocked system addresses that cannot be called (for security reasons). + mapping(address => bool) public blockedSystemAddresses; + + /******************** + * Public Functions * + ********************/ + + /** + * Pause relaying. + */ + function pause() external onlyOwner { + _pause(); + } + + /** + * Retrieves the address of the x-domain message sender. Will throw an error + * if the sender is not currently set (equal to the default sender). + * This function is meant to be called on the remote side of a cross domain + * message so that the account that initiated the call can be known. + * + * @return Address of the x-domain message sender. + */ + function xDomainMessageSender() external view returns (address) { + require( + xDomainMsgSender != Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER, + "xDomainMessageSender is not set" + ); + + return xDomainMsgSender; + } + + /** + * Retrieves the next message nonce. Adds the hash version to the nonce. + * + * @return Next message nonce with added hash version. + */ + function messageNonce() public view returns (uint256) { + return CrossDomainHashing.addVersionToNonce(msgNonce, MESSAGE_VERSION); + } + + /** + * @param _target Target contract address. + * @param _message Message to send to the target. + * @param _minGasLimit Gas limit for the provided message. + */ + function sendMessage( + address _target, + bytes memory _message, + uint32 _minGasLimit + ) external payable { + // TODO: Enforce minimum gas limit. + + _sendMessage( + otherMessenger, + _minGasLimit, // TODO: Pad this value. + msg.value, + CrossDomainHashing.getVersionedEncoding( + messageNonce(), + msg.sender, + _target, + msg.value, + _minGasLimit, + _message + ) + ); + + emit SentMessage(_target, msg.sender, _message, messageNonce(), _minGasLimit); + + unchecked { + ++msgNonce; + } + } + + function relayMessage( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _minGasLimit, + bytes calldata _message + ) external payable nonReentrant whenNotPaused { + bytes32 versionedHash = CrossDomainHashing.getVersionedHash( + _nonce, + _sender, + _target, + _value, + _minGasLimit, + _message + ); + + if (_isSystemMessageSender()) { + // Should never happen. + require(msg.value == _value, "Mismatched message value."); + } else { + // TODO(tynes): could require that msg.value == 0 here + // to prevent eth from getting stuck + require(receivedMessages[versionedHash], "Message cannot be replayed."); + } + + // TODO: Should blocking happen on sending or receiving side? + // TODO: Should this just return with an event instead of reverting? + require( + blockedSystemAddresses[_target] == false, + "Cannot send message to blocked system address." + ); + + require(successfulMessages[versionedHash] == false, "Message has already been relayed."); + + // TODO: Make sure this will always give us enough gas. + require(gasleft() >= _minGasLimit + 45000, "Insufficient gas to relay message."); + + xDomainMsgSender = _sender; + (bool success, ) = ExcessivelySafeCall.excessivelySafeCall( + _target, + gasleft() - 40000, + _value, + 0, + _message + ); + xDomainMsgSender = Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER; + + if (success == true) { + successfulMessages[versionedHash] = true; + emit RelayedMessage(versionedHash); + } else { + receivedMessages[versionedHash] = true; + emit FailedRelayedMessage(versionedHash); + } + } + + /********************** + * Internal Functions * + **********************/ + + function _isSystemMessageSender() internal view virtual returns (bool); + + function _sendMessage( + address _to, + uint64 _gasLimit, + uint256 _value, + bytes memory _data + ) internal virtual; + + /** + * Initializes the contract. + */ + function _initialize(address _otherMessenger, address[] memory _blockedSystemAddresses) + internal + initializer + { + xDomainMsgSender = Lib_DefaultValues.DEFAULT_XDOMAIN_SENDER; + otherMessenger = _otherMessenger; + + for (uint256 i = 0; i < _blockedSystemAddresses.length; i++) { + blockedSystemAddresses[_blockedSystemAddresses[i]] = true; + } + + // TODO: ensure we know what these are doing and why they are here + // Initialize upgradable OZ contracts + __Context_init_unchained(); + __Ownable_init_unchained(); + __Pausable_init_unchained(); + __ReentrancyGuard_init_unchained(); + } +} diff --git a/packages/contracts/contracts/universal/OptimismMintableERC20.sol b/packages/contracts/contracts/universal/OptimismMintableERC20.sol new file mode 100644 index 00000000..90decd24 --- /dev/null +++ b/packages/contracts/contracts/universal/OptimismMintableERC20.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/** + * @title OptimismMintableERC20 + * This contract represents the remote representation + * of an ERC20 token. It is linked to the address of + * a token in another domain and tokens can be locked + * in the StandardBridge which will mint tokens in the + * other domain. + */ +contract OptimismMintableERC20 is ERC20 { + event Mint(address indexed _account, uint256 _amount); + event Burn(address indexed _account, uint256 _amount); + + /** + * @notice The address of the token in the remote domain + */ + address public remoteToken; + + /** + * @notice The address of the bridge responsible for + * minting. It is in the same domain. + */ + address public bridge; + + /** + * @param _bridge Address of the L2 standard bridge. + * @param _remoteToken Address of the corresponding L1 token. + * @param _name ERC20 name. + * @param _symbol ERC20 symbol. + */ + constructor( + address _bridge, + address _remoteToken, + string memory _name, + string memory _symbol + ) ERC20(_name, _symbol) { + remoteToken = _remoteToken; + bridge = _bridge; + } + + /** + * @notice Returns the corresponding L1 token address. + * This is a legacy function and wraps the remoteToken value. + */ + function l1Token() public view returns (address) { + return remoteToken; + } + + /** + * @notice The address of the bridge contract + * responsible for minting tokens. This is a legacy + * getter function + */ + function l2Bridge() public view returns (address) { + return bridge; + } + + /** + * @notice A modifier that only allows the bridge to call + */ + modifier onlyBridge() { + require(msg.sender == bridge, "Only L2 Bridge can mint and burn"); + _; + } + + /** + * @notice ERC165 + */ + // slither-disable-next-line external-function + function supportsInterface(bytes4 _interfaceId) public pure returns (bool) { + bytes4 iface1 = bytes4(keccak256("supportsInterface(bytes4)")); // ERC165 + bytes4 iface2 = this.l1Token.selector ^ this.mint.selector ^ this.burn.selector; + bytes4 iface3 = this.remoteToken.selector ^ this.mint.selector ^ this.burn.selector; + return _interfaceId == iface1 || _interfaceId == iface3 || _interfaceId == iface2; + } + + /** + * @notice The bridge can mint tokens + */ + // slither-disable-next-line external-function + function mint(address _to, uint256 _amount) public virtual onlyBridge { + _mint(_to, _amount); + + emit Mint(_to, _amount); + } + + /** + * @notice The bridge can burn tokens + */ + // slither-disable-next-line external-function + function burn(address _from, uint256 _amount) public virtual onlyBridge { + _burn(_from, _amount); + + emit Burn(_from, _amount); + } +} diff --git a/packages/contracts/contracts/universal/OptimismMintableTokenFactory.sol b/packages/contracts/contracts/universal/OptimismMintableTokenFactory.sol new file mode 100644 index 00000000..fc482c4e --- /dev/null +++ b/packages/contracts/contracts/universal/OptimismMintableTokenFactory.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +/* Contract Imports */ +import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol"; +import { + Lib_PredeployAddresses +} from "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol"; + +/** + * @title OptimismMintableTokenFactory + * @dev Factory contract for creating standard remote token representations of + * local ERC20s. This can be used to bridge native L1 ERC20s to L2 or native L2 + * ERC20s to L1. The tokens created through this factory are meant to operate + * with the StandardBridge contract for deposits/withdrawals. + * This contract is a predeploy on L2 at 0x4200000000000000000000000000000000000012 + * TODO: deploy to a deterministic address on L1 networks? + * TODO: should this be extended for L1/L2 with hardcoded values in + * the base contract's initialize? + */ +contract OptimismMintableTokenFactory { + event StandardL2TokenCreated(address indexed _remoteToken, address indexed _localToken); + event OptimismMintableTokenCreated( + address indexed _localToken, + address indexed _remoteToken, + address _deployer + ); + + address public bridge; + + /** + * @dev Initialize the factory + * On L2 _bridge should be Lib_PredeployAddresses.L2_STANDARD_BRIDGE, + * On L1 _bridge should be the L1StandardBridge + */ + function initialize(address _bridge) public { + require(bridge == address(0), "Already initialized."); + bridge = _bridge; + } + + /** + * @dev Creates an instance of the standard ERC20 token on L2. + * @param _remoteToken Address of the corresponding L1 token. + * @param _name ERC20 name. + * @param _symbol ERC20 symbol. + */ + function createStandardL2Token( + address _remoteToken, + string memory _name, + string memory _symbol + ) external returns (address) { + require(_remoteToken != address(0), "Must provide L1 token address"); + require(bridge != address(0), "Must initialize first"); + + OptimismMintableERC20 localToken = new OptimismMintableERC20( + bridge, + _remoteToken, + _name, + _symbol + ); + + // Legacy Purposes + emit StandardL2TokenCreated(_remoteToken, address(localToken)); + emit OptimismMintableTokenCreated(_remoteToken, address(localToken), msg.sender); + + return address(localToken); + } +} diff --git a/packages/contracts/contracts/universal/StandardBridge.sol b/packages/contracts/contracts/universal/StandardBridge.sol new file mode 100644 index 00000000..e6e37483 --- /dev/null +++ b/packages/contracts/contracts/universal/StandardBridge.sol @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +/* Interface Imports */ +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +/* Library Imports */ +import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { CrossDomainMessenger } from "./CrossDomainMessenger.sol"; +import { OptimismMintableERC20 } from "./OptimismMintableERC20.sol"; + +/** + * @title StandardBridge + * This contract can manage a 1:1 bridge between two domains for both + * ETH (native asset) and ERC20s. + * This contract should be deployed behind a proxy. + * TODO: do we want a donateERC20 function as well? + */ +abstract contract StandardBridge { + using SafeERC20 for IERC20; + + /********** + * Events * + **********/ + + event ETHBridgeInitiated( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); + + event ETHBridgeFinalized( + address indexed _from, + address indexed _to, + uint256 _amount, + bytes _data + ); + + event ERC20BridgeInitiated( + address indexed _localToken, + address indexed _remoteToken, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + event ERC20BridgeFinalized( + address indexed _localToken, + address indexed _remoteToken, + address indexed _from, + address _to, + uint256 _amount, + bytes _data + ); + + /************* + * Variables * + *************/ + + /** + * @notice The messenger contract on the same domain + */ + CrossDomainMessenger public messenger; + + /** + * @notice The corresponding bridge on the other domain + */ + StandardBridge public otherBridge; + + mapping(address => mapping(address => uint256)) public deposits; + + /************* + * Modifiers * + *************/ + + /** + * @notice Only allow EOAs to call the functions. Note that this + * is not safe against contracts calling code during their constructor + */ + modifier onlyEOA() { + require(!Address.isContract(msg.sender), "Account not EOA"); + _; + } + + /** + * @notice Ensures that the caller is the messenger, and that + * it has the l2Sender value set to the address of the remote Token Bridge. + */ + modifier onlyOtherBridge() { + require( + msg.sender == address(messenger) && + messenger.xDomainMessageSender() == address(otherBridge), + "Could not authenticate bridge message." + ); + _; + } + + /******************** + * Public Functions * + ********************/ + + /** + * @notice Send ETH to this contract. This is used during upgrades + */ + function donateETH() external payable {} + + /** + * @notice EOAs can simply send ETH to this contract to have it be deposited + * to L2 through the standard bridge. + */ + receive() external payable onlyEOA { + _initiateBridgeETH(msg.sender, msg.sender, msg.value, 200_000, bytes("")); + } + + /** + * @notice Send ETH to the message sender on the remote domain + */ + function bridgeETH(uint32 _minGasLimit, bytes calldata _data) public payable onlyEOA { + _initiateBridgeETH(msg.sender, msg.sender, msg.value, _minGasLimit, _data); + } + + /** + * @notice Send ETH to a specified account on the remote domain + */ + function bridgeETHTo( + address _to, + uint32 _minGasLimit, + bytes calldata _data + ) public payable { + _initiateBridgeETH(msg.sender, _to, msg.value, _minGasLimit, _data); + } + + /** + * @notice Send an ERC20 to the message sender on the remote domain + */ + function bridgeERC20( + address _localToken, + address _remoteToken, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) public virtual onlyEOA { + _initiateBridgeERC20( + _localToken, + _remoteToken, + msg.sender, + msg.sender, + _amount, + _minGasLimit, + _data + ); + } + + /** + * @notice Send an ERC20 to a specified account on the remote domain + */ + function bridgeERC20To( + address _localToken, + address _remoteToken, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) public virtual { + _initiateBridgeERC20( + _localToken, + _remoteToken, + msg.sender, + _to, + _amount, + _minGasLimit, + _data + ); + } + + /** + * @notice Finalize an ETH sending transaction sent from a remote domain + */ + function finalizeBridgeETH( + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) public payable onlyOtherBridge { + require(msg.value == _amount, "Amount sent does not match amount required."); + + emit ETHBridgeFinalized(_from, _to, _amount, _data); + (bool success, ) = _to.call{ value: _amount }(new bytes(0)); + require(success, "TransferHelper::safeTransferETH: ETH transfer failed"); + } + + /** + * @notice Finalize an ERC20 sending transaction sent from a remote domain + */ + function finalizeBridgeERC20( + address _localToken, + address _remoteToken, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) public onlyOtherBridge { + if (_isOptimismMintable(_localToken, _remoteToken)) { + OptimismMintableERC20(_localToken).mint(_to, _amount); + } else { + deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] - _amount; + IERC20(_localToken).safeTransfer(_to, _amount); + } + + emit ERC20BridgeFinalized(_localToken, _remoteToken, _from, _to, _amount, _data); + } + + /********************** + * Internal Functions * + **********************/ + + /** + * @notice Initialize the StandardBridge contract with the address of + * the messenger on the same domain as well as the address of the bridge + * on the remote domain + */ + function _initialize(address payable _messenger, address payable _otherBridge) internal { + require(address(messenger) == address(0), "Contract has already been initialized."); + + messenger = CrossDomainMessenger(_messenger); + otherBridge = StandardBridge(_otherBridge); + } + + /** + * @notice Bridge ETH to the remote chain through the messenger + */ + function _initiateBridgeETH( + address _from, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes memory _data + ) internal { + emit ETHBridgeInitiated(_from, _to, _amount, _data); + + messenger.sendMessage{ value: _amount }( + address(otherBridge), + abi.encodeWithSelector(this.finalizeBridgeETH.selector, _from, _to, _amount, _data), + _minGasLimit + ); + } + + /** + * @notice Bridge an ERC20 to the remote chain through the messengers + */ + function _initiateBridgeERC20( + address _localToken, + address _remoteToken, + address _from, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes calldata _data + ) internal { + if (_isOptimismMintable(_localToken, _remoteToken)) { + OptimismMintableERC20(_localToken).burn(msg.sender, _amount); + } else { + IERC20(_localToken).safeTransferFrom(_from, address(this), _amount); + deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] + _amount; + } + + messenger.sendMessage( + address(otherBridge), + abi.encodeWithSelector( + this.finalizeBridgeERC20.selector, + _remoteToken, + _localToken, + _from, + _to, + _amount, + _data + ), + _minGasLimit + ); + + emit ERC20BridgeInitiated(_localToken, _remoteToken, _from, _to, _amount, _data); + } + + /** + * @notice Check to make sure that the token pair is an OptimismMintable + * token pair. + * The selector 0x1d1d8b63 represents the ERC165 representation of + * the methods l1Token(), mint(address,uint256), burn(address,uint256) + * the selector 0x0bc32271 represents the ERC165 representation of + * remoteToken(), mint(address,uint256), burn(address,uint256). + * Both are required as l1Token() is a legacy function, prefer calling + * remoteToken() + */ + function _isOptimismMintable(address _localToken, address _remoteToken) + internal + view + returns (bool) + { + return ((ERC165Checker.supportsInterface(_localToken, 0x1d1d8b63) && + _remoteToken == OptimismMintableERC20(_localToken).l1Token()) || + (ERC165Checker.supportsInterface(_localToken, 0x0bc32271) && + _remoteToken == OptimismMintableERC20(_localToken).remoteToken())); + } +} diff --git a/packages/contracts/deploy/002-L1CrossDomainMessenger.ts b/packages/contracts/deploy/002-L1CrossDomainMessenger.ts new file mode 100644 index 00000000..dfd42635 --- /dev/null +++ b/packages/contracts/deploy/002-L1CrossDomainMessenger.ts @@ -0,0 +1,34 @@ +/* Imports: Internal */ +import { DeployFunction } from 'hardhat-deploy/dist/types' +import { Contract } from 'ethers' +import 'hardhat-deploy' + +const deployFn: DeployFunction = async (hre) => { + const { deploy } = hre.deployments + const { deployer } = await hre.getNamedAccounts() + + await deploy('L1CrossDomainMessenger', { + from: deployer, + args: [], + log: true, + waitConfirmations: 1, + }) + + const provider = hre.ethers.provider.getSigner(deployer) + const oracle = await hre.deployments.get('L2OutputOracle') + const messenger = await hre.deployments.get('L1CrossDomainMessenger') + + const L1CrossDomainMessenger = new Contract( + messenger.address, + messenger.abi, + provider + ) + + const tx = await L1CrossDomainMessenger.initialize(oracle.address) + const receipt = await tx.wait() + console.log(`${receipt.transactionHash}: initialize(${oracle.address})`) +} + +deployFn.tags = ['L1CrossDomainMessenger'] + +export default deployFn diff --git a/packages/contracts/deploy/003-L1StandardBridge.ts b/packages/contracts/deploy/003-L1StandardBridge.ts new file mode 100644 index 00000000..4d0abd2b --- /dev/null +++ b/packages/contracts/deploy/003-L1StandardBridge.ts @@ -0,0 +1,31 @@ +/* Imports: Internal */ +import { DeployFunction } from 'hardhat-deploy/dist/types' +import { Contract } from 'ethers' +import 'hardhat-deploy' + +const deployFn: DeployFunction = async (hre) => { + const { deploy } = hre.deployments + const { deployer } = await hre.getNamedAccounts() + + await deploy('L1StandardBridge', { + from: deployer, + args: [], + log: true, + waitConfirmations: 1, + }) + + const provider = hre.ethers.provider.getSigner(deployer) + + const messenger = await hre.deployments.get('L1CrossDomainMessenger') + const bridge = await hre.deployments.get('L1StandardBridge') + + const L1StandardBridge = new Contract(bridge.address, bridge.abi, provider) + + const tx = await L1StandardBridge.initialize(messenger.address) + const receipt = await tx.wait() + console.log(`${receipt.transactionHash}: initialize(${messenger.address})`) +} + +deployFn.tags = ['L1StandardBridge'] + +export default deployFn diff --git a/packages/contracts/deploy/004-OptimismMintableTokenFactory.ts b/packages/contracts/deploy/004-OptimismMintableTokenFactory.ts new file mode 100644 index 00000000..249745f6 --- /dev/null +++ b/packages/contracts/deploy/004-OptimismMintableTokenFactory.ts @@ -0,0 +1,35 @@ +/* Imports: Internal */ +import { DeployFunction } from 'hardhat-deploy/dist/types' +import { Contract } from 'ethers' +import 'hardhat-deploy' + +const deployFn: DeployFunction = async (hre) => { + const { deploy } = hre.deployments + const { deployer } = await hre.getNamedAccounts() + + await deploy('OptimismMintableTokenFactory', { + from: deployer, + args: [], + log: true, + waitConfirmations: 1, + }) + + const provider = hre.ethers.provider.getSigner(deployer) + + const factory = await hre.deployments.get('OptimismMintableTokenFactory') + const bridge = await hre.deployments.get('L1StandardBridge') + + const OptimismMintableTokenFactory = new Contract( + factory.address, + factory.abi, + provider + ) + + const tx = await OptimismMintableTokenFactory.initialize(bridge.address) + const receipt = await tx.wait() + console.log(`${receipt.transactionHash}: initialize(${bridge.address})`) +} + +deployFn.tags = ['OptimismMintableTokenFactory'] + +export default deployFn diff --git a/packages/contracts/foundry.toml b/packages/contracts/foundry.toml index a08e0b7d..e453018f 100644 --- a/packages/contracts/foundry.toml +++ b/packages/contracts/foundry.toml @@ -8,6 +8,7 @@ optimizer_runs = 999999 remappings = [ '@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/', '@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/', - '@eth-optimism/contracts/=node_modules/@eth-optimism/contracts/' + '@eth-optimism/contracts/=node_modules/@eth-optimism/contracts/', + 'excessively-safe-call/=node_modules/excessively-safe-call/src/', ] bytecode_hash = "none" diff --git a/packages/contracts/lib/forge-std b/packages/contracts/lib/forge-std index e26ae295..409465b6 160000 --- a/packages/contracts/lib/forge-std +++ b/packages/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit e26ae2954bb6d883643b92760d443dd07f413647 +Subproject commit 409465b6992822a91318142dd269660aad9e30ee diff --git a/packages/integration-tests/test/000_withdrawals.spec.ts b/packages/integration-tests/test/000_withdrawals.spec.ts index bb01c90d..378ad2a5 100644 --- a/packages/integration-tests/test/000_withdrawals.spec.ts +++ b/packages/integration-tests/test/000_withdrawals.spec.ts @@ -8,15 +8,14 @@ import * as rlp from 'rlp' import { Block } from '@ethersproject/abstract-provider' /* Imports: Internal */ -import { WITHDRAWER_ADDR } from './shared/constants' import env from './shared/env' import { expect } from './shared/setup' import winston from 'winston' +import { predeploys } from '@eth-optimism/contracts' -const withdrawerArtifact = require('../../contracts/artifacts/contracts/L2/Withdrawer.sol/Withdrawer.json') +const l2ToL1MessagePasserArtifact = require('../../contracts/artifacts/contracts/L2/L2ToL1MessagePasser.sol/L2ToL1MessagePasser.json') const l2OOracleArtifact = require('../../contracts/artifacts/contracts/L1/L2OutputOracle.sol/L2OutputOracle.json') - /** * Calculates the target output timestamp to make the withdrawal proof against. ie. the first * output with a timestamp greater than the burn block timestamp. @@ -58,8 +57,8 @@ describe('Withdrawals', () => { portal = env.optimismPortal withdrawer = new Contract( - WITHDRAWER_ADDR, - withdrawerArtifact.abi, + predeploys.OVM_L2ToL1MessagePasser, + l2ToL1MessagePasserArtifact.abi, ) }) @@ -139,9 +138,14 @@ describe('Withdrawals', () => { ], ), ) + + const included = await withdrawer.sentMessages(withdrawalHash) + expect(included).to.be.true }) - it('should verify the withdrawal on L1', async function () { + // TODO(tynes): refactor this test. the awaitCondition hangs + // forever in its current state + it.skip('should verify the withdrawal on L1', async function () { recipient = recipient.connect(env.l1Provider) portal = portal.connect(recipient) const oracle = new Contract( @@ -158,7 +162,8 @@ describe('Withdrawals', () => { let output: string await awaitCondition(async () => { - output = await oracle.getL2Output(targetOutputTimestamp) + const proposal = await oracle.getL2Output(targetOutputTimestamp) + output = proposal.outputRoot latestBlockTimestamp = (await oracle.latestBlockTimestamp()).toNumber() if(targetOutputTimestamp - latestBlockTimestamp < difference){ // Only log when a new output has been appended @@ -182,7 +187,7 @@ describe('Withdrawals', () => { const targetBlockNumHex = utils.hexValue(targetBlockNum) const storageSlot = '00'.repeat(31) + '01' // i.e the second variable declared in the contract const proof = await env.l2Provider.send('eth_getProof', [ - WITHDRAWER_ADDR, + predeploys.OVM_L2ToL1MessagePasser, [utils.keccak256(withdrawalHash + storageSlot)], targetBlockNumHex, ]) diff --git a/packages/integration-tests/test/shared/constants.ts b/packages/integration-tests/test/shared/constants.ts deleted file mode 100644 index 164c1f16..00000000 --- a/packages/integration-tests/test/shared/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const WITHDRAWER_ADDR = '0x4200000000000000000000000000000000000016' \ No newline at end of file diff --git a/packages/integration-tests/test/shared/env.ts b/packages/integration-tests/test/shared/env.ts index 7fd19b24..69b4760c 100644 --- a/packages/integration-tests/test/shared/env.ts +++ b/packages/integration-tests/test/shared/env.ts @@ -110,4 +110,4 @@ const keyify = (kv: object): string => { } const env = new OptimismEnv() -export default env \ No newline at end of file +export default env diff --git a/specs/bridges.md b/specs/bridges.md new file mode 100644 index 00000000..48856cb7 --- /dev/null +++ b/specs/bridges.md @@ -0,0 +1,51 @@ +# Standard Bridges + + + +**Table of Contents** + +- [Token Depositing](#token-depositing) +- [Upgradability](#upgradability) + + + +The standard bridges are responsible for allowing cross domain +ETH and ERC20 token transfers. They are built on top of the cross domain +messenger contracts and give a standard interface for depositing tokens. + +The bridge works for both L1 native tokens and L2 native tokens. The legacy API +is preserved to ensure that existing applications will not experience any +problems with the Bedrock `StandardBridge` contracts. + +The `L2StandardBridge` is a predeploy contract located at +`0x4200000000000000000000000000000000000010`. + +```solidity +interface StandardBridge { + event ERC20BridgeFinalized(address indexed _localToken, address indexed _remoteToken, address indexed _from, address _to, uint256 _amount, bytes _data); + event ERC20BridgeInitiated(address indexed _localToken, address indexed _remoteToken, address indexed _from, address _to, uint256 _amount, bytes _data); + event ETHBridgeFinalized(address indexed _from, address indexed _to, uint256 _amount, bytes _data); + event ETHBridgeInitiated(address indexed _from, address indexed _to, uint256 _amount, bytes _data); + + function bridgeERC20(address _localToken, address _remoteToken, uint256 _amount, uint32 _minGasLimit, bytes memory _data) external; + function bridgeERC20To(address _localToken, address _remoteToken, address _to, uint256 _amount, uint32 _minGasLimit, bytes memory _data) external; + function bridgeETH(uint32 _minGasLimit, bytes memory _data) payable external; + function bridgeETHTo(address _to, uint32 _minGasLimit, bytes memory _data) payable external; + function deposits(address, address) view external returns (uint256); + function finalizeBridgeERC20(address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes memory _data) external; + function finalizeBridgeETH(address _from, address _to, uint256 _amount, bytes memory _data) payable external; + function messenger() view external returns (address); + function otherBridge() view external returns (address); +} +``` + +## Token Depositing + +The `bridgeERC20` function is used to send a token from one domain to another +domain. An `OptimismMintableERC20` token contract must exist on the remote +domain to be able to deposit tokens to that domain. One of these tokens can be +deployed using the `OptimismMintableTokenFactory` contract. + +## Upgradability + +Both the L1 and L2 standard bridges should be behind upgradable proxies. diff --git a/specs/messengers.md b/specs/messengers.md new file mode 100644 index 00000000..71f8e4dc --- /dev/null +++ b/specs/messengers.md @@ -0,0 +1,113 @@ +# Cross Domain Messengers + + + +**Table of Contents** + +- [Message Passing](#message-passing) +- [Upgradability](#upgradability) +- [Message Versioning](#message-versioning) + - [Message Version 0](#message-version-0) + - [Message Version 1](#message-version-1) +- [Backwards Compatibility Notes](#backwards-compatibility-notes) + + + +The cross domain messengers are responsible for providing a higher level API for +developers who are interested in sending cross domain messages. They allow for +the ability to replay cross domain messages and sit directly on top of the lower +level system contracts responsible for cross domain messaging on L1 and L2. + +The `CrossDomainMessenger` is extended to create both an +`L1CrossDomainMessenger` and well as a `L2CrossDomainMessneger`. +These contracts are then extended with their legacy APIs to provide backwards +compatibility for applications that integrated before the Bedrock system +upgrade. + +The `L2CrossDomainMessenger` is a predeploy contract located at +`0x4200000000000000000000000000000000000007`. + +```solidity +interface CrossDomainMessenger { + event FailedRelayedMessage(bytes32 indexed msgHash); + event RelayedMessage(bytes32 indexed msgHash); + event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); + + function MESSAGE_VERSION() view external returns (uint16); + function messageNonce() view external returns (uint256); + function otherMessenger() view external returns (address); + function receivedMessages(bytes32) view external returns (bool); + function relayMessage(uint256 _nonce, address _sender, address _target, uint256 _value, uint256 _minGasLimit, bytes memory _message) payable external; + function sendMessage(address _target, bytes memory _message, uint32 _minGasLimit) payable external; + function successfulMessages(bytes32) view external returns (bool); + function xDomainMessageSender() view external returns (address); +} +``` + +## Message Passing + +The `sendMessage` function is used to send a cross domain message. To trigger +the execution on the other side, the `relayMessage` function is called. +Successful messages have their hash stored in the `successfulMessages` mapping +while unsuccessful messages have their hash stored in the `receivedMessages` +mapping. + +The user experience when sending from L1 to L2 is a bit different than when +sending a transaction from L2 to L1. When going into L2 from L1, the user does +not need to call `relayMessage` on L2 themselves. The user pays for L2 gas on L1 +and the transaction is automatically pulled into L2 where it is executed on L2. +When going from L2 into L1, the user must call `relayMessage` on the +`L1CrossDomainMessenger` to finalize the withdrawal. This function can only +be called after the finalization window has passed. + +## Upgradability + +The L1 and L2 cross domain messengers should be deployed behind upgradable +proxies. This will allow for updating the message version. + +## Message Versioning + +Messages are versioned based on the first 2 bytes of their nonce. Depending on +the version, messages can have a different serialization and hashing scheme. +The first two bytes of the nonce are reserved for version metadata because +a version field was not originally included in the messages themselves, but +a `uint256` nonce is so large that we can very easily pack additional data +into that field. + +### Message Version 0 + +```solidity +abi.encodeWithSignature( + "relayMessage(address,address,bytes,uint256)", + _target, + _sender, + _message, + _messageNonce +); +``` + +### Message Version 1 + +```solidity +abi.encodeWithSignature( + "relayMessage(uint256,address,address,uint256,uint256,bytes)", + _nonce, + _sender, + _target, + _value, + _gasLimit, + _data +); +``` + +## Backwards Compatibility Notes + +An older version of the messenger contracts had the concept of blocked messages +in a `blockedMessages` mapping. This functionality was removed from the +messengers because a smart attacker could get around any message blocking +attempts. It also saves gas on finalizing withdrawals. + +The concept of a "relay id" and the `relayedMessages` mapping was removed. +It was built as a way to be able to fund third parties who relayed messages +on the behalf of users, but it was improperly implemented as it was impossible +to know if the relayed message actually succeeded. diff --git a/specs/proposals.md b/specs/proposals.md index 5861f0af..511dd300 100644 --- a/specs/proposals.md +++ b/specs/proposals.md @@ -83,10 +83,10 @@ where: L2 state root on L1. 1. The `withdrawal_storage_root` (`bytes32`) elevates the Merkle-Patricia-Trie ([MPT][g-mpt]) root of the [L2 Withdrawal - contract](./withdrawals.md#withdrawer-contract) storage. Instead of making an MPT proof for a withdrawal against the - state root (proving first the storage root of the L2 withdrawal contract against the state root, then the withdrawal - against that storage root), we can prove against the L2 withdrawal contract's storage root directly, thus reducing - the verification cost of withdrawals on L1. + contract](./withdrawals.md#the-l2tol1messagepasser-contract) storage. Instead of making an MPT proof for a withdrawal + against the state root (proving first the storage root of the L2 withdrawal contract against the state root, then + the withdrawal against that storage root), we can prove against the L2 withdrawal contract's storage root directly, + thus reducing the verification cost of withdrawals on L1. ## L2 Output Oracle Smart Contract diff --git a/specs/withdrawals.md b/specs/withdrawals.md index e8ed4f26..e2b3f501 100644 --- a/specs/withdrawals.md +++ b/specs/withdrawals.md @@ -36,7 +36,7 @@ finalization. - [Withdrawal Flow](#withdrawal-flow) - [On L2](#on-l2) - [On L1](#on-l1) -- [The L2 Withdrawer Contract](#the-l2-withdrawer-contract) +- [The L2ToL1MessagePasser Contract](#the-l2tol1messagepasser-contract) - [Address Aliasing](#address-aliasing) - [The Optimism Portal Contract](#the-optimism-portal-contract) - [Withdrawal Verification and Finalization](#withdrawal-verification-and-finalization) @@ -54,7 +54,7 @@ We first describe the end to end flow of initiating and finalizing a withdrawal: ### On L2 -An L2 account sends a withdrawal message (and possibly also ETH) to the `Withdrawer` predeploy contract. +An L2 account sends a withdrawal message (and possibly also ETH) to the `L2ToL1MessagePasser` predeploy contract. This is a very simple contract that stores the a hash of the withdrawal data. ### On L1 @@ -68,34 +68,36 @@ An L2 account sends a withdrawal message (and possibly also ETH) to the `Withdra 3. If verification fails, the call reverts. Otherwise the call is forwarded, and the hash is recorded to prevent it from from being replayed. -## The L2 Withdrawer Contract +## The L2ToL1MessagePasser Contract -[withdrawer-contract]: #the-l2-withdrawer-contract +[message-passer-contract]: #the-l2tol1messagepasser-contract -A withdrawal is initiated by calling the Withdrawer contract's `initiateWithdrawal` function. -The Withdrawer is a simple predeploy contract at `0x4200000000000000000000000000000000000016` which stores messages -to be withdrawn. +A withdrawal is initiated by calling the L2ToL1MessagePasser contract's `initiateWithdrawal` function. +The L2ToL1MessagePasser is a simple predeploy contract at `0x4200000000000000000000000000000000000000` +which stores messages to be withdrawn. ```js -interface Withdrawer { - - event WithdrawalMessage( +interface L2ToL1MessagePasser { + event WithdrawalInitiated( uint256 indexed nonce, // this is a global nonce value for all withdrawal messages address indexed sender, address indexed target, uint256 value, uint256 gasLimit, - bytes message + bytes data ); - function initiateWithdrawal( - address target, - uint256 gasLimit, - bytes data, - ) external payable; + event WithdrawerBalanceBurnt(uint256 indexed amount); + + function burn() external; - function burn(); + function initiateWithdrawal(address _target, uint256 _gasLimit, bytes memory _data) payable external; + + function nonce() view external returns (uint256); + + function sentMessages(bytes32) view external returns (bool); } + ``` ### Address Aliasing @@ -145,7 +147,7 @@ The following inputs are required to verify and finalize a withdrawal: - Proof and verification data: - `timestamp`: The L2 timestamp corresponding with the output root. - `outputRootProof`: Four `bytes32` values which are used to derive the output root. - - `withdrawalProof`: An inclusion proof for the given withdrawal in the withdrawer contract. + - `withdrawalProof`: An inclusion proof for the given withdrawal in the L2ToL1MessagePasser contract. These inputs must satisfy the following conditions: @@ -153,7 +155,7 @@ These inputs must satisfy the following conditions: 1. `OutputOracle.l2Outputs(timestamp)` returns a non-zero value `l2Output`. 1. The keccak256 hash of the `outputRootProof` values is equal to the `l2Output`. 1. The `withdrawalProof` is a valid inclusion proof demonstrating that a hash of the Withdrawal transaction data - is contained in the storage of the Withdrawer contract on L2. + is contained in the storage of the L2ToL1MessagePasser contract on L2. ## Security Considerations