Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x412aCAd86FFa3b287C1043ab4e56F7C4A6A9e385
Balance 0 ETH
Nonce 1
Code Size 11560 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

11560 bytes
0x608060405234801561001057600080fd5b50600436106101515760003560e01c80639bbee240116100cd578063ca4b208b11610081578063e449f34111610066578063e449f34114610351578063f2fde38b14610364578063f953cec71461037757600080fd5b8063ca4b208b14610313578063de9b771f1461033157600080fd5b8063bad96b54116100b2578063bad96b54146102ae578063bbb781cc146102ce578063c0857ba0146102f357600080fd5b80639bbee24014610288578063aea4e49e1461029b57600080fd5b8063715018a6116101245780638da5cb5b116101095780638da5cb5b146101fb5780638f1698161461023a578063972c49281461026857600080fd5b8063715018a6146101eb5780638cc401d5146101f357600080fd5b80630e387de6146101565780630fbf0a931461019057806315b31bbb146101a5578063607f2d42146101b8575b600080fd5b61017d7f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b03681565b6040519081526020015b60405180910390f35b6101a361019e366004612617565b61038a565b005b6101a36101b336600461268c565b61055a565b6101db6101c63660046126ae565b60036020526000908152604090205460ff1681565b6040519015158152602001610187565b6101a361066d565b6101a36106fa565b60045473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610187565b6101db6102483660046126e9565b600760209081526000928352604080842090915290825290205460ff1681565b6002546102159073ffffffffffffffffffffffffffffffffffffffff1681565b6101a3610296366004612715565b6107aa565b6101a36102a9366004612715565b6108ff565b6006546102159073ffffffffffffffffffffffffffffffffffffffff1681565b6006546101db9074010000000000000000000000000000000000000000900460ff1681565b6001546102159073ffffffffffffffffffffffffffffffffffffffff1681565b60055473ffffffffffffffffffffffffffffffffffffffff16610215565b6000546102159073ffffffffffffffffffffffffffffffffffffffff1681565b6101a361035f366004612617565b610a0f565b6101a3610372366004612715565b610bf6565b6101a36103853660046127f6565b610d23565b60065474010000000000000000000000000000000000000000900460ff1615610414576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f5374616b696e672070617573656400000000000000000000000000000000000060448201526064015b60405180910390fd5b60005b818110156105495760065473ffffffffffffffffffffffffffffffffffffffff166323b872dd333086868681811061045157610451612876565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b1580156104cd57600080fd5b505af11580156104e1573d6000803e3d6000fd5b505033600090815260076020526040812060019350915085858581811061050a5761050a612876565b90506020020135815260200190815260200160002060006101000a81548160ff0219169083151502179055508080610541906128d4565b915050610417565b5061055633826001610d33565b5050565b60055473ffffffffffffffffffffffffffffffffffffffff16331480610597575060045473ffffffffffffffffffffffffffffffffffffffff1633145b610623576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201527f206f7220646576656c6f70657200000000000000000000000000000000000000606482015260840161040b565b6006805491151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60045473ffffffffffffffffffffffffffffffffffffffff1633146106ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161040b565b6106f86000610d7c565b565b60055473ffffffffffffffffffffffffffffffffffffffff1633146107a0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f4f776e61626c653a2063616c6c6572206973206e6f742074686520646576656c60448201527f6f70657200000000000000000000000000000000000000000000000000000000606482015260840161040b565b6106f86000610df3565b60055473ffffffffffffffffffffffffffffffffffffffff163314610850576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f4f776e61626c653a2063616c6c6572206973206e6f742074686520646576656c60448201527f6f70657200000000000000000000000000000000000000000000000000000000606482015260840161040b565b73ffffffffffffffffffffffffffffffffffffffff81166108f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f4f776e61626c653a206e657720646576656c6f70657220697320746865207a6560448201527f726f206164647265737300000000000000000000000000000000000000000000606482015260840161040b565b6108fc81610df3565b50565b60055473ffffffffffffffffffffffffffffffffffffffff1633148061093c575060045473ffffffffffffffffffffffffffffffffffffffff1633145b6109c8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201527f206f7220646576656c6f70657200000000000000000000000000000000000000606482015260840161040b565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005b81811015610be95733600090815260076020526040812090848484818110610a3c57610a3c612876565b602090810292909201358352508101919091526040016000205460ff16610abf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f74206f776e65640000000000000000000000000000000000000000000000604482015260640161040b565b60065473ffffffffffffffffffffffffffffffffffffffff166323b872dd3033868686818110610af157610af1612876565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b16815273ffffffffffffffffffffffffffffffffffffffff958616600482015294909316602485015250602090910201356044820152606401600060405180830381600087803b158015610b6d57600080fd5b505af1158015610b81573d6000803e3d6000fd5b5050336000908152600760205260408120909250905081858585818110610baa57610baa612876565b90506020020135815260200190815260200160002060006101000a81548160ff0219169083151502179055508080610be1906128d4565b915050610a12565b5061055633826000610d33565b60045473ffffffffffffffffffffffffffffffffffffffff163314610c77576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161040b565b73ffffffffffffffffffffffffffffffffffffffff8116610d1a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161040b565b6108fc81610d7c565b6000610d2e82610e6a565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff851660208201529081018390528115156060820152610d2e9060800160405160208183030381529060405261124c565b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6005805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907fede61b2c1b6ea8932acda2da1fa8be10c31d93a5ef149f84a2a04c178054044990600090a35050565b60606000610e77836112dd565b90506000610e848261133c565b90506000610e918361136b565b9050600081610e9f84611394565b610ea886611582565b604051602001610eba9392919061293d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301206000818152600390935291205490915060ff1615610f8f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f4678526f6f7454756e6e656c3a20455849545f414c52454144595f50524f434560448201527f5353454400000000000000000000000000000000000000000000000000000000606482015260840161040b565b600081815260036020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055610fcf8561159e565b90506000610fdc826116e8565b9050610fe781611778565b60025473ffffffffffffffffffffffffffffffffffffffff908116911614611091576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4678526f6f7454756e6e656c3a20494e56414c49445f46585f4348494c445f5460448201527f554e4e454c000000000000000000000000000000000000000000000000000000606482015260840161040b565b600061109c876117a1565b90506110bc6110ac846020015190565b876110b68a6117bd565b846117d9565b611148576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f4678526f6f7454756e6e656c3a20494e56414c49445f524543454950545f505260448201527f4f4f460000000000000000000000000000000000000000000000000000000000606482015260840161040b565b6111768561115589611a90565b61115e8a611aac565b846111688c611ac8565b6111718d611ae4565b611b00565b50600061118283611c66565b90507f8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b0366111b86111b3836000611ca2565b611cda565b1461121f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4678526f6f7454756e6e656c3a20494e56414c49445f5349474e415455524500604482015260640161040b565b600061122a84611d55565b80602001905181019061123d919061296a565b9b9a5050505050505050505050565b6000546002546040517fb472047700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9283169263b4720477926112a89291169085906004016129e1565b600060405180830381600087803b1580156112c257600080fd5b505af11580156112d6573d6000803e3d6000fd5b5050505050565b60408051602081019091526060815260006113276113228460408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b611d71565b60408051602081019091529081529392505050565b6060611365826000015160088151811061135857611358612876565b6020026020010151611e87565b92915050565b6000611365826000015160028151811061138757611387612876565b6020026020010151611cda565b60408051602081019091526000815281516060919015611365576000806113bc600086611f24565b60f81c905060018114806113d357508060ff166003145b15611493576001855160026113e89190612a4f565b6113f29190612a8c565b67ffffffffffffffff81111561140a5761140a612732565b6040519080825280601f01601f191660200182016040528015611434576020820181803683370190505b5092506000611444600187611f24565b9050808460008151811061145a5761145a612876565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060019250506114f7565b6002855160026114a39190612a4f565b6114ad9190612a8c565b67ffffffffffffffff8111156114c5576114c5612732565b6040519080825280601f01601f1916602001820160405280156114ef576020820181803683370190505b509250600091505b60ff82165b83518110156115795761152661151560ff851683612a8c565b611520906002612aa3565b87611f24565b84828151811061153857611538612876565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080611571816128d4565b9150506114fc565b50505092915050565b6000611365826000015160098151811061138757611387612876565b6115c260405180606001604052806060815260200160608152602001600081525090565b6115dc826000015160068151811061135857611358612876565b60208281018290526040805180820182526000808252908301528051808201909152825181529181019082015261161281611fa5565b156116275761162081611d71565b82526116d4565b6020820151805160009061163d90600190612a8c565b67ffffffffffffffff81111561165557611655612732565b6040519080825280601f01601f19166020018201604052801561167f576020820181803683370190505b50905060008083602101915082602001905061169d82828551611fde565b6040805180820182526000808252602091820152815180830190925284518252808501908201526116cd90611d71565b8652505050505b6116dd83611582565b604083015250919050565b604080516080810182526000918101828152606080830193909352815260208101919091526000611736836000015160038151811061172957611729612876565b6020026020010151611d71565b83604001518151811061174b5761174b612876565b60200260200101519050604051806040016040528082815260200161176f83611d71565b90529392505050565b6000611365826020015160008151811061179457611794612876565b6020026020010151612059565b6000611365826000015160058151811061138757611387612876565b6060611365826000015160078151811061135857611358612876565b60008061180d8460408051808201825260008082526020918201528151808301909252825182529182019181019190915290565b9050600061181a82611d71565b90506060808560008061182c8b611394565b9050805160001415611848576000975050505050505050611a88565b60005b8651811015611a7f57815183111561186e57600098505050505050505050611a88565b61189087828151811061188357611883612876565b6020026020010151612073565b9550858051906020012084146118b157600098505050505050505050611a88565b6118c687828151811061172957611729612876565b945084516011141561199b578151831415611928578c805190602001206118f98660108151811061135857611358612876565b80519060200120141561191757600198505050505050505050611a88565b600098505050505050505050611a88565b600082848151811061193c5761193c612876565b016020015160f81c905060108111156119615760009950505050505050505050611a88565b611986868260ff168151811061197957611979612876565b60200260200101516120f3565b9450611993600185612aa3565b935050611a6d565b8451600214156119175760006119c76119c08760008151811061135857611358612876565b8486612121565b83519091506119d68286612aa3565b1415611a2b578d805190602001206119fa8760018151811061135857611358612876565b805190602001201415611a195760019950505050505050505050611a88565b60009950505050505050505050611a88565b80611a425760009950505050505050505050611a88565b611a4c8185612aa3565b9350611a648660018151811061197957611979612876565b9450611a6d9050565b80611a77816128d4565b91505061184b565b50505050505050505b949350505050565b6000611365826000015160038151811061138757611387612876565b6000611365826000015160048151811061138757611387612876565b6000611365826000015160008151811061138757611387612876565b6060611365826000015160018151811061135857611358612876565b6001546040517f41539d4a0000000000000000000000000000000000000000000000000000000081526004810184905260009182918291829173ffffffffffffffffffffffffffffffffffffffff909116906341539d4a9060240160a060405180830381865afa158015611b78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b9c9190612abb565b5093505092509250611bf3828b611bb39190612a8c565b6040805160208082018f90528183018e9052606082018d905260808083018d90528351808403909101815260a0909201909252805191012090858861225a565b611c59576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f4678526f6f7454756e6e656c3a20494e56414c49445f48454144455200000000604482015260640161040b565b9998505050505050505050565b6040805160208101909152606081526040518060200160405280611c9a846020015160018151811061172957611729612876565b905292915050565b60408051808201909152600080825260208201528251805183908110611cca57611cca612876565b6020026020010151905092915050565b805160009015801590611cef57508151602110155b611cf857600080fd5b6000611d078360200151612404565b90506000818460000151611d1b9190612a8c565b9050600080838660200151611d309190612aa3565b9050805191506020831015611d4c57826020036101000a820491505b50949350505050565b6060611365826020015160028151811061135857611358612876565b6060611d7c82611fa5565b611d8557600080fd5b6000611d9083612486565b905060008167ffffffffffffffff811115611dad57611dad612732565b604051908082528060200260200182016040528015611df257816020015b6040805180820190915260008082526020820152815260200190600190039081611dcb5790505b5090506000611e048560200151612404565b8560200151611e139190612aa3565b90506000805b84811015611e7c57611e2a83612509565b9150604051806040016040528083815260200184815250848281518110611e5357611e53612876565b6020908102919091010152611e688284612aa3565b925080611e74816128d4565b915050611e19565b509195945050505050565b8051606090611e9557600080fd5b6000611ea48360200151612404565b90506000818460000151611eb89190612a8c565b905060008167ffffffffffffffff811115611ed557611ed5612732565b6040519080825280601f01601f191660200182016040528015611eff576020820181803683370190505b5090506000816020019050611d4c848760200151611f1d9190612aa3565b82856125cb565b6000611f31600284612b37565b15611f6b57601082611f44600286612b4b565b81518110611f5457611f54612876565b0160200151611f66919060f81c612b5f565b611f9b565b601082611f79600286612b4b565b81518110611f8957611f89612876565b0160200151611f9b919060f81c612b81565b60f81b9392505050565b8051600090611fb657506000919050565b6020820151805160001a9060c0821015611fd4575060009392505050565b5060019392505050565b80611fe857505050565b602081106120205782518252611fff602084612aa3565b925061200c602083612aa3565b9150612019602082612a8c565b9050611fe8565b6000600161202f836020612a8c565b61203b90610100612cc3565b6120459190612a8c565b935183518516941916939093179091525050565b805160009060151461206a57600080fd5b61136582611cda565b60606000826000015167ffffffffffffffff81111561209457612094612732565b6040519080825280601f01601f1916602001820160405280156120be576020820181803683370190505b5090508051600014156120d15792915050565b60008160200190506120ec84602001518286600001516125cb565b5092915050565b805160009060211461210457600080fd5b600080836020015160016121189190612aa3565b51949350505050565b6000808061212e86611394565b90506000815167ffffffffffffffff81111561214c5761214c612732565b6040519080825280601f01601f191660200182016040528015612176576020820181803683370190505b509050845b82516121879087612aa3565b81101561222a5760008782815181106121a2576121a2612876565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016905080836121d78985612a8c565b815181106121e7576121e7612876565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350508080612222906128d4565b91505061217b565b5080805190602001208280519060200120141561224a578151925061224f565b600092505b509095945050505050565b60006020825161226a9190612b37565b156122d1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f496e76616c69642070726f6f66206c656e677468000000000000000000000000604482015260640161040b565b6000602083516122e19190612b4b565b90506122ee816002612cc3565b8510612356576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4c65616620696e64657820697320746f6f206269670000000000000000000000604482015260640161040b565b60008660205b855181116123f657858101519250612375600289612b37565b6123aa5760408051602081018490529081018490526060016040516020818303038152906040528051906020012091506123d7565b60408051602081018590529081018390526060016040516020818303038152906040528051906020012091505b6123e2600289612b4b565b97506123ef602082612aa3565b905061235c565b509094149695505050505050565b8051600090811a608081101561241d5750600092915050565b60b8811080612438575060c08110801590612438575060f881105b156124465750600192915050565b60c081101561247a5761245b600160b8612ccf565b6124689060ff1682612a8c565b612473906001612aa3565b9392505050565b61245b600160f8612ccf565b805160009061249757506000919050565b6000806124a78460200151612404565b84602001516124b69190612aa3565b90506000846000015185602001516124ce9190612aa3565b90505b80821015612500576124e282612509565b6124ec9083612aa3565b9150826124f8816128d4565b9350506124d1565b50909392505050565b80516000908190811a608081101561252457600191506120ec565b60b881101561254a57612538608082612a8c565b612543906001612aa3565b91506120ec565b60c08110156125775760b78103600185019450806020036101000a855104600182018101935050506120ec565b60f881101561258b5761253860c082612a8c565b60019390930151602084900360f7016101000a90049092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0a0192915050565b806125d557505050565b6020811061260d57825182526125ec602084612aa3565b92506125f9602083612aa3565b9150612606602082612a8c565b90506125d5565b8061202057505050565b6000806020838503121561262a57600080fd5b823567ffffffffffffffff8082111561264257600080fd5b818501915085601f83011261265657600080fd5b81358181111561266557600080fd5b8660208260051b850101111561267a57600080fd5b60209290920196919550909350505050565b60006020828403121561269e57600080fd5b8135801515811461247357600080fd5b6000602082840312156126c057600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146108fc57600080fd5b600080604083850312156126fc57600080fd5b8235612707816126c7565b946020939093013593505050565b60006020828403121561272757600080fd5b8135612473816126c7565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156127a8576127a8612732565b604052919050565b600067ffffffffffffffff8211156127ca576127ca612732565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60006020828403121561280857600080fd5b813567ffffffffffffffff81111561281f57600080fd5b8201601f8101841361283057600080fd5b803561284361283e826127b0565b612761565b81815285602083850101111561285857600080fd5b81602084016020830137600091810160200191909152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415612906576129066128a5565b5060010190565b60005b83811015612928578181015183820152602001612910565b83811115612937576000848401525b50505050565b8381526000835161295581602085016020880161290d565b60209201918201929092526040019392505050565b60006020828403121561297c57600080fd5b815167ffffffffffffffff81111561299357600080fd5b8201601f810184136129a457600080fd5b80516129b261283e826127b0565b8181528560208385010111156129c757600080fd5b6129d882602083016020860161290d565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff831681526040602082015260008251806040840152612a1c81606085016020870161290d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016060019392505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612a8757612a876128a5565b500290565b600082821015612a9e57612a9e6128a5565b500390565b60008219821115612ab657612ab66128a5565b500190565b600080600080600060a08688031215612ad357600080fd5b855194506020860151935060408601519250606086015191506080860151612afa816126c7565b809150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082612b4657612b46612b08565b500690565b600082612b5a57612b5a612b08565b500490565b600060ff831680612b7257612b72612b08565b8060ff84160691505092915050565b600060ff831680612b9457612b94612b08565b8060ff84160491505092915050565b600181815b80851115612bfc57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612be257612be26128a5565b80851615612bef57918102915b93841c9390800290612ba8565b509250929050565b600082612c1357506001611365565b81612c2057506000611365565b8160018114612c365760028114612c4057612c5c565b6001915050611365565b60ff841115612c5157612c516128a5565b50506001821b611365565b5060208310610133831016604e8410600b8410161715612c7f575081810a611365565b612c898383612ba3565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115612cbb57612cbb6128a5565b029392505050565b60006124738383612c04565b600060ff821660ff841680821015612ce957612ce96128a5565b9003939250505056fea2646970667358221220525dc0e88414b7326bd513ba35a8439396dca1f584548c353c39dd319a64526c64736f6c634300080a0033

Verified Source Code Partial Match

Compiler: v0.8.10+commit.fc410830 EVM: london Optimization: Yes (100000 runs)
GACStaking.sol 1358 lines
// SPDX-License-Identifier: MIT
// File: contracts/fx-portal/lib/Merkle.sol


pragma solidity ^0.8.0;

library Merkle {
    function checkMembership(
        bytes32 leaf,
        uint256 index,
        bytes32 rootHash,
        bytes memory proof
    ) internal pure returns (bool) {
        require(proof.length % 32 == 0, "Invalid proof length");
        uint256 proofHeight = proof.length / 32;
        // Proof of size n means, height of the tree is n+1.
        // In a tree of height n+1, max #leafs possible is 2 ^ n
        require(index < 2**proofHeight, "Leaf index is too big");

        bytes32 proofElement;
        bytes32 computedHash = leaf;
        for (uint256 i = 32; i <= proof.length; i += 32) {
            assembly {
                proofElement := mload(add(proof, i))
            }

            if (index % 2 == 0) {
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }

            index = index / 2;
        }
        return computedHash == rootHash;
    }
}

// File: contracts/fx-portal/lib/RLPReader.sol

/*
 * @author Hamdi Allam [email protected]
 * Please reach out with any questions or concerns
 */
pragma solidity ^0.8.0;

library RLPReader {
    uint8 constant STRING_SHORT_START = 0x80;
    uint8 constant STRING_LONG_START = 0xb8;
    uint8 constant LIST_SHORT_START = 0xc0;
    uint8 constant LIST_LONG_START = 0xf8;
    uint8 constant WORD_SIZE = 32;

    struct RLPItem {
        uint256 len;
        uint256 memPtr;
    }

    struct Iterator {
        RLPItem item; // Item that's being iterated over.
        uint256 nextPtr; // Position of the next item in the list.
    }

    /*
     * @dev Returns the next element in the iteration. Reverts if it has not next element.
     * @param self The iterator.
     * @return The next element in the iteration.
     */
    function next(Iterator memory self) internal pure returns (RLPItem memory) {
        require(hasNext(self));

        uint256 ptr = self.nextPtr;
        uint256 itemLength = _itemLength(ptr);
        self.nextPtr = ptr + itemLength;

        return RLPItem(itemLength, ptr);
    }

    /*
     * @dev Returns true if the iteration has more elements.
     * @param self The iterator.
     * @return true if the iteration has more elements.
     */
    function hasNext(Iterator memory self) internal pure returns (bool) {
        RLPItem memory item = self.item;
        return self.nextPtr < item.memPtr + item.len;
    }

    /*
     * @param item RLP encoded bytes
     */
    function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) {
        uint256 memPtr;
        assembly {
            memPtr := add(item, 0x20)
        }

        return RLPItem(item.length, memPtr);
    }

    /*
     * @dev Create an iterator. Reverts if item is not a list.
     * @param self The RLP item.
     * @return An 'Iterator' over the item.
     */
    function iterator(RLPItem memory self) internal pure returns (Iterator memory) {
        require(isList(self));

        uint256 ptr = self.memPtr + _payloadOffset(self.memPtr);
        return Iterator(self, ptr);
    }

    /*
     * @param item RLP encoded bytes
     */
    function rlpLen(RLPItem memory item) internal pure returns (uint256) {
        return item.len;
    }

    /*
     * @param item RLP encoded bytes
     */
    function payloadLen(RLPItem memory item) internal pure returns (uint256) {
        return item.len - _payloadOffset(item.memPtr);
    }

    /*
     * @param item RLP encoded list in bytes
     */
    function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) {
        require(isList(item));

        uint256 items = numItems(item);
        RLPItem[] memory result = new RLPItem[](items);

        uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr);
        uint256 dataLen;
        for (uint256 i = 0; i < items; i++) {
            dataLen = _itemLength(memPtr);
            result[i] = RLPItem(dataLen, memPtr);
            memPtr = memPtr + dataLen;
        }

        return result;
    }

    // @return indicator whether encoded payload is a list. negate this function call for isData.
    function isList(RLPItem memory item) internal pure returns (bool) {
        if (item.len == 0) return false;

        uint8 byte0;
        uint256 memPtr = item.memPtr;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < LIST_SHORT_START) return false;
        return true;
    }

    /*
     * @dev A cheaper version of keccak256(toRlpBytes(item)) that avoids copying memory.
     * @return keccak256 hash of RLP encoded bytes.
     */
    function rlpBytesKeccak256(RLPItem memory item) internal pure returns (bytes32) {
        uint256 ptr = item.memPtr;
        uint256 len = item.len;
        bytes32 result;
        assembly {
            result := keccak256(ptr, len)
        }
        return result;
    }

    function payloadLocation(RLPItem memory item) internal pure returns (uint256, uint256) {
        uint256 offset = _payloadOffset(item.memPtr);
        uint256 memPtr = item.memPtr + offset;
        uint256 len = item.len - offset; // data length
        return (memPtr, len);
    }

    /*
     * @dev A cheaper version of keccak256(toBytes(item)) that avoids copying memory.
     * @return keccak256 hash of the item payload.
     */
    function payloadKeccak256(RLPItem memory item) internal pure returns (bytes32) {
        (uint256 memPtr, uint256 len) = payloadLocation(item);
        bytes32 result;
        assembly {
            result := keccak256(memPtr, len)
        }
        return result;
    }

    /** RLPItem conversions into data types **/

    // @returns raw rlp encoding in bytes
    function toRlpBytes(RLPItem memory item) internal pure returns (bytes memory) {
        bytes memory result = new bytes(item.len);
        if (result.length == 0) return result;

        uint256 ptr;
        assembly {
            ptr := add(0x20, result)
        }

        copy(item.memPtr, ptr, item.len);
        return result;
    }

    // any non-zero byte is considered true
    function toBoolean(RLPItem memory item) internal pure returns (bool) {
        require(item.len == 1);
        uint256 result;
        uint256 memPtr = item.memPtr;
        assembly {
            result := byte(0, mload(memPtr))
        }

        return result == 0 ? false : true;
    }

    function toAddress(RLPItem memory item) internal pure returns (address) {
        // 1 byte for the length prefix
        require(item.len == 21);

        return address(uint160(toUint(item)));
    }

    function toUint(RLPItem memory item) internal pure returns (uint256) {
        require(item.len > 0 && item.len <= 33);

        uint256 offset = _payloadOffset(item.memPtr);
        uint256 len = item.len - offset;

        uint256 result;
        uint256 memPtr = item.memPtr + offset;
        assembly {
            result := mload(memPtr)

            // shfit to the correct location if neccesary
            if lt(len, 32) {
                result := div(result, exp(256, sub(32, len)))
            }
        }

        return result;
    }

    // enforces 32 byte length
    function toUintStrict(RLPItem memory item) internal pure returns (uint256) {
        // one byte prefix
        require(item.len == 33);

        uint256 result;
        uint256 memPtr = item.memPtr + 1;
        assembly {
            result := mload(memPtr)
        }

        return result;
    }

    function toBytes(RLPItem memory item) internal pure returns (bytes memory) {
        require(item.len > 0);

        uint256 offset = _payloadOffset(item.memPtr);
        uint256 len = item.len - offset; // data length
        bytes memory result = new bytes(len);

        uint256 destPtr;
        assembly {
            destPtr := add(0x20, result)
        }

        copy(item.memPtr + offset, destPtr, len);
        return result;
    }

    /*
     * Private Helpers
     */

    // @return number of payload items inside an encoded list.
    function numItems(RLPItem memory item) private pure returns (uint256) {
        if (item.len == 0) return 0;

        uint256 count = 0;
        uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr);
        uint256 endPtr = item.memPtr + item.len;
        while (currPtr < endPtr) {
            currPtr = currPtr + _itemLength(currPtr); // skip over an item
            count++;
        }

        return count;
    }

    // @return entire rlp item byte length
    function _itemLength(uint256 memPtr) private pure returns (uint256) {
        uint256 itemLen;
        uint256 byte0;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < STRING_SHORT_START) itemLen = 1;
        else if (byte0 < STRING_LONG_START) itemLen = byte0 - STRING_SHORT_START + 1;
        else if (byte0 < LIST_SHORT_START) {
            assembly {
                let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is
                memPtr := add(memPtr, 1) // skip over the first byte
                /* 32 byte word size */
                let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len
                itemLen := add(dataLen, add(byteLen, 1))
            }
        } else if (byte0 < LIST_LONG_START) {
            itemLen = byte0 - LIST_SHORT_START + 1;
        } else {
            assembly {
                let byteLen := sub(byte0, 0xf7)
                memPtr := add(memPtr, 1)

                let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length
                itemLen := add(dataLen, add(byteLen, 1))
            }
        }

        return itemLen;
    }

    // @return number of bytes until the data
    function _payloadOffset(uint256 memPtr) private pure returns (uint256) {
        uint256 byte0;
        assembly {
            byte0 := byte(0, mload(memPtr))
        }

        if (byte0 < STRING_SHORT_START) return 0;
        else if (byte0 < STRING_LONG_START || (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START)) return 1;
        else if (byte0 < LIST_SHORT_START)
            // being explicit
            return byte0 - (STRING_LONG_START - 1) + 1;
        else return byte0 - (LIST_LONG_START - 1) + 1;
    }

    /*
     * @param src Pointer to source
     * @param dest Pointer to destination
     * @param len Amount of memory to copy from the source
     */
    function copy(
        uint256 src,
        uint256 dest,
        uint256 len
    ) private pure {
        if (len == 0) return;

        // copy as many word sizes as possible
        for (; len >= WORD_SIZE; len -= WORD_SIZE) {
            assembly {
                mstore(dest, mload(src))
            }

            src += WORD_SIZE;
            dest += WORD_SIZE;
        }

        if (len == 0) return;

        // left over bytes. Mask is used to remove unwanted bytes from the word
        uint256 mask = 256**(WORD_SIZE - len) - 1;

        assembly {
            let srcpart := and(mload(src), not(mask)) // zero out src
            let destpart := and(mload(dest), mask) // retrieve the bytes
            mstore(dest, or(destpart, srcpart))
        }
    }
}

// File: contracts/fx-portal/lib/ExitPayloadReader.sol

pragma solidity ^0.8.0;


library ExitPayloadReader {
    using RLPReader for bytes;
    using RLPReader for RLPReader.RLPItem;

    uint8 constant WORD_SIZE = 32;

    struct ExitPayload {
        RLPReader.RLPItem[] data;
    }

    struct Receipt {
        RLPReader.RLPItem[] data;
        bytes raw;
        uint256 logIndex;
    }

    struct Log {
        RLPReader.RLPItem data;
        RLPReader.RLPItem[] list;
    }

    struct LogTopics {
        RLPReader.RLPItem[] data;
    }

    // copy paste of private copy() from RLPReader to avoid changing of existing contracts
    function copy(
        uint256 src,
        uint256 dest,
        uint256 len
    ) private pure {
        if (len == 0) return;

        // copy as many word sizes as possible
        for (; len >= WORD_SIZE; len -= WORD_SIZE) {
            assembly {
                mstore(dest, mload(src))
            }

            src += WORD_SIZE;
            dest += WORD_SIZE;
        }

        // left over bytes. Mask is used to remove unwanted bytes from the word
        uint256 mask = 256**(WORD_SIZE - len) - 1;
        assembly {
            let srcpart := and(mload(src), not(mask)) // zero out src
            let destpart := and(mload(dest), mask) // retrieve the bytes
            mstore(dest, or(destpart, srcpart))
        }
    }

    function toExitPayload(bytes memory data) internal pure returns (ExitPayload memory) {
        RLPReader.RLPItem[] memory payloadData = data.toRlpItem().toList();

        return ExitPayload(payloadData);
    }

    function getHeaderNumber(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[0].toUint();
    }

    function getBlockProof(ExitPayload memory payload) internal pure returns (bytes memory) {
        return payload.data[1].toBytes();
    }

    function getBlockNumber(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[2].toUint();
    }

    function getBlockTime(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[3].toUint();
    }

    function getTxRoot(ExitPayload memory payload) internal pure returns (bytes32) {
        return bytes32(payload.data[4].toUint());
    }

    function getReceiptRoot(ExitPayload memory payload) internal pure returns (bytes32) {
        return bytes32(payload.data[5].toUint());
    }

    function getReceipt(ExitPayload memory payload) internal pure returns (Receipt memory receipt) {
        receipt.raw = payload.data[6].toBytes();
        RLPReader.RLPItem memory receiptItem = receipt.raw.toRlpItem();

        if (receiptItem.isList()) {
            // legacy tx
            receipt.data = receiptItem.toList();
        } else {
            // pop first byte before parsting receipt
            bytes memory typedBytes = receipt.raw;
            bytes memory result = new bytes(typedBytes.length - 1);
            uint256 srcPtr;
            uint256 destPtr;
            assembly {
                srcPtr := add(33, typedBytes)
                destPtr := add(0x20, result)
            }

            copy(srcPtr, destPtr, result.length);
            receipt.data = result.toRlpItem().toList();
        }

        receipt.logIndex = getReceiptLogIndex(payload);
        return receipt;
    }

    function getReceiptProof(ExitPayload memory payload) internal pure returns (bytes memory) {
        return payload.data[7].toBytes();
    }

    function getBranchMaskAsBytes(ExitPayload memory payload) internal pure returns (bytes memory) {
        return payload.data[8].toBytes();
    }

    function getBranchMaskAsUint(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[8].toUint();
    }

    function getReceiptLogIndex(ExitPayload memory payload) internal pure returns (uint256) {
        return payload.data[9].toUint();
    }

    // Receipt methods
    function toBytes(Receipt memory receipt) internal pure returns (bytes memory) {
        return receipt.raw;
    }

    function getLog(Receipt memory receipt) internal pure returns (Log memory) {
        RLPReader.RLPItem memory logData = receipt.data[3].toList()[receipt.logIndex];
        return Log(logData, logData.toList());
    }

    // Log methods
    function getEmitter(Log memory log) internal pure returns (address) {
        return RLPReader.toAddress(log.list[0]);
    }

    function getTopics(Log memory log) internal pure returns (LogTopics memory) {
        return LogTopics(log.list[1].toList());
    }

    function getData(Log memory log) internal pure returns (bytes memory) {
        return log.list[2].toBytes();
    }

    function toRlpBytes(Log memory log) internal pure returns (bytes memory) {
        return log.data.toRlpBytes();
    }

    // LogTopics methods
    function getField(LogTopics memory topics, uint256 index) internal pure returns (RLPReader.RLPItem memory) {
        return topics.data[index];
    }
}

// File: contracts/fx-portal/lib/MerklePatriciaProof.sol


pragma solidity ^0.8.0;


library MerklePatriciaProof {
    /*
     * @dev Verifies a merkle patricia proof.
     * @param value The terminating value in the trie.
     * @param encodedPath The path in the trie leading to value.
     * @param rlpParentNodes The rlp encoded stack of nodes.
     * @param root The root hash of the trie.
     * @return The boolean validity of the proof.
     */
    function verify(
        bytes memory value,
        bytes memory encodedPath,
        bytes memory rlpParentNodes,
        bytes32 root
    ) internal pure returns (bool) {
        RLPReader.RLPItem memory item = RLPReader.toRlpItem(rlpParentNodes);
        RLPReader.RLPItem[] memory parentNodes = RLPReader.toList(item);

        bytes memory currentNode;
        RLPReader.RLPItem[] memory currentNodeList;

        bytes32 nodeKey = root;
        uint256 pathPtr = 0;

        bytes memory path = _getNibbleArray(encodedPath);
        if (path.length == 0) {
            return false;
        }

        for (uint256 i = 0; i < parentNodes.length; i++) {
            if (pathPtr > path.length) {
                return false;
            }

            currentNode = RLPReader.toRlpBytes(parentNodes[i]);
            if (nodeKey != keccak256(currentNode)) {
                return false;
            }
            currentNodeList = RLPReader.toList(parentNodes[i]);

            if (currentNodeList.length == 17) {
                if (pathPtr == path.length) {
                    if (keccak256(RLPReader.toBytes(currentNodeList[16])) == keccak256(value)) {
                        return true;
                    } else {
                        return false;
                    }
                }

                uint8 nextPathNibble = uint8(path[pathPtr]);
                if (nextPathNibble > 16) {
                    return false;
                }
                nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[nextPathNibble]));
                pathPtr += 1;
            } else if (currentNodeList.length == 2) {
                uint256 traversed = _nibblesToTraverse(RLPReader.toBytes(currentNodeList[0]), path, pathPtr);
                if (pathPtr + traversed == path.length) {
                    //leaf node
                    if (keccak256(RLPReader.toBytes(currentNodeList[1])) == keccak256(value)) {
                        return true;
                    } else {
                        return false;
                    }
                }

                //extension node
                if (traversed == 0) {
                    return false;
                }

                pathPtr += traversed;
                nodeKey = bytes32(RLPReader.toUintStrict(currentNodeList[1]));
            } else {
                return false;
            }
        }
    }

    function _nibblesToTraverse(
        bytes memory encodedPartialPath,
        bytes memory path,
        uint256 pathPtr
    ) private pure returns (uint256) {
        uint256 len = 0;
        // encodedPartialPath has elements that are each two hex characters (1 byte), but partialPath
        // and slicedPath have elements that are each one hex character (1 nibble)
        bytes memory partialPath = _getNibbleArray(encodedPartialPath);
        bytes memory slicedPath = new bytes(partialPath.length);

        // pathPtr counts nibbles in path
        // partialPath.length is a number of nibbles
        for (uint256 i = pathPtr; i < pathPtr + partialPath.length; i++) {
            bytes1 pathNibble = path[i];
            slicedPath[i - pathPtr] = pathNibble;
        }

        if (keccak256(partialPath) == keccak256(slicedPath)) {
            len = partialPath.length;
        } else {
            len = 0;
        }
        return len;
    }

    // bytes b must be hp encoded
    function _getNibbleArray(bytes memory b) internal pure returns (bytes memory) {
        bytes memory nibbles = "";
        if (b.length > 0) {
            uint8 offset;
            uint8 hpNibble = uint8(_getNthNibbleOfBytes(0, b));
            if (hpNibble == 1 || hpNibble == 3) {
                nibbles = new bytes(b.length * 2 - 1);
                bytes1 oddNibble = _getNthNibbleOfBytes(1, b);
                nibbles[0] = oddNibble;
                offset = 1;
            } else {
                nibbles = new bytes(b.length * 2 - 2);
                offset = 0;
            }

            for (uint256 i = offset; i < nibbles.length; i++) {
                nibbles[i] = _getNthNibbleOfBytes(i - offset + 2, b);
            }
        }
        return nibbles;
    }

    function _getNthNibbleOfBytes(uint256 n, bytes memory str) private pure returns (bytes1) {
        return bytes1(n % 2 == 0 ? uint8(str[n / 2]) / 0x10 : uint8(str[n / 2]) % 0x10);
    }
}

// File: contracts/fx-portal/tunnel/FxBaseRootTunnel.sol


pragma solidity ^0.8.0;





interface IFxStateSender {
    function sendMessageToChild(address _receiver, bytes calldata _data) external;
}

contract ICheckpointManager {
    struct HeaderBlock {
        bytes32 root;
        uint256 start;
        uint256 end;
        uint256 createdAt;
        address proposer;
    }

    /**
     * @notice mapping of checkpoint header numbers to block details
     * @dev These checkpoints are submited by plasma contracts
     */
    mapping(uint256 => HeaderBlock) public headerBlocks;
}

abstract contract FxBaseRootTunnel {
    using RLPReader for RLPReader.RLPItem;
    using Merkle for bytes32;
    using ExitPayloadReader for bytes;
    using ExitPayloadReader for ExitPayloadReader.ExitPayload;
    using ExitPayloadReader for ExitPayloadReader.Log;
    using ExitPayloadReader for ExitPayloadReader.LogTopics;
    using ExitPayloadReader for ExitPayloadReader.Receipt;

    // keccak256(MessageSent(bytes))
    bytes32 public constant SEND_MESSAGE_EVENT_SIG = 0x8c5261668696ce22758910d05bab8f186d6eb247ceac2af2e82c7dc17669b036;

    // state sender contract
    IFxStateSender public fxRoot;
    // root chain manager
    ICheckpointManager public checkpointManager;
    // child tunnel contract which receives and sends messages
    address public fxChildTunnel;

    // storage to avoid duplicate exits
    mapping(bytes32 => bool) public processedExits;

    constructor(address _checkpointManager, address _fxRoot) {
        checkpointManager = ICheckpointManager(_checkpointManager);
        fxRoot = IFxStateSender(_fxRoot);
    }

    // set fxChildTunnel if not set already
    function setFxChildTunnel(address _fxChildTunnel) public virtual {
        require(fxChildTunnel == address(0x0), "FxBaseRootTunnel: CHILD_TUNNEL_ALREADY_SET");
        fxChildTunnel = _fxChildTunnel;
    }

    /**
     * @notice Send bytes message to Child Tunnel
     * @param message bytes message that will be sent to Child Tunnel
     * some message examples -
     *   abi.encode(tokenId);
     *   abi.encode(tokenId, tokenMetadata);
     *   abi.encode(messageType, messageData);
     */
    function _sendMessageToChild(bytes memory message) internal {
        fxRoot.sendMessageToChild(fxChildTunnel, message);
    }

    function _validateAndExtractMessage(bytes memory inputData) internal returns (bytes memory) {
        ExitPayloadReader.ExitPayload memory payload = inputData.toExitPayload();

        bytes memory branchMaskBytes = payload.getBranchMaskAsBytes();
        uint256 blockNumber = payload.getBlockNumber();
        // checking if exit has already been processed
        // unique exit is identified using hash of (blockNumber, branchMask, receiptLogIndex)
        bytes32 exitHash = keccak256(
            abi.encodePacked(
                blockNumber,
                // first 2 nibbles are dropped while generating nibble array
                // this allows branch masks that are valid but bypass exitHash check (changing first 2 nibbles only)
                // so converting to nibble array and then hashing it
                MerklePatriciaProof._getNibbleArray(branchMaskBytes),
                payload.getReceiptLogIndex()
            )
        );
        require(processedExits[exitHash] == false, "FxRootTunnel: EXIT_ALREADY_PROCESSED");
        processedExits[exitHash] = true;

        ExitPayloadReader.Receipt memory receipt = payload.getReceipt();
        ExitPayloadReader.Log memory log = receipt.getLog();

        // check child tunnel
        require(fxChildTunnel == log.getEmitter(), "FxRootTunnel: INVALID_FX_CHILD_TUNNEL");

        bytes32 receiptRoot = payload.getReceiptRoot();
        // verify receipt inclusion
        require(
            MerklePatriciaProof.verify(receipt.toBytes(), branchMaskBytes, payload.getReceiptProof(), receiptRoot),
            "FxRootTunnel: INVALID_RECEIPT_PROOF"
        );

        // verify checkpoint inclusion
        _checkBlockMembershipInCheckpoint(
            blockNumber,
            payload.getBlockTime(),
            payload.getTxRoot(),
            receiptRoot,
            payload.getHeaderNumber(),
            payload.getBlockProof()
        );

        ExitPayloadReader.LogTopics memory topics = log.getTopics();

        require(
            bytes32(topics.getField(0).toUint()) == SEND_MESSAGE_EVENT_SIG, // topic0 is event sig
            "FxRootTunnel: INVALID_SIGNATURE"
        );

        // received message data
        bytes memory message = abi.decode(log.getData(), (bytes)); // event decodes params again, so decoding bytes to get message
        return message;
    }

    function _checkBlockMembershipInCheckpoint(
        uint256 blockNumber,
        uint256 blockTime,
        bytes32 txRoot,
        bytes32 receiptRoot,
        uint256 headerNumber,
        bytes memory blockProof
    ) private view returns (uint256) {
        (bytes32 headerRoot, uint256 startBlock, , uint256 createdAt, ) = checkpointManager.headerBlocks(headerNumber);

        require(
            keccak256(abi.encodePacked(blockNumber, blockTime, txRoot, receiptRoot)).checkMembership(
                blockNumber - startBlock,
                headerRoot,
                blockProof
            ),
            "FxRootTunnel: INVALID_HEADER"
        );
        return createdAt;
    }

    /**
     * @notice receive message from  L2 to L1, validated by proof
     * @dev This function verifies if the transaction actually happened on child chain
     *
     * @param inputData RLP encoded data of the reference tx containing following list of fields
     *  0 - headerNumber - Checkpoint header block number containing the reference tx
     *  1 - blockProof - Proof that the block header (in the child chain) is a leaf in the submitted merkle root
     *  2 - blockNumber - Block number containing the reference tx on child chain
     *  3 - blockTime - Reference tx block time
     *  4 - txRoot - Transactions root of block
     *  5 - receiptRoot - Receipts root of block
     *  6 - receipt - Receipt of the reference transaction
     *  7 - receiptProof - Merkle proof of the reference receipt
     *  8 - branchMask - 32 bits denoting the path of receipt in merkle tree
     *  9 - receiptLogIndex - Log Index to read from the receipt
     */
    function receiveMessage(bytes memory inputData) public virtual {
        bytes memory message = _validateAndExtractMessage(inputData);
        _processMessageFromChild(message);
    }

    /**
     * @notice Process message received from Child Tunnel
     * @dev function needs to be implemented to handle message as per requirement
     * This is called by onStateReceive function.
     * Since it is called via a system call, any event will not be emitted during its execution.
     * @param message bytes message that was sent from Child Tunnel
     */
    function _processMessageFromChild(bytes memory message) internal virtual;
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol


// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: @openzeppelin/contracts/token/ERC721/IERC721.sol


// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;


/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

// File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol


// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;


/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// File: @openzeppelin/contracts/utils/Context.sol


// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: contracts/access/DeveloperAccess.sol



pragma solidity ^0.8.0;


/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an developer) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the developer account will be the one that deploys the contract. This
 * can later be changed with {transferDevelopership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyDeveloper`, which can be applied to your functions to restrict their use to
 * the developer.
 */
abstract contract DeveloperAccess is Context {
    address private _developer;

    event DevelopershipTransferred(address indexed previousDeveloper, address indexed newDeveloper);

    /**
     * @dev Initializes the contract setting the deployer as the initial developer.
     */
    constructor(address dev) {
        _setDeveloper(dev);
    }

    /**
     * @dev Returns the address of the current developer.
     */
    function developer() public view virtual returns (address) {
        return _developer;
    }

    /**
     * @dev Throws if called by any account other than the developer.
     */
    modifier onlyDeveloper() {
        require(developer() == _msgSender(), "Ownable: caller is not the developer");
        _;
    }

    /**
     * @dev Leaves the contract without developer. It will not be possible to call
     * `onlyDeveloper` functions anymore. Can only be called by the current developer.
     *
     * NOTE: Renouncing developership will leave the contract without an developer,
     * thereby removing any functionality that is only available to the developer.
     */
    function renounceDevelopership() public virtual onlyDeveloper {
        _setDeveloper(address(0));
    }

    /**
     * @dev Transfers developership of the contract to a new account (`newDeveloper`).
     * Can only be called by the current developer.
     */
    function transferDevelopership(address newDeveloper) public virtual onlyDeveloper {
        require(newDeveloper != address(0), "Ownable: new developer is the zero address");
        _setDeveloper(newDeveloper);
    }

    function _setDeveloper(address newDeveloper) private {
        address oldDeveloper = _developer;
        _developer = newDeveloper;
        emit DevelopershipTransferred(oldDeveloper, newDeveloper);
    }
}

// File: @openzeppelin/contracts/access/Ownable.sol


// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;


/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: contracts/GACStaking.sol



pragma solidity ^0.8.10;





/**
 * The staking contract designated to exist on the Ethereum chain,
 * briged to Polygon (MATIC) via FX-Portal.
 *
 * Author: Cory Cherven (Animalmix55/ToxicPizza)
 */
contract GACStaking is FxBaseRootTunnel, Ownable, DeveloperAccess {
    IERC721Metadata public gacToken;
    bool public stakingPaused;

    /**
     * Users' staked tokens mapped from their address
     */
    mapping(address => mapping(uint256 => bool)) public staked;

    constructor(
        address checkpointManager,
        address fxRoot,
        address devAddress,
        address tokenAddress
    ) FxBaseRootTunnel(checkpointManager, fxRoot) DeveloperAccess(devAddress) {
        gacToken = IERC721Metadata(tokenAddress);
    }

    // ----------------------------------------------- PUBLIC FUNCTIONS ----------------------------------------------

    /**
     * Stakes the given token ids, provided the contract is approved to move them.
     * @param tokenIds - the token ids to stake
     */
    function stake(uint256[] calldata tokenIds) external {
        require(!stakingPaused, "Staking paused");
        for (uint256 i; i < tokenIds.length; i++) {
            gacToken.transferFrom(msg.sender, address(this), tokenIds[i]);
            staked[msg.sender][tokenIds[i]] = true;
        }

        _informChildOfEvent(msg.sender, tokenIds.length, true);
    }

    /**
     * Unstakes the given token ids.
     * @param tokenIds - the token ids to unstake
     */
    function unstake(uint256[] calldata tokenIds) external {
        for (uint256 i; i < tokenIds.length; i++) {
            require(staked[msg.sender][tokenIds[i]], "Not owned");
            gacToken.transferFrom(address(this), msg.sender, tokenIds[i]);
            staked[msg.sender][tokenIds[i]] = false;
        }

        _informChildOfEvent(msg.sender, tokenIds.length, false);
    }

    // -------------------------------------------- ADMIN FUNCTIONS --------------------------------------------------

    /**
     * @dev Throws if called by any account other than the developer/owner.
     */
    modifier onlyOwnerOrDeveloper() {
        require(
            developer() == _msgSender() || owner() == _msgSender(),
            "Ownable: caller is not the owner or developer"
        );
        _;
    }

    /**
     * Updates the paused state of staking.
     * @param paused - the state's new value.
     */
    function setStakingPaused(bool paused) external onlyOwnerOrDeveloper {
        stakingPaused = paused;
    }

    /**
     * Allows permissioned setting of fxChildTunnel
     * @param _fxChildTunnel - the fxChildTunnel address
     */
    function setFxChildTunnel(address _fxChildTunnel) public override onlyOwnerOrDeveloper {
        fxChildTunnel = _fxChildTunnel;
    }

    // -------------------------------------------- INTERNAL FUNCTIONS ----------------------------------------------

    /**
     * Informs the child contract, via FX-Portal, that a staking event has occurred.
     * @param from - the user that staked/unstaked
     * @param count - the number staked/unstaked
     * @param isInbound - true if staking, false if unstaking
     */
    function _informChildOfEvent(
        address from,
        uint256 count,
        bool isInbound
    ) internal {
        _sendMessageToChild(abi.encode(from, count, isInbound));
    }

    /**
     * A stub that does nothing. We will not anticipate receiving messages from Polygon,
     * we will only send messages to Polygon via FX-Portal.
     */
    function _processMessageFromChild(bytes memory) internal override {}
}

Read Contract

SEND_MESSAGE_EVENT_SIG 0x0e387de6 → bytes32
checkpointManager 0xc0857ba0 → address
developer 0xca4b208b → address
fxChildTunnel 0x972c4928 → address
fxRoot 0xde9b771f → address
gacToken 0xbad96b54 → address
owner 0x8da5cb5b → address
processedExits 0x607f2d42 → bool
staked 0x8f169816 → bool
stakingPaused 0xbbb781cc → bool

Write Contract 9 functions

These functions modify contract state and require a wallet transaction to execute.

receiveMessage 0xf953cec7
bytes inputData
renounceDevelopership 0x8cc401d5
No parameters
renounceOwnership 0x715018a6
No parameters
setFxChildTunnel 0xaea4e49e
address _fxChildTunnel
setStakingPaused 0x15b31bbb
bool paused
stake 0x0fbf0a93
uint256[] tokenIds
transferDevelopership 0x9bbee240
address newDeveloper
transferOwnership 0xf2fde38b
address newOwner
unstake 0xe449f341
uint256[] tokenIds

Recent Transactions

No transactions found for this address