Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x5bd484dde8dB2f79782EdBc76F0C3c405D1Ff350
Balance 0 ETH
Nonce 1
Code Size 22504 bytes
Indexed Transactions 0 (1 on-chain, 0.7% indexed)
External Etherscan · Sourcify

Contract Bytecode

22504 bytes
0x6080604052600436106101d3575f3560e01c80638b2ab1b7116100fd578063bdacaae711610092578063d383e3e711610062578063d383e3e71461061d578063dae2fc1214610691578063f1d3ac4f146106b0578063f620399014610720575f5ffd5b8063bdacaae71461058d578063bedb86fb146105ac578063cc8388d0146105cb578063d0fb0203146105fe575f5ffd5b8063a5e1ea7d116100cd578063a5e1ea7d146104ef578063ac432e631461051b578063b365b1911461053a578063b854c12b14610559575f5ffd5b80638b2ab1b7146104375780639c3f1e901461044c5780639e70a740146104a5578063a3a30834146104d0575f5ffd5b8063452a9320116101735780634818e84d116101435780634818e84d146103985780635c975abb146103b75780636147435b146103d757806384acd1bb14610404575f5ffd5b8063452a932014610327578063459656ee14610346578063469635051461035a578063472d35b914610379575f5ffd5b806339f3fbd9116101ae57806339f3fbd91461023d5780633d474866146102725780633fc8cef31461029e578063423c5f22146102e9575f5ffd5b8063119abf67146101de5780632d67b5ea146101ff5780632fcb4f041461021e575f5ffd5b366101da57005b5f5ffd5b3480156101e9575f5ffd5b506101fd6101f83660046147ab565b6107a6565b005b34801561020a575f5ffd5b506101fd61021936600461488b565b6109a6565b348015610229575f5ffd5b506101fd61023836600461492a565b610b77565b348015610248575f5ffd5b5061025c6102573660046147ab565b610bd0565b6040516102699190614945565b60405180910390f35b34801561027d575f5ffd5b5061029161028c366004614a12565b610d35565b6040516102699190614a77565b3480156102a9575f5ffd5b506102d17f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6040516001600160a01b039091168152602001610269565b3480156102f4575f5ffd5b50610317610303366004614ae9565b60096020525f908152604090205460ff1681565b6040519015158152602001610269565b348015610332575f5ffd5b506004546102d1906001600160a01b031681565b348015610351575f5ffd5b506101fd610e66565b348015610365575f5ffd5b506101fd6103743660046147ab565b610ec1565b348015610384575f5ffd5b506101fd61039336600461492a565b611186565b3480156103a3575f5ffd5b506005546102d1906001600160a01b031681565b3480156103c2575f5ffd5b5060055461031790600160a01b900460ff1681565b3480156103e2575f5ffd5b506103f66103f1366004614c41565b6111df565b604051908152602001610269565b34801561040f575f5ffd5b506102d17f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b81565b348015610442575f5ffd5b506103f660025481565b348015610457575f5ffd5b50610496610466366004614ae9565b60076020525f908152604090205460ff81169061010081046001600160401b031690600160481b900461ffff1683565b60405161026993929190614d22565b3480156104b0575f5ffd5b506103f66104bf366004614d51565b60086020525f908152604090205481565b3480156104db575f5ffd5b506103f66104ea366004614d6c565b6117a4565b3480156104fa575f5ffd5b5061050e6105093660046147ab565b611d7c565b6040516102699190614dd4565b348015610526575f5ffd5b506101fd610535366004614e7d565b611edd565b348015610545575f5ffd5b506101fd610554366004614ecb565b61276e565b348015610564575f5ffd5b5060015461057a90600160a01b900461ffff1681565b60405161ffff9091168152602001610269565b348015610598575f5ffd5b506001546102d1906001600160a01b031681565b3480156105b7575f5ffd5b506101fd6105c6366004614f2e565b6128b6565b3480156105d6575f5ffd5b506102d17f00000000000000000000000071b6e467f549b367487a28ef07520147a90a5f3c81565b348015610609575f5ffd5b506003546102d1906001600160a01b031681565b348015610628575f5ffd5b5061063c6106373660046147ab565b612919565b60405161026991905f60a08201905060ff835116825261ffff60208401511660208301526001600160a01b03604084015116604083015261ffff60608401511660608301526080830151608083015292915050565b34801561069c575f5ffd5b506101fd6106ab3660046147ab565b612a04565b3480156106bb575f5ffd5b506106cf6106ca3660046147ab565b612d77565b60405161026991905f60a08201905060ff835116825260208301516020830152604083015160408301526001600160401b03606084015116606083015261ffff608084015116608083015292915050565b34801561072b575f5ffd5b5061073f61073a3660046147ab565b612e41565b60405161026991905f60c08201905060ff835116825261ffff60208401511660208301526040830151604083015260ff60608401511660608301526001600160a01b0360808401511660808301526001600160401b0360a08401511660a083015292915050565b6107ae612f57565b5f5f5f7f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b031663c0fd8bde856040518263ffffffff1660e01b81526004016107fd9190614f77565b5f60405180830381865afa158015610817573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261083e91908101906150bd565b92509250925081819061086d5760405162461bcd60e51b81526004016108649190614f77565b60405180910390fd5b505f61087c8460e00151611d7c565b6020808201515f9081526007909152604080822081516060810190925280549394509192909190829060ff1660058111156108b9576108b9614a43565b60058111156108ca576108ca614a43565b815290546001600160401b03610100820416602083015261ffff600160481b909104811660409283015290820151919250165f0361092357816020015160405163956fc85760e01b815260040161086491815260200190565b806040015161ffff16856060015161ffff1614610953576040516327e8d62960e11b815260040160405180910390fd5b60408082015161ffff165f9081526008602052205460808601511461098b5760405163d08bf9e160e01b815260040160405180910390fd5b6109958282612fae565b50505050506109a360015f55565b50565b6109ae612f57565b5f5f5f7f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b031663c0fd8bde876040518263ffffffff1660e01b81526004016109fd9190614f77565b5f60405180830381865afa158015610a17573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610a3e91908101906150bd565b925092509250818190610a645760405162461bcd60e51b81526004016108649190614f77565b5060e08301515f90610a76908261346e565b905060ff8116600514610a9c57604051634a7f394f60e01b815260040160405180910390fd5b60e08401515f90610aae9060016134d9565b8751909150610ac260ac61ffff8416615227565b14610af9576040517f5e7407c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8651602088012060e08601515f90610b1290600361353e565b9050808214610b4d576040517f7c6953f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610b62898489606001518a608001518c6135a3565b50505050505050610b7260015f55565b505050565b6004546001600160a01b03163314610ba1576040516282b42960e81b815260040160405180910390fd5b6005805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b60408051610100810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905290610c1a838261346e565b60ff168252610c2a60018261523e565b825190915060ff16600314610c5257604051634a7f394f60e01b815260040160405180910390fd5b610c5c838261353e565b602080840191909152610c6f908261523e565b9050610c7b83826134d9565b61ffff166040830152610c8f60028261523e565b9050610c9b838261353e565b6060830152610cab60208261523e565b9050610cb7838261353e565b6080830152610cc760208261523e565b9050610cd3838261353e565b60a0830152610ce360208261523e565b9050610cef83826138ca565b6001600160401b031660c0830152610d0860088261523e565b9050610d1483826138ca565b6001600160401b031660e0830152610d2d60088261523e565b905050919050565b60605f82516001600160401b03811115610d5157610d51614681565b604051908082528060200260200182016040528015610d9a57816020015b604080516060810182525f80825260208083018290529282015282525f19909201910181610d6f5790505b5090505f5b8351811015610e5f5760075f858381518110610dbd57610dbd615251565b602002602001015181526020019081526020015f206040518060600160405290815f82015f9054906101000a900460ff166005811115610dff57610dff614a43565b6005811115610e1057610e10614a43565b8152905461010081046001600160401b03166020830152600160481b900461ffff166040909101528251839083908110610e4c57610e4c615251565b6020908102919091010152600101610d9f565b5092915050565b6005546001600160a01b03163314610e90576040516282b42960e81b815260040160405180910390fd5b6005546004805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03909216919091179055565b6004546001600160a01b03163314610eeb576040516282b42960e81b815260040160405180910390fd5b5f5f5f7f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b031663c0fd8bde856040518263ffffffff1660e01b8152600401610f3a9190614f77565b5f60405180830381865afa158015610f54573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610f7b91908101906150bd565b925092509250818190610fa15760405162461bcd60e51b81526004016108649190614f77565b506101408301515f9081526009602052604090205460ff1615610fd757604051633891b8bb60e11b815260040160405180910390fd5b6101408301515f908152600960205260409020805460ff19166001908117909155606084015161ffff161461101f576040516327e8d62960e11b815260040160405180910390fd5b60015f5260086020527fad67d757c34507f157cacfa2e3153e9f260a2244f30428821be7be64587ac55f5460808401511461106d5760405163d08bf9e160e01b815260040160405180910390fd5b5f61107b8460e00151612919565b90507f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa1580156110d9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110fd9190615265565b61ffff16816020015161ffff161461112857604051633bec3b7d60e11b815260040160405180910390fd5b604081015160018054606084015161ffff16600160a01b027fffffffffffffffffffff000000000000000000000000000000000000000000009091166001600160a01b03909316929092179190911790556080015160025550505050565b6004546001600160a01b031633146111b0576040516282b42960e81b815260040160405180910390fd5b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b5f6111e8612f57565b600554600160a01b900460ff1615611213576040516313d0ff5960e31b815260040160405180910390fd5b5f611221886020015161392f565b905061122d868a61523e565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301523060248301528c169063dd62ed3e90604401602060405180830381865afa158015611290573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112b49190615280565b10156112c5576112c58a8285613983565b6112d08a8a83613a36565b9850851561135c576003546112f4906001600160a01b038c81169184911689613b32565b600354604051631c43245560e01b81523360048201526001600160a01b038c811660248301526044820189905290911690631c432455906064015f604051808303815f87803b158015611345575f5ffd5b505af1158015611357573d5f5f3e3d5ffd5b505050505b5f61136f8a61136a8d613bd1565b613c9d565b9050806001600160401b03165f0361139a576040516356fdd81f60e11b815260040160405180910390fd5b806001600160401b03168961012001518a61010001516113ba9190615297565b6001600160401b0316106113e15760405163192069c360e31b815260040160405180910390fd5b60a08901511580156113ff575060e08901516001600160401b031615155b1561141d57604051636e51edcd60e11b815260040160405180910390fd5b600354604051633972bf5560e11b81525f916001600160a01b0316906372e57eaa90611451908f908f908f906004016152b6565b6020604051808303815f875af115801561146d573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061149191906153c3565b905060c860ff168a610160015160ff1611806114b0575060c860ff8216115b156114ce57604051633f6e8d0d60e21b815260040160405180910390fd5b6115886115838b8e6001600160a01b03165f1b7f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa15801561153d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115619190615265565b858f5f015160ff16600214611576575f613cd4565b8e80519060200120613cd4565b613e63565b8051906020012093506115a961159f858d8b613f74565b8890889086614012565b60608a015161ffff165f90815260086020526040902054158061165657507f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa158015611623573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116479190615265565b61ffff168a6060015161ffff16145b15611674576040516355ef7a1d60e11b815260040160405180910390fd5b5f84815260076020526040902054600160481b900461ffff16156116ab5760405163015b9d8b60e41b815260040160405180910390fd5b6040805160608101909152805f81526001600160401b03841660208083019190915260608d015161ffff166040928301525f87815260079091522081518154829060ff1916600183600581111561170457611704614a43565b02179055506020820151815460409384015161ffff16600160481b026affff000000000000000000196001600160401b0390931661010002929092166affffffffffffffffffff001990911617179055517f918554b6bd6e2895ce6553de5de0e1a69db5289aa0e4fe193a0dcd1f14347477906117849086815260200190565b60405180910390a150505061179860015f55565b98975050505050505050565b5f6117ad612f57565b600554600160a01b900460ff16156117d8576040516313d0ff5960e31b815260040160405180910390fd5b846001600160a01b03811661180b577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc295505b611816868633613a36565b94505f6118268661136a89613bd1565b9050806001600160401b03165f03611851576040516356fdd81f60e11b815260040160405180910390fd5b806001600160401b03168561012001518661010001516118719190615297565b6001600160401b0316106118985760405163192069c360e31b815260040160405180910390fd5b60a08501511580156118b6575060e08501516001600160401b031615155b156118d457604051636e51edcd60e11b815260040160405180910390fd5b600354604051633972bf5560e11b81525f916001600160a01b0316906372e57eaa90611908908b908b908b906004016152b6565b6020604051808303815f875af1158015611924573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061194891906153c3565b905060c860ff1686610160015160ff161180611967575060c860ff8216115b1561198557604051633f6e8d0d60e21b815260040160405180910390fd5b85515f9060ff1660020361199c5750845160208601205b611a3661158388866001600160a01b03165f1b7f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a0b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a2f9190615265565b8686613cd4565b8051602091820120606089015161ffff165f90815260089092526040909120549095501580611aef57507f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa158015611abc573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ae09190615265565b61ffff16876060015161ffff16145b15611b0d576040516355ef7a1d60e11b815260040160405180910390fd5b5f858152600760205260408082208151606081019092528054829060ff166005811115611b3c57611b3c614a43565b6005811115611b4d57611b4d614a43565b815290546001600160401b03610100820416602083015261ffff600160481b9091048116604092830152908201519192501615611c855780602001516001600160401b0316846001600160401b0316118015611bba57505f81516005811115611bb857611bb8614a43565b145b15611c6c57896001600160a01b031663a9059cbb611bdb8a6020015161392f565b611bfa84602001516001600160401b0316611bf58f613bd1565b6142a3565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303815f875af1158015611c42573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c6691906153de565b50611c85565b60405163015b9d8b60e41b815260040160405180910390fd5b6040805160608101909152805f81526001600160401b03861660208083019190915260608b015161ffff166040928301525f89815260079091522081518154829060ff19166001836005811115611cde57611cde614a43565b02179055506020820151815460409384015161ffff16600160481b026affff000000000000000000196001600160401b0390931661010002929092166affffffffffffffffffff001990911617179055517f918554b6bd6e2895ce6553de5de0e1a69db5289aa0e4fe193a0dcd1f1434747790611d5e9088815260200190565b60405180910390a15050505050611d7460015f55565b949350505050565b60408051610140810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810182905290611dd6838261346e565b60ff168252611de660018261523e565b825190915060ff16600214611e0e57604051634a7f394f60e01b815260040160405180910390fd5b611e18838261353e565b602080840191909152611e2b908261523e565b9050611e3783826134d9565b61ffff166040830152611e4b60028261523e565b9050611e57838261353e565b6060830152611e6760208261523e565b9050611e73838261353e565b6080830152611e8360208261523e565b9050611e8f838261346e565b60ff1660a0830152611ea260018261523e565b9050611eae838261346e565b60ff1660c0830152611ec160018261523e565b9050611ecd838261353e565b60e0830152610d2d60208261523e565b611ee5612f57565b60408051610160810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820183905260e0820181905261010082018390526101208201526101408101919091525f6060838015611f5657506001546001600160a01b031615155b15611fd85760015460405163607ec5ef60e11b81526001600160a01b039091169063c0fd8bde90611f8b908890600401614f77565b5f60405180830381865afa158015611fa5573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611fcc91908101906150bd565b9194509250905061206d565b60405163607ec5ef60e11b81526001600160a01b037f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b169063c0fd8bde90612024908890600401614f77565b5f60405180830381865afa15801561203e573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261206591908101906150bd565b919450925090505b808261208c5760405162461bcd60e51b81526004016108649190614f77565b505f61209b8460e00151610bd0565b6020808201515f9081526007909152604080822081516060810190925280549394509192909190829060ff1660058111156120d8576120d8614a43565b60058111156120e9576120e9614a43565b8152905461010081046001600160401b0316602080840191909152600160481b90910461ffff1660409283015281517f9a8a059200000000000000000000000000000000000000000000000000000000815291519293507f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b031692639a8a05929260048082019392918290030181865afa158015612190573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121b49190615265565b61ffff16826040015161ffff16146121df57604051633bec3b7d60e11b815260040160405180910390fd5b806040015161ffff165f0361220f57816020015160405163956fc85760e01b815260040161086491815260200190565b5f8151600581111561222357612223614a43565b1461224157604051632916ae3360e01b815260040160405180910390fd5b6020828101515f908152600790915260409020805460ff1916600517905585156122c557600154606086015161ffff908116600160a01b909204161461229a576040516327e8d62960e11b815260040160405180910390fd5b6002548560800151146122c05760405163d08bf9e160e01b815260040160405180910390fd5b61232d565b806040015161ffff16856060015161ffff16146122f5576040516327e8d62960e11b815260040160405180910390fd5b60408082015161ffff165f9081526008602052205460808601511461232d5760405163d08bf9e160e01b815260040160405180910390fd5b5f61233b836080015161392f565b60a08401516060850151919250905f906123549061392f565b90505f6001600160a01b03821661236d57506012612379565b61237682613bd1565b90505b5f6123918760c001516001600160401b0316836142a3565b90505f6123ab8860e001516001600160401b0316846142a3565b90505f6123c588602001516001600160401b0316856142a3565b90505f826123d385846153f9565b6123dd91906153f9565b90506001600160a01b0386166125a257831561247957600354604051631c43245560e01b81526001600160a01b0389811660048301527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811660248301526044820187905290911690631c432455906064015f604051808303815f87803b158015612466575f5ffd5b505af1925050508015612477575060015b505b821561250357600354604051631c43245560e01b81523360048201526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2811660248301526044820186905290911690631c432455906064015f604051808303815f87803b1580156124f0575f5ffd5b505af1925050508015612501575060015b505b6040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031690632e1a7d4d906024015f604051808303815f87803b15801561257b575f5ffd5b505af115801561258d573d5f5f3e3d5ffd5b5050505061259d888260016142d0565b612716565b831561260e57600354604051631c43245560e01b81526001600160a01b03898116600483015288811660248301526044820187905290911690631c432455906064015f604051808303815f87803b1580156125fb575f5ffd5b505af192505050801561260c575060015b505b821561267857600354604051631c43245560e01b81523360048201526001600160a01b0388811660248301526044820186905290911690631c432455906064015f604051808303815f87803b158015612665575f5ffd5b505af1925050508015612676575060015b505b5f612683848661523e565b905080156127005760035460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018390529088169063a9059cbb906044016020604051808303815f875af19250505080156126f9575060408051601f3d908101601f191682019092526126f6918101906153de565b60015b1561270057505b6127146001600160a01b0388168a84614375565b505b6020808b0151604080519182529181018390527fbff5487f6422ba4acbcde6bd5e0ccb83124c240b9deb6a72e7b5eb8c7b71d6fc910160405180910390a15050505050505050505050505061276a60015f55565b5050565b6004546001600160a01b03163314612798576040516282b42960e81b815260040160405180910390fd5b80518251146127e95760405162461bcd60e51b815260206004820152601460248201527f696e76616c6964206172726179206c656e6774680000000000000000000000006044820152606401610864565b5f5b8251811015610b72575f5f1b60085f85848151811061280c5761280c615251565b602002602001015161ffff1661ffff1681526020019081526020015f205414612861576040517f9a23a9de00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81818151811061287357612873615251565b602002602001015160085f85848151811061289057612890615251565b60209081029190910181015161ffff1682528101919091526040015f20556001016127eb565b6004546001600160a01b031633146128e0576040516282b42960e81b815260040160405180910390fd5b60058054911515600160a01b027fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff909216919091179055565b6040805160a0810182525f8082526020820181905291810182905260608101829052608081018290529061294d838261346e565b60ff16825261295d60018261523e565b825190915060ff1660061461298557604051634a7f394f60e01b815260040160405180910390fd5b61298f83826134d9565b61ffff1660208301526129a360028261523e565b90506129af83826143a5565b6001600160a01b031660408301526129c860208261523e565b90506129d483826134d9565b61ffff1660608301526129e860028261523e565b90506129f4838261353e565b6080830152610d2d60208261523e565b6004546001600160a01b03163314612a2e576040516282b42960e81b815260040160405180910390fd5b5f5f5f7f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b031663c0fd8bde856040518263ffffffff1660e01b8152600401612a7d9190614f77565b5f60405180830381865afa158015612a97573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612abe91908101906150bd565b925092509250818190612ae45760405162461bcd60e51b81526004016108649190614f77565b506101408301515f9081526009602052604090205460ff1615612b1a57604051633891b8bb60e11b815260040160405180910390fd5b6101408301515f908152600960205260409020805460ff19166001908117909155606084015161ffff1614612b62576040516327e8d62960e11b815260040160405180910390fd5b60015f5260086020527fad67d757c34507f157cacfa2e3153e9f260a2244f30428821be7be64587ac55f54608084015114612bb05760405163d08bf9e160e01b815260040160405180910390fd5b5f612bbe8460e00151612e41565b90507f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c1c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612c409190615265565b61ffff16816020015161ffff1614612c6b57604051633bec3b7d60e11b815260040160405180910390fd5b604081015115612cbe57806060015160ff166005811115612c8e57612c8e614a43565b6040808301515f908152600760205220805460ff19166001836005811115612cb857612cb8614a43565b02179055505b60a08101516001600160401b031615612d705760808101516001600160a01b0316612d2157612d1c7f00000000000000000000000071b6e467f549b367487a28ef07520147a90a5f3c8260a001516001600160401b031660016142d0565b612d70565b612d707f00000000000000000000000071b6e467f549b367487a28ef07520147a90a5f3c8260a001516001600160401b031683608001516001600160a01b03166143759092919063ffffffff16565b5050505050565b6040805160a0810182525f80825260208201819052918101829052606081018290526080810182905290612dab838261346e565b60ff168252612dbb60018261523e565b825190915060ff16600114612de357604051634a7f394f60e01b815260040160405180910390fd5b612ded838261353e565b602080840191909152612e00908261523e565b9050612e0c838261353e565b6040830152612e1c60208261523e565b9050612e2883826138ca565b6001600160401b03166060830152610d2d60088261523e565b6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905290612e7c838261346e565b60ff168252612e8c60018261523e565b825190915060ff16600714612eb457604051634a7f394f60e01b815260040160405180910390fd5b612ebe83826134d9565b61ffff166020830152612ed260028261523e565b9050612ede838261353e565b6040830152612eee60208261523e565b9050612efa838261346e565b60ff166060830152612f0d60018261523e565b9050612f1983826143a5565b6001600160a01b03166080830152612f3260208261523e565b9050612f3e83826138ca565b6001600160401b031660a0830152610d2d60088261523e565b60025f5403612fa85760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610864565b60025f55565b7f00000000000000000000000098f3c9e6e3face36baad05fe09d375ef1464288b6001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa15801561300a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061302e9190615265565b61ffff16826040015161ffff161461305957604051633bec3b7d60e11b815260040160405180910390fd5b5f8151600581111561306d5761306d614a43565b1461308b57604051632916ae3360e01b815260040160405180910390fd5b6020828101515f908152600790915260408120805460ff1916600317905560e08301516130b79061392f565b90505f6130c7846060015161392f565b90505f6001600160a01b0382166130e0575060126130ec565b6130e982613bd1565b90505b5f6127108660a0015160ff168660200151613107919061540c565b6131119190615442565b90505f86608001515f1c90505f6127108860c0015160ff168860200151613138919061540c565b6131429190615442565b90505f60035f9054906101000a90046001600160a01b03166001600160a01b031663c415b95c6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156131b3575060408051601f3d908101601f191682019092526131b09181019061546f565b60015b156131bb5790505b5f82858a602001516131cd919061548a565b6131d7919061548a565b90506001600160a01b03871661320b577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc296505b5f6001600160401b0386161580159061322c57506001600160a01b03851615155b156132bf575f613245876001600160401b0316896142a3565b600354604051631c43245560e01b81526001600160a01b0389811660048301528c8116602483015260448201849052929350911690631c432455906064015f604051808303815f87803b15801561329a575f5ffd5b505af19250505080156132ab575060015b156132bd576132ba818361523e565b91505b505b5f846001600160401b03161180156132df57506001600160a01b03831615155b15613372575f6132f8856001600160401b0316896142a3565b600354604051631c43245560e01b81526001600160a01b0387811660048301528c8116602483015260448201849052929350911690631c432455906064015f604051808303815f87803b15801561334d575f5ffd5b505af192505050801561335e575060015b156133705761336d818361523e565b91505b505b80156133ed5760035460405163a9059cbb60e01b81526001600160a01b039182166004820152602481018390529089169063a9059cbb906044016020604051808303815f875af19250505080156133e6575060408051601f3d908101601f191682019092526133e3918101906153de565b60015b156133ed57505b6001600160401b038216156134245761342489613413846001600160401b03168a6142a3565b6001600160a01b038b169190614375565b7f4bdcff348c4d11383c487afb95f732f243d93fbfc478aa736a4981cf6a6409118b6020015160405161345991815260200190565b60405180910390a15050505050505050505050565b5f61347a82600161523e565b835110156134ca5760405162461bcd60e51b815260206004820152601360248201527f746f55696e74385f6f75744f66426f756e6473000000000000000000000000006044820152606401610864565b50818101600101515b92915050565b5f6134e582600261523e565b835110156135355760405162461bcd60e51b815260206004820152601460248201527f746f55696e7431365f6f75744f66426f756e64730000000000000000000000006044820152606401610864565b50016002015190565b5f61354a82602061523e565b8351101561359a5760405162461bcd60e51b815260206004820152601560248201527f746f427974657333325f6f75744f66426f756e647300000000000000000000006044820152606401610864565b50016020015190565b80515f03613639578361ffff166001600160401b038111156135c7576135c7614681565b6040519080825280602002602001820160405280156135f0578160200160208202803683370190505b5090505f5b8461ffff168161ffff1610156136375780828261ffff168151811061361c5761361c615251565b61ffff909216602092830291909101909101526001016135f5565b505b5f5b81518110156138c2575f82828151811061365757613657615251565b602002602001015190508561ffff168161ffff16106136a2576040517f782cacc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f6136b260ac61ffff8416615227565b90505f604051806101400160405280600260078111156136d4576136d4614a43565b60ff1681526020016136e68b8561353e565b81526020016137028460206136fb919061523e565b8c906134d9565b61ffff16815260200161372061371985602261523e565b8c9061353e565b815260200161373361371985604261523e565b815260200161374d61374685606261523e565b8c9061346e565b60ff16815260200161376361374685606361523e565b60ff16815260200161377961371985606461523e565b815260200161378c61371985608461523e565b81526020016137a661379f8560a461523e565b8c906138ca565b6001600160401b031690526020808201515f9081526007909152604080822081516060810190925280549394509192909190829060ff1660058111156137ee576137ee614a43565b60058111156137ff576137ff614a43565b8152905461010081046001600160401b03166020830152600160481b900461ffff1660409091015290505f8151600581111561383d5761383d614a43565b1461384b57505050506138ba565b806040015161ffff168861ffff1614613877576040516327e8d62960e11b815260040160405180910390fd5b60408082015161ffff165f9081526008602052205487146138ab5760405163d08bf9e160e01b815260040160405180910390fd5b6138b58282612fae565b505050505b60010161363b565b505050505050565b5f6138d682600861523e565b835110156139265760405162461bcd60e51b815260206004820152601460248201527f746f55696e7436345f6f75744f66426f756e64730000000000000000000000006044820152606401610864565b50016008015190565b5f73ffffffffffffffffffffffffffffffffffffffff1982161561397f576040517ff652ddbc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5090565b6001600160a01b03831663d505accf8330843560208601356139ab60608801604089016154a9565b6040516001600160e01b031960e088901b1681526001600160a01b0395861660048201529490931660248501526044840191909152606483015260ff166084820152606084013560a4820152608084013560c482015260e4015f604051808303815f87803b158015613a1b575f5ffd5b505af1158015613a2d573d5f5f3e3d5ffd5b50505050505050565b6040516370a0823160e01b81523060048201525f9081906001600160a01b038616906370a0823190602401602060405180830381865afa158015613a7c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613aa09190615280565b9050613ab76001600160a01b038616843087613b32565b6040516370a0823160e01b815230600482015281906001600160a01b038716906370a0823190602401602060405180830381865afa158015613afb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b1f9190615280565b613b2991906153f9565b95945050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052613bcb9085907f23b872dd00000000000000000000000000000000000000000000000000000000906084015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152614401565b50505050565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f313ce5670000000000000000000000000000000000000000000000000000000017905290515f9182916001600160a01b03851691613c41916154c4565b5f60405180830381855afa9150503d805f8114613c79576040519150601f19603f3d011682016040523d82523d5f602084013e613c7e565b606091505b5091505080806020019051810190613c9691906153c3565b9392505050565b5f60088260ff161115613ccd57613cb56008836154da565b613cc090600a6155d6565b613cca90846155e4565b92505b5090919050565b60408051610240810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e081018290526102008101829052610220810191909152604051806102400160405280875f015160ff168152602001876020015181526020018561ffff16815260200186815260200187604001518152602001876060015161ffff1681526020018760a0015181526020018760c001516001600160401b031681526020018760e001516001600160401b031681526020018761010001516001600160401b031681526020018761012001516001600160401b031681526020018761014001516001600160401b031681526020018760800151815260200187610160015160ff1681526020018460ff16815260200187610180015160ff168152602001876101a00151815260200183815250905095945050505050565b6060815f015182602001518360400151846060015185608001518660a001518760c001518860e001518961010001518a61012001518b61014001518c6101600151604051602001613ebf9c9b9a999897969594939291906155f7565b60408051601f19818403018152908290526101808401516101a08501516101c08601516101e08701516102008801516102208901519597506134d396613f5d969060200195865260f894851b7fff00000000000000000000000000000000000000000000000000000000000000908116602088015293851b841660218701529190931b90911660228401526023830191909152604382015260630190565b60408051601f1981840301815291905282906144e7565b604080517fc1e8426d70a8367e3d86a5f2e0bdec2344bd6a74f82bfdf50cdfdc08a36500f1602082015290810184905260608101839052608081018290525f90819060a0016040516020818303038152906040529050613b2960065482805190602001206040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b5f5f5f836001600160a01b03163b5f036141dc5760418690036140645761403b8688018861571d565b90935091508686604081811061405357614053615251565b919091013560f81c91506140ed9050565b60408690036140bb575f61407a8789018961571d565b9094507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116935090506140b360ff82901c601b61573d565b9150506140ed565b6040517f4be6321b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080515f8082526020820180845288905260ff841692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa15801561413e573d5f5f3e3d5ffd5b5050604051601f1901519150506001600160a01b03811661418b576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b846001600160a01b0316816001600160a01b0316146141d6576040517f815e1d6400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50613a2d565b604051630b135d3f60e11b81525f906001600160a01b03861690631626ba7e9061420e9089908c908c90600401615756565b602060405180830381865afa158015614229573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061424d919061578b565b90506001600160e01b03198116630b135d3f60e11b14614299576040517fb0669cbc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050505050565b5f60088260ff161115613ccd576142bb6008836154da565b6142c690600a6155d6565b613cca9084615227565b5f836001600160a01b0316836040515f6040518083038185875af1925050503d805f8114614319576040519150601f19603f3d011682016040523d82523d5f602084013e61431e565b606091505b505090508115613bcb5780613bcb5760405162461bcd60e51b815260206004820152600e60248201527f7061796d656e74206661696c65640000000000000000000000000000000000006044820152606401610864565b6040516001600160a01b038316602482015260448101829052610b7290849063a9059cbb60e01b90606401613b7f565b5f6143b182602061523e565b8351101561359a5760405162461bcd60e51b815260206004820152601560248201527f746f55696e743235365f6f75744f66426f756e647300000000000000000000006044820152606401610864565b5f614455826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166145649092919063ffffffff16565b905080515f148061447557508080602001905181019061447591906153de565b610b725760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610864565b6060806040519050835180825260208201818101602087015b81831015614518578051835260209283019201614500565b50855184518101855292509050808201602086015b8183101561454557805183526020928301920161452d565b508651929092011591909101601f01601f191660405250905092915050565b6060611d7484845f85855f5f866001600160a01b0316858760405161458991906154c4565b5f6040518083038185875af1925050503d805f81146145c3576040519150601f19603f3d011682016040523d82523d5f602084013e6145c8565b606091505b50915091506145d9878383876145e4565b979650505050505050565b606083156146525782515f0361464b576001600160a01b0385163b61464b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610864565b5081611d74565b611d7483838151156146675781518083602001fd5b8060405162461bcd60e51b81526004016108649190614f77565b634e487b7160e01b5f52604160045260245ffd5b6040516101c081016001600160401b03811182821017156146b8576146b8614681565b60405290565b604051608081016001600160401b03811182821017156146b8576146b8614681565b60405161016081016001600160401b03811182821017156146b8576146b8614681565b604051601f8201601f191681016001600160401b038111828210171561472b5761472b614681565b604052919050565b5f6001600160401b0382111561474b5761474b614681565b50601f01601f191660200190565b5f82601f830112614768575f5ffd5b813561477b61477682614733565b614703565b81815284602083860101111561478f575f5ffd5b816020850160208301375f918101602001919091529392505050565b5f602082840312156147bb575f5ffd5b81356001600160401b038111156147d0575f5ffd5b611d7484828501614759565b5f6001600160401b038211156147f4576147f4614681565b5060051b60200190565b61ffff811681146109a3575f5ffd5b8035614818816147fe565b919050565b5f82601f83011261482c575f5ffd5b813561483a614776826147dc565b8082825260208201915060208360051b86010192508583111561485b575f5ffd5b602085015b83811015614881578035614873816147fe565b835260209283019201614860565b5095945050505050565b5f5f5f6060848603121561489d575f5ffd5b83356001600160401b038111156148b2575f5ffd5b6148be86828701614759565b93505060208401356001600160401b038111156148d9575f5ffd5b6148e586828701614759565b92505060408401356001600160401b03811115614900575f5ffd5b61490c8682870161481d565b9150509250925092565b6001600160a01b03811681146109a3575f5ffd5b5f6020828403121561493a575f5ffd5b8135613c9681614916565b5f6101008201905060ff83511682526020830151602083015261ffff6040840151166040830152606083015160608301526080830151608083015260a083015160a08301526001600160401b0360c08401511660c083015260e0830151610e5f60e08401826001600160401b03169052565b5f82601f8301126149c6575f5ffd5b81356149d4614776826147dc565b8082825260208201915060208360051b8601019250858311156149f5575f5ffd5b602085015b838110156148815780358352602092830192016149fa565b5f60208284031215614a22575f5ffd5b81356001600160401b03811115614a37575f5ffd5b611d74848285016149b7565b634e487b7160e01b5f52602160045260245ffd5b60068110614a7357634e487b7160e01b5f52602160045260245ffd5b9052565b602080825282518282018190525f918401906040840190835b81811015614ade578351614aa5848251614a57565b6001600160401b03602082015116602085015261ffff604082015116604085015250606083019250602084019350600181019050614a90565b509095945050505050565b5f60208284031215614af9575f5ffd5b5035919050565b60ff811681146109a3575f5ffd5b803561481881614b00565b6001600160401b03811681146109a3575f5ffd5b803561481881614b19565b5f6101c08284031215614b49575f5ffd5b614b51614695565b9050614b5c82614b0e565b81526020828101359082015260408083013590820152614b7e6060830161480d565b60608201526080828101359082015260a08083013590820152614ba360c08301614b2d565b60c0820152614bb460e08301614b2d565b60e0820152614bc66101008301614b2d565b610100820152614bd96101208301614b2d565b610120820152614bec6101408301614b2d565b610140820152614bff6101608301614b0e565b610160820152614c126101808301614b0e565b6101808201526101a09182013591810191909152919050565b5f60a08284031215614c3b575f5ffd5b50919050565b5f5f5f5f5f5f5f5f610300898b031215614c59575f5ffd5b8835614c6481614916565b975060208901359650614c7a8a60408b01614b38565b95506102008901356001600160401b03811115614c95575f5ffd5b614ca18b828c01614759565b95505061022089013593506102408901356001600160401b03811115614cc5575f5ffd5b8901601f81018b13614cd5575f5ffd5b80356001600160401b03811115614cea575f5ffd5b8b6020828401011115614cfb575f5ffd5b60209190910193509150614d138a6102608b01614c2b565b90509295985092959890939650565b60608101614d308286614a57565b6001600160401b038416602083015261ffff83166040830152949350505050565b5f60208284031215614d61575f5ffd5b8135613c96816147fe565b5f5f5f5f6102208587031215614d80575f5ffd5b8435614d8b81614916565b935060208501359250614da18660408701614b38565b91506102008501356001600160401b03811115614dbc575f5ffd5b614dc887828801614759565b91505092959194509250565b815160ff1681526101408101602083015160208301526040830151614dff604084018261ffff169052565b50606083015160608301526080830151608083015260a0830151614e2860a084018260ff169052565b5060c0830151614e3d60c084018260ff169052565b5060e083015160e0830152610100830151610100830152610120830151610e5f6101208401826001600160401b03169052565b80151581146109a3575f5ffd5b5f5f60408385031215614e8e575f5ffd5b82356001600160401b03811115614ea3575f5ffd5b614eaf85828601614759565b9250506020830135614ec081614e70565b809150509250929050565b5f5f60408385031215614edc575f5ffd5b82356001600160401b03811115614ef1575f5ffd5b614efd8582860161481d565b92505060208301356001600160401b03811115614f18575f5ffd5b614f24858286016149b7565b9150509250929050565b5f60208284031215614f3e575f5ffd5b8135613c9681614e70565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f613c966020830184614f49565b805161481881614b00565b805163ffffffff81168114614818575f5ffd5b8051614818816147fe565b805161481881614b19565b5f82601f830112614fcc575f5ffd5b8151602083015f614fdf61477684614733565b9050828152858383011115614ff2575f5ffd5b8282602083015e5f92810160200192909252509392505050565b5f82601f83011261501b575f5ffd5b8151615029614776826147dc565b8082825260208201915060208360071b86010192508583111561504a575f5ffd5b602085015b838110156148815760808188031215615066575f5ffd5b61506e6146be565b8151815260208083015190820152604082015161508a81614b00565b6040820152606082015161509d81614b00565b6060820152835260209092019160800161504f565b805161481881614e70565b5f5f5f606084860312156150cf575f5ffd5b83516001600160401b038111156150e4575f5ffd5b840161016081870312156150f6575f5ffd5b6150fe6146e0565b61510782614f89565b815261511560208301614f94565b602082015261512660408301614f94565b604082015261513760608301614fa7565b60608201526080828101519082015261515260a08301614fb2565b60a082015261516360c08301614f89565b60c082015260e08201516001600160401b03811115615180575f5ffd5b61518c88828501614fbd565b60e08301525061519f6101008301614f94565b6101008201526101208201516001600160401b038111156151be575f5ffd5b6151ca8882850161500c565b61012083015250610140918201519181019190915292506151ed602085016150b2565b915060408401516001600160401b03811115615207575f5ffd5b61490c86828701614fbd565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176134d3576134d3615213565b808201808211156134d3576134d3615213565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215615275575f5ffd5b8151613c96816147fe565b5f60208284031215615290575f5ffd5b5051919050565b6001600160401b0381811683821601908111156134d3576134d3615213565b6001600160a01b038416815260208101839052815160ff16604082015261020081016020830151606083015260408301516080830152606083015161530160a084018261ffff169052565b50608083015160c083015260a083015160e083015260c08301516153316101008401826001600160401b03169052565b5060e08301516001600160401b038116610120840152506101008301516001600160401b038116610140840152506101208301516001600160401b038116610160840152506101408301516001600160401b0381166101808401525061016083015160ff81166101a08401525061018083015160ff81166101c0840152506101a08301516101e0830152949350505050565b5f602082840312156153d3575f5ffd5b8151613c9681614b00565b5f602082840312156153ee575f5ffd5b8151613c9681614e70565b818103818111156134d3576134d3615213565b6001600160401b038181168382160290811690818114610e5f57610e5f615213565b634e487b7160e01b5f52601260045260245ffd5b5f6001600160401b0383168061545a5761545a61542e565b806001600160401b0384160491505092915050565b5f6020828403121561547f575f5ffd5b8151613c9681614916565b6001600160401b0382811682821603908111156134d3576134d3615213565b5f602082840312156154b9575f5ffd5b8135613c9681614b00565b5f82518060208501845e5f920191825250919050565b60ff82811682821603908111156134d3576134d3615213565b6001815b600184111561552e5780850481111561551257615512615213565b600184161561552057908102905b60019390931c9280026154f7565b935093915050565b5f82615544575060016134d3565b8161555057505f6134d3565b816001811461556657600281146155705761558c565b60019150506134d3565b60ff84111561558157615581615213565b50506001821b6134d3565b5060208310610133831016604e8410600b84101617156155af575081810a6134d3565b6155bb5f1984846154f3565b805f19048211156155ce576155ce615213565b029392505050565b5f613c9660ff841683615536565b5f826155f2576155f261542e565b500490565b7fff000000000000000000000000000000000000000000000000000000000000008d60f81b1681528b60018201527fffff0000000000000000000000000000000000000000000000000000000000008b60f01b1660218201528960238201528860438201527fffff0000000000000000000000000000000000000000000000000000000000008860f01b1660638201528660658201526156a6608582018760c01b6001600160c01b0319169052565b6156bf608d82018660c01b6001600160c01b0319169052565b6156d8609582018560c01b6001600160c01b0319169052565b6156f1609d82018460c01b6001600160c01b0319169052565b61570a60a582018360c01b6001600160c01b0319169052565b60ad019c9b505050505050505050505050565b5f5f6040838503121561572e575f5ffd5b50508035926020909101359150565b60ff81811683821601908111156134d3576134d3615213565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f6020828403121561579b575f5ffd5b81516001600160e01b031981168114613c96575f5ffdfea264697066735822122068669baef832339b63e9cb73a2816f190c90adcf1c4f6a896fb7f052165f558864736f6c634300081c0033

Verified Source Code Full Match

Compiler: v0.8.28+commit.7893614a EVM: prague Optimization: Yes (1000 runs)
SwiftSource.sol 795 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.28;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "../interfaces/IWormhole.sol";
import "../interfaces/IFeeManager.sol";
import "../interfaces/IWETH.sol";
import "../libs/BytesLib.sol";
import "../libs/SignatureVerifier.sol";
import "./SwiftStructs.sol";
import "./SwiftErrors.sol";

contract SwiftSource is ReentrancyGuard {
	event OrderCreated(bytes32 key);
	event OrderFulfilled(bytes32 key, uint64 sequence, uint256 netAmount);
	event OrderUnlocked(bytes32 key);
	event OrderCanceled(bytes32 key, uint64 sequence);
	event OrderRefunded(bytes32 key, uint256 netAmount);

	using SafeERC20 for IERC20;
	using BytesLib for bytes;
	using SignatureVerifier for bytes;

	uint8 constant BPS_FEE_LIMIT = 200;
	uint8 constant NATIVE_DECIMALS = 18;

	IWormhole public immutable wormhole;
	IWormhole public refundVerifier;
	uint16 public refundEmitterChainId;
	bytes32 public refundEmitterAddr;
	IFeeManager public feeManager;
	address public immutable rescueVault;
	IWETH public immutable weth;
	address public guardian;
	address public nextGuardian;
	bool public paused;

	bytes32 private domainSeparator;

	mapping(bytes32 => Order) public orders;
	mapping(uint16 => bytes32) public emitters;
	mapping(bytes32 => bool) public usedVms;

	constructor(
		address _wormhole,
		address _refundVerifier,
		uint16 _refundEmitterChainId,
		bytes32 _refundEmitterAddr,
		address _feeManager,
		address _rescueVault,
		address _weth
	) {
		guardian = msg.sender;
		wormhole = IWormhole(_wormhole);
		refundVerifier = IWormhole(_refundVerifier);
		refundEmitterChainId = _refundEmitterChainId;
		refundEmitterAddr = _refundEmitterAddr;
		feeManager = IFeeManager(_feeManager);
		rescueVault = _rescueVault;
		weth = IWETH(_weth);

		domainSeparator = keccak256(abi.encode(
			keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"),
			keccak256("Mayan Swift"),
			uint256(block.chainid),
			address(this)
		));
	}

	function createOrderWithToken(
		address tokenIn,
		uint256 amountIn,
		OrderParams memory params,
		bytes memory customPayload
	) nonReentrant external returns (bytes32 orderHash) {
		if (paused) {
			revert Paused();
		}

		address inputToken = tokenIn;
		if (tokenIn == address(0)) {
			tokenIn = address(weth);
		}
		amountIn = pullTokensFrom(tokenIn, amountIn, msg.sender);
		uint64 normlizedAmountIn = uint64(normalizeAmount(amountIn, decimalsOf(tokenIn)));
		if (normlizedAmountIn == 0) {
			revert SmallAmountIn();
		}
		if (params.cancelFee + params.refundFee >= normlizedAmountIn) {
			revert FeesTooHigh();
		}
		if (params.tokenOut == bytes32(0) && params.gasDrop != 0) {
			revert InvalidGasDrop();
		}

		uint8 protocolBps = feeManager.calcSwiftProtocolBps(tokenIn, amountIn, params);
		if (params.referrerBps > BPS_FEE_LIMIT || protocolBps > BPS_FEE_LIMIT) {
			revert InvalidBpsFee();
		}

		bytes32 customPayloadHash;
		if (params.payloadType == 2) {
			customPayloadHash = keccak256(customPayload);
		}

		orderHash = keccak256(encodeKey(buildKey(params, bytes32(uint256(uint160(inputToken))), wormhole.chainId(), protocolBps, customPayloadHash)));

		if (emitters[params.destChainId] == 0 || params.destChainId == wormhole.chainId()) {
			revert InvalidDestChain();
		}

		Order memory order = orders[orderHash];
		if (order.destChainId != 0) {
			if (normlizedAmountIn > order.amountIn && order.status == Status.CREATED) {
				IERC20(tokenIn).transfer(truncateAddress(params.trader), deNormalizeAmount(order.amountIn, decimalsOf(tokenIn)));
			} else {
				revert DuplicateOrder();
			}
		}

		orders[orderHash] = Order({
			status: Status.CREATED,
			amountIn: normlizedAmountIn,
			destChainId: params.destChainId
		});

		emit OrderCreated(orderHash);
	}

	function createOrderWithSig(
		address tokenIn,
		uint256 amountIn,
		OrderParams memory params,
		bytes memory customPayload,
		uint256 submissionFee,
		bytes calldata signedOrderHash,
		PermitParams calldata permitParams
	) nonReentrant external returns (bytes32 orderHash) {
		if (paused) {
			revert Paused();
		}

		address trader = truncateAddress(params.trader);
		if (IERC20(tokenIn).allowance(trader, address(this)) < amountIn + submissionFee) {
			execPermit(tokenIn, trader, permitParams);
		}
		amountIn = pullTokensFrom(tokenIn, amountIn, trader);
		if (submissionFee > 0) {
			IERC20(tokenIn).safeTransferFrom(trader, address(feeManager), submissionFee);
			feeManager.depositFee(msg.sender, tokenIn, submissionFee);
		}

		uint64 normlizedAmountIn = uint64(normalizeAmount(amountIn, decimalsOf(tokenIn)));
		if (normlizedAmountIn == 0) {
			revert SmallAmountIn();
		}

		if (params.cancelFee + params.refundFee >= normlizedAmountIn) {
			revert FeesTooHigh();
		}
		if (params.tokenOut == bytes32(0) && params.gasDrop != 0) {
			revert InvalidGasDrop();
		}

		uint8 protocolBps = feeManager.calcSwiftProtocolBps(tokenIn, amountIn, params);
		if (params.referrerBps > BPS_FEE_LIMIT || protocolBps > BPS_FEE_LIMIT) {
			revert InvalidBpsFee();
		}

		orderHash = keccak256(encodeKey(buildKey(
			params,
			bytes32(uint256(uint160(tokenIn))),
			wormhole.chainId(),
			protocolBps,
			params.payloadType == 2 ? keccak256(customPayload) : bytes32(0)
		)));

		signedOrderHash.verify(hashTypedData(orderHash, amountIn, submissionFee), trader);

		if (emitters[params.destChainId] == 0 || params.destChainId == wormhole.chainId()) {
			revert InvalidDestChain();
		}
		if (orders[orderHash].destChainId != 0) {
			revert DuplicateOrder();
		}

		orders[orderHash] = Order({
			status: Status.CREATED,
			amountIn: normlizedAmountIn,
			destChainId: params.destChainId
		});

		emit OrderCreated(orderHash);
	}

	function unlockOrder(UnlockMsg memory unlockMsg, Order memory order) internal {
		if (unlockMsg.srcChainId != wormhole.chainId()) {
			revert InvalidSrcChain();
		}
		if (order.status != Status.CREATED) {
			revert InvalidOrderStatus();
		}
		orders[unlockMsg.orderHash].status = Status.UNLOCKED;
		
		address receiver = truncateAddress(unlockMsg.unlockReceiver);
		address tokenIn = truncateAddress(unlockMsg.tokenIn);
		uint8 decimals;
		if (tokenIn == address(0)) {
			decimals = NATIVE_DECIMALS;
		} else {
			decimals = decimalsOf(tokenIn);
		}

		uint64 normalizedReferrerFee = order.amountIn * unlockMsg.referrerBps / 10000;
		address referrerAddress = address(uint160(uint256(unlockMsg.referrerAddr)));

		uint64 normalizedProtocolFee = order.amountIn * unlockMsg.protocolBps / 10000;

		address feeCollector;
		try feeManager.feeCollector() returns (address _feeCollector) {
			feeCollector = _feeCollector;
		} catch {}

		uint64 netAmount = order.amountIn - normalizedReferrerFee - normalizedProtocolFee;

		if (tokenIn == address(0)) {
			tokenIn = address(weth);
		}
		uint256 totalFee = 0;
		if (normalizedReferrerFee > 0 && referrerAddress != address(0)) {
			uint256 referrerFee = deNormalizeAmount(normalizedReferrerFee, decimals);
			try feeManager.depositFee(referrerAddress, tokenIn, referrerFee) {totalFee += referrerFee;} catch {}
		}
		if (normalizedProtocolFee > 0 && feeCollector != address(0)) {
			uint256 protocolFee = deNormalizeAmount(normalizedProtocolFee, decimals);
			try feeManager.depositFee(feeCollector, tokenIn, protocolFee) {totalFee += protocolFee;} catch {}
		}
		if (totalFee > 0) {
			try IERC20(tokenIn).transfer(address(feeManager), totalFee) {} catch {}
		}
		if (netAmount > 0) {
			IERC20(tokenIn).safeTransfer(receiver, deNormalizeAmount(netAmount, decimals));
		}
		
		emit OrderUnlocked(unlockMsg.orderHash);
	}

	function refundOrder(bytes memory encodedVm, bool fast) nonReentrant() public {
		IWormhole.VM memory vm;
		bool valid;
		string memory reason;

		if (fast && address(refundVerifier) != address(0)) {
			(vm, valid,reason) = refundVerifier.parseAndVerifyVM(encodedVm);
		} else {
			(vm, valid,reason) = wormhole.parseAndVerifyVM(encodedVm);
		}

		require(valid, reason);

		RefundMsg memory refundMsg = parseRefundPayload(vm.payload);
		Order memory order = orders[refundMsg.orderHash];

		if (refundMsg.srcChainId != wormhole.chainId()) {
			revert InvalidSrcChain();
		}
		if (order.destChainId == 0) {
			revert OrderNotExists(refundMsg.orderHash);
		}
		if (order.status != Status.CREATED) {
			revert InvalidOrderStatus();
		}
		orders[refundMsg.orderHash].status = Status.REFUNDED;

		if (fast) {
			if (vm.emitterChainId != refundEmitterChainId) {
				revert InvalidEmitterChain();
			}
			if (vm.emitterAddress != refundEmitterAddr) {
				revert InvalidEmitterAddress();
			}
		} else {
			if (vm.emitterChainId != order.destChainId) {
				revert InvalidEmitterChain();
			}
			if (vm.emitterAddress != emitters[order.destChainId]) {
				revert InvalidEmitterAddress();
			}
		}

		address trader = truncateAddress(refundMsg.trader);
		// no error if canceler is invalid
		address canceler = address(uint160(uint256(refundMsg.canceler)));
		address tokenIn = truncateAddress(refundMsg.tokenIn);
		
		uint8 decimals;
		if (tokenIn == address(0)) {
			decimals = NATIVE_DECIMALS;
		} else {
			decimals = decimalsOf(tokenIn);
		}

		uint256 cancelFee = deNormalizeAmount(refundMsg.cancelFee, decimals);
		uint256 refundFee = deNormalizeAmount(refundMsg.refundFee, decimals);
		uint256 amountIn = deNormalizeAmount(order.amountIn, decimals);

		uint256 netAmount = amountIn - cancelFee - refundFee;
		if (tokenIn == address(0)) {
			if (cancelFee > 0) {
				try feeManager.depositFee(canceler, address(weth), cancelFee) {} catch {}
			}
			if (refundFee > 0) {
				try feeManager.depositFee(msg.sender, address(weth), refundFee) {} catch {}
			}
			weth.withdraw(netAmount);
			payEth(trader, netAmount, true);
		} else {
			if (cancelFee > 0) {
				try feeManager.depositFee(canceler, tokenIn, cancelFee) {} catch {}
			}
			if (refundFee > 0) {
				try feeManager.depositFee(msg.sender, tokenIn, refundFee) {} catch {}
			}
			uint256 totalFee = cancelFee + refundFee;
			if (totalFee > 0) {
				try IERC20(tokenIn).transfer(address(feeManager), totalFee) {} catch {}
			}
			IERC20(tokenIn).safeTransfer(trader, netAmount);
		}

		emit OrderRefunded(refundMsg.orderHash, netAmount);
	}

	function unlockSingle(bytes memory encodedVm) nonReentrant public {
		(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);

		require(valid, reason);

		UnlockMsg memory unlockMsg = parseUnlockPayload(vm.payload);
		Order memory order = orders[unlockMsg.orderHash];

		if (order.destChainId == 0) {
			revert OrderNotExists(unlockMsg.orderHash);
		}
		if (vm.emitterChainId != order.destChainId) {
			revert InvalidEmitterChain();
		}
		if (vm.emitterAddress != emitters[order.destChainId]) {
			revert InvalidEmitterAddress();
		}

		unlockOrder(unlockMsg, order);
	}
	
	function unlockCompressedBatch(bytes memory encodedVm, bytes memory encodedPayload, uint16[] memory indexes) nonReentrant public {
		(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedVm);

		require(valid, reason);

		uint8 action = vm.payload.toUint8(0);
		if (action != uint8(Action.COMPRESSED_UNLOCK)) {
			revert InvalidAction();
		}

		uint16 count = vm.payload.toUint16(1);
		if (count * UNLOCK_MSG_SIZE != encodedPayload.length) {
			revert InvalidPayloadLength();
		}

		bytes32 computedHash = keccak256(encodedPayload);
		bytes32 msgHash = vm.payload.toBytes32(3);
		if (computedHash != msgHash) {
			revert InvalidPayload();
		}

		processUnlocks(encodedPayload, count, vm.emitterChainId, vm.emitterAddress, indexes);
	}

	function rescue(bytes memory encodedVm) public {
		if (msg.sender != guardian) {
			revert Unauthorized();
		}
		(IWormhole.VM memory vm, bool valid, string memory reason) = IWormhole(wormhole).parseAndVerifyVM(encodedVm);
		require(valid, reason);

		if (usedVms[vm.hash]) {
			revert SequenceAlreadyUsed();
		}
		usedVms[vm.hash] = true;

		if (vm.emitterChainId != 1) {
			revert InvalidEmitterChain();
		}
		if (vm.emitterAddress != emitters[1]) {
			revert InvalidEmitterAddress();
		}

		RescueMsg memory rescueMsg = parseRescuePayload(vm.payload);
		if (rescueMsg.chainId != wormhole.chainId()) {
			revert InvalidSrcChain();
		}
		if (rescueMsg.orderHash != bytes32(0)) {
			orders[rescueMsg.orderHash].status = Status(rescueMsg.orderStatus);
		}
		if (rescueMsg.amount > 0) {
			if (rescueMsg.token == address(0)) {
				payEth(rescueVault, rescueMsg.amount, true);
			} else {
				IERC20(rescueMsg.token).safeTransfer(rescueVault, rescueMsg.amount);
			}
		}
	}

	function setRefundVerifier(bytes memory encodedVm) public {
		if (msg.sender != guardian) {
			revert Unauthorized();
		}
		(IWormhole.VM memory vm, bool valid, string memory reason) = IWormhole(wormhole).parseAndVerifyVM(encodedVm);
		require(valid, reason);

		if (usedVms[vm.hash]) {
			revert SequenceAlreadyUsed();
		}
		usedVms[vm.hash] = true;

		if (vm.emitterChainId != 1) {
			revert InvalidEmitterChain();
		}
		if (vm.emitterAddress != emitters[1]) {
			revert InvalidEmitterAddress();
		}

		RefundVerifier memory payload = parseRefundVerifierPayload(vm.payload);
		if (payload.chainId != wormhole.chainId()) {
			revert InvalidSrcChain();
		}
		refundVerifier = IWormhole(payload.verifier);
		refundEmitterChainId = payload.emitterChainId;
		refundEmitterAddr = payload.emitterAddr;
	}

	function processUnlocks(bytes memory payload, uint16 count, uint16 emitterChainId, bytes32 emitterAddress, uint16[] memory indexes) internal {
		// If indexes array is empty, create a default array to iterate over all indices
		if (indexes.length == 0) {
			indexes = new uint16[](count);
			for (uint16 i = 0; i < count; i++) {
				indexes[i] = i;
			}
		}

		for (uint i = 0; i < indexes.length; i++) {
			uint16 index = indexes[i];
			if (index >= count) {
				revert InvalidBatchIndex();
			}

			uint currentOffset = index * UNLOCK_MSG_SIZE;

			UnlockMsg memory unlockMsg = UnlockMsg({
				action: uint8(Action.UNLOCK),
				orderHash: payload.toBytes32(currentOffset),
				srcChainId: payload.toUint16(currentOffset + 32),
				tokenIn: payload.toBytes32(currentOffset + 34),
				referrerAddr: payload.toBytes32(currentOffset + 66),
				referrerBps: payload.toUint8(currentOffset + 98),
				protocolBps: payload.toUint8(currentOffset + 99),
				unlockReceiver: payload.toBytes32(currentOffset + 100),
				driver: payload.toBytes32(currentOffset + 132),
				fulfillTime: payload.toUint64(currentOffset + 164)
			});

			Order memory order = orders[unlockMsg.orderHash];
			if (order.status != Status.CREATED) {
				continue;
			}
			if (emitterChainId != order.destChainId) {
				revert InvalidEmitterChain();
			}
			if (emitterAddress != emitters[order.destChainId]) {
				revert InvalidEmitterAddress();
			}

			unlockOrder(unlockMsg, order);
		}
	}

	function buildKey(
		OrderParams memory params, 
		bytes32 tokenIn,
		uint16 srcChainId,
		uint8 protocolBps,
		bytes32 customPayloadHash
	) internal pure returns (Key memory) {
		return Key({
			payloadType: params.payloadType,
			trader: params.trader,
			srcChainId: srcChainId,
			tokenIn: tokenIn,
			destAddr: params.destAddr,
			destChainId: params.destChainId,		
			tokenOut: params.tokenOut,
			minAmountOut: params.minAmountOut,
			gasDrop: params.gasDrop,
			cancelFee: params.cancelFee,
			refundFee: params.refundFee,
			deadline: params.deadline,
			referrerAddr: params.referrerAddr,	
			referrerBps: params.referrerBps,
			protocolBps: protocolBps,
			auctionMode: params.auctionMode,
			random: params.random,
			customPayloadHash: customPayloadHash
		});
	}

	function parseFulfillPayload(bytes memory encoded) public pure returns (FulfillMsg memory fulfillMsg) {
		uint index = 0;

		fulfillMsg.action = encoded.toUint8(index);
		index += 1;

		if (fulfillMsg.action != uint8(Action.FULFILL)) {
			revert InvalidAction();
		}

		fulfillMsg.orderHash = encoded.toBytes32(index);
		index += 32;

		fulfillMsg.driver = encoded.toBytes32(index);
		index += 32;

		fulfillMsg.promisedAmount = encoded.toUint64(index);
		index += 8;
	}

	function parseUnlockPayload(bytes memory encoded) public pure returns (UnlockMsg memory unlockMsg) {
		uint index = 0;

		unlockMsg.action = encoded.toUint8(index);
		index += 1;

		if (unlockMsg.action != uint8(Action.UNLOCK)) {
			revert InvalidAction();
		}

		unlockMsg.orderHash = encoded.toBytes32(index);
		index += 32;

		unlockMsg.srcChainId = encoded.toUint16(index);
		index += 2;

		unlockMsg.tokenIn = encoded.toBytes32(index);
		index += 32;

		unlockMsg.referrerAddr = encoded.toBytes32(index);
		index += 32;

		unlockMsg.referrerBps = encoded.toUint8(index);
		index += 1;

		unlockMsg.protocolBps = encoded.toUint8(index);
		index += 1;

		unlockMsg.unlockReceiver = encoded.toBytes32(index);
		index += 32;
	}

	function parseRefundPayload(bytes memory encoded) public pure returns (RefundMsg memory refundMsg) {
		uint index = 0;

		refundMsg.action = encoded.toUint8(index);
		index += 1;

		if (refundMsg.action != uint8(Action.REFUND)) {
			revert InvalidAction();
		}

		refundMsg.orderHash = encoded.toBytes32(index);
		index += 32;

		refundMsg.srcChainId = encoded.toUint16(index);
		index += 2;

		refundMsg.tokenIn = encoded.toBytes32(index);
		index += 32;

		refundMsg.trader = encoded.toBytes32(index);
		index += 32;

		refundMsg.canceler = encoded.toBytes32(index);
		index += 32;

		refundMsg.cancelFee = encoded.toUint64(index);
		index += 8;

		refundMsg.refundFee = encoded.toUint64(index);
		index += 8;
	}

	function parseRescuePayload(bytes memory encoded) public pure returns (RescueMsg memory rescueMsg) {
		uint index = 0;

		rescueMsg.action = encoded.toUint8(index);
		index += 1;
		if (rescueMsg.action != uint8(Action.RESCUE)) {
			revert InvalidAction();
		}

		rescueMsg.chainId = encoded.toUint16(index);
		index += 2;

		rescueMsg.orderHash = encoded.toBytes32(index);
		index += 32;

		rescueMsg.orderStatus = encoded.toUint8(index);
		index += 1;

		rescueMsg.token = address(uint160(encoded.toUint256(index)));
		index += 32;

		rescueMsg.amount = encoded.toUint64(index);
		index += 8;
	}

	function parseRefundVerifierPayload(bytes memory encoded) public pure returns (RefundVerifier memory verifier) {
		uint index = 0;

		verifier.action = encoded.toUint8(index);
		index += 1;
		if (verifier.action != uint8(Action.SET_REFUND_VERIFIER)) {
			revert InvalidAction();
		}

		verifier.chainId = encoded.toUint16(index);
		index += 2;

		verifier.verifier = address(uint160(encoded.toUint256(index)));
		index += 32;

		verifier.emitterChainId = encoded.toUint16(index);
		index += 2;

		verifier.emitterAddr = encoded.toBytes32(index);
		index += 32;
	}

	function encodeKey(Key memory key) internal pure returns (bytes memory encoded) {
		encoded = abi.encodePacked(
			key.payloadType,
			key.trader,
			key.srcChainId,
			key.tokenIn,
			key.destAddr,
			key.destChainId,
			key.tokenOut,
			key.minAmountOut,
			key.gasDrop,
			key.cancelFee,
			key.refundFee,
			key.deadline
		);
		encoded = encoded.concat(abi.encodePacked(
			key.referrerAddr,
			key.referrerBps,
			key.protocolBps,
			key.auctionMode,
			key.random,
			key.customPayloadHash
		));
	}

	function payEth(address to, uint256 amount, bool revertOnFailure) internal {
		(bool success, ) = payable(to).call{value: amount}('');
		if (revertOnFailure) {
			require(success, 'payment failed');
		}
	}

	function truncateAddress(bytes32 b) internal pure returns (address) {
		if (bytes12(b) != 0) {
			revert InvalidEvmAddr();
		}
		return address(uint160(uint256(b)));
	}

	function decimalsOf(address token) internal view returns(uint8) {
		(,bytes memory queriedDecimals) = token.staticcall(abi.encodeWithSignature('decimals()'));
		return abi.decode(queriedDecimals, (uint8));
	}

	function normalizeAmount(uint256 amount, uint8 decimals) internal pure returns(uint256) {
		if (decimals > 8) {
			amount /= 10 ** (decimals - 8);
		}
		return amount;
	}

	function deNormalizeAmount(uint256 amount, uint8 decimals) internal pure returns(uint256) {
		if (decimals > 8) {
			amount *= 10 ** (decimals - 8);
		}
		return amount;
	}

	function hashTypedData(bytes32 orderHash, uint256 amountIn, uint256 submissionFee) internal view returns (bytes32) {
		bytes memory encoded = abi.encode(keccak256("CreateOrder(bytes32 OrderId,uint256 InputAmount,uint256 SubmissionFee)"), orderHash, amountIn, submissionFee);
		return toTypedDataHash(domainSeparator, keccak256(encoded));
	}

	function toTypedDataHash(bytes32 _domainSeparator, bytes32 _structHash) internal pure returns (bytes32 digest) {
		assembly {
			let ptr := mload(0x40)
			mstore(ptr, "\x19\x01")
			mstore(add(ptr, 0x02), _domainSeparator)
			mstore(add(ptr, 0x22), _structHash)
			digest := keccak256(ptr, 0x42)
		}
	}

	function pullTokensFrom(address tokenIn, uint256 amount, address from) internal returns (uint256) {
		uint256 balance = IERC20(tokenIn).balanceOf(address(this));
		IERC20(tokenIn).safeTransferFrom(from, address(this), amount);
		return IERC20(tokenIn).balanceOf(address(this)) - balance;
	}

	function execPermit(
		address token,
		address owner,
		PermitParams calldata permitParams
	) internal {
		IERC20Permit(token).permit(
			owner,
			address(this),
			permitParams.value,
			permitParams.deadline,
			permitParams.v,
			permitParams.r,
			permitParams.s
		);
	}

	function setPause(bool _pause) public {
		if (msg.sender != guardian) {
			revert Unauthorized();
		}
		paused = _pause;
	}

	function setFeeManager(address _feeManager) public {
		if (msg.sender != guardian) {
			revert Unauthorized();
		}
		feeManager = IFeeManager(_feeManager);
	}

	function setEmitters(uint16[] memory chainIds, bytes32[] memory addresses) public {
		if (msg.sender != guardian) {
			revert Unauthorized();
		}
		require(chainIds.length == addresses.length, 'invalid array length');
		for (uint i=0; i<chainIds.length; i++) {
			if (emitters[chainIds[i]] != bytes32(0)) {
				revert EmitterAddressExists();
			}
			emitters[chainIds[i]] = addresses[i];
		}
	}

	function changeGuardian(address newGuardian) public {
		if (msg.sender != guardian) {
			revert Unauthorized();
		}
		nextGuardian = newGuardian;
	}

	function claimGuardian() public {
		if (msg.sender != nextGuardian) {
			revert Unauthorized();
		}
		guardian = nextGuardian;
	}

	function getOrders(bytes32[] memory orderHashes) public view returns (Order[] memory) {
		Order[] memory result = new Order[](orderHashes.length);
		for (uint i=0; i<orderHashes.length; i++) {
			result[i] = orders[orderHashes[i]];
		}
		return result;
	}

	receive() external payable {}
}
IERC20.sol 78 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}
IWormhole.sol 141 lines
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

interface IWormhole {
    struct GuardianSet {
        address[] keys;
        uint32 expirationTime;
    }

    struct Signature {
        bytes32 r;
        bytes32 s;
        uint8 v;
        uint8 guardianIndex;
    }

    struct VM {
        uint8 version;
        uint32 timestamp;
        uint32 nonce;
        uint16 emitterChainId;
        bytes32 emitterAddress;
        uint64 sequence;
        uint8 consistencyLevel;
        bytes payload;

        uint32 guardianSetIndex;
        Signature[] signatures;

        bytes32 hash;
    }

    struct ContractUpgrade {
        bytes32 module;
        uint8 action;
        uint16 chain;

        address newContract;
    }

    struct GuardianSetUpgrade {
        bytes32 module;
        uint8 action;
        uint16 chain;

        GuardianSet newGuardianSet;
        uint32 newGuardianSetIndex;
    }

    struct SetMessageFee {
        bytes32 module;
        uint8 action;
        uint16 chain;

        uint256 messageFee;
    }

    struct TransferFees {
        bytes32 module;
        uint8 action;
        uint16 chain;

        uint256 amount;
        bytes32 recipient;
    }

    struct RecoverChainId {
        bytes32 module;
        uint8 action;

        uint256 evmChainId;
        uint16 newChainId;
    }

    event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
    event ContractUpgraded(address indexed oldContract, address indexed newContract);
    event GuardianSetAdded(uint32 indexed index);

    function publishMessage(
        uint32 nonce,
        bytes memory payload,
        uint8 consistencyLevel
    ) external payable returns (uint64 sequence);

    function initialize() external;

    function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason);

    function verifyVM(VM memory vm) external view returns (bool valid, string memory reason);

    function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason);

    function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);

    function quorum(uint numGuardians) external pure returns (uint numSignaturesRequiredForQuorum);

    function getGuardianSet(uint32 index) external view returns (GuardianSet memory);

    function getCurrentGuardianSetIndex() external view returns (uint32);

    function getGuardianSetExpiry() external view returns (uint32);

    function governanceActionIsConsumed(bytes32 hash) external view returns (bool);

    function isInitialized(address impl) external view returns (bool);

    function chainId() external view returns (uint16);

    function isFork() external view returns (bool);

    function governanceChainId() external view returns (uint16);

    function governanceContract() external view returns (bytes32);

    function messageFee() external view returns (uint256);

    function evmChainId() external view returns (uint256);

    function nextSequence(address emitter) external view returns (uint64);

    function parseContractUpgrade(bytes memory encodedUpgrade) external pure returns (ContractUpgrade memory cu);

    function parseGuardianSetUpgrade(bytes memory encodedUpgrade) external pure returns (GuardianSetUpgrade memory gsu);

    function parseSetMessageFee(bytes memory encodedSetMessageFee) external pure returns (SetMessageFee memory smf);

    function parseTransferFees(bytes memory encodedTransferFees) external pure returns (TransferFees memory tf);

    function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci);

    function submitContractUpgrade(bytes memory _vm) external;

    function submitSetMessageFee(bytes memory _vm) external;

    function submitNewGuardianSet(bytes memory _vm) external;

    function submitTransferFees(bytes memory _vm) external;

    function submitRecoverChainId(bytes memory _vm) external;
}
IFeeManager.sol 38 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../swift/SwiftStructs.sol";

interface IFeeManager {
    event ProtocolFeeCalced(uint8 bps);
    event FeeDeposited(address relayer, address token, uint256 amount);
    event FeeWithdrawn(address token, uint256 amount);

    function calcProtocolBps(
        uint64 amountIn,
        address tokenIn,
        bytes32 tokenOut,
        uint16 destChain,
        uint8 referrerBps
    ) external returns (uint8);

     function calcSwiftProtocolBps(
        address tokenIn,
        uint256 amountIn,
        OrderParams memory params
    )  external returns (uint8);

    function calcFastMCTPProtocolBps(
        uint8 payloadType,
        address localToken,
        uint256 recievedAmount,
        address tokenOut,
        address referrerAddr,
        uint8 referrerBps
    ) external returns (uint8);

	function feeCollector() external view returns (address);

    function depositFee(address owner, address token, uint256 amount) payable external;
    function withdrawFee(address token, uint256 amount) external;
}
IWETH.sol 10 lines
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IWETH is IERC20 {
    function deposit() external payable;
    function withdraw(uint amount) external;
}
BytesLib.sol 510 lines
// SPDX-License-Identifier: Unlicense
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */
pragma solidity >=0.8.0 <0.9.0;


library BytesLib {
    function concat(
        bytes memory _preBytes,
        bytes memory _postBytes
    )
        internal
        pure
        returns (bytes memory)
    {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(0x40, and(
              add(add(end, iszero(add(length, mload(_preBytes)))), 31),
              not(31) // Round down to the nearest 32 bytes.
            ))
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(
                            fslot,
                            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                        ),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    )
        internal
        pure
        returns (bytes memory)
    {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                // the next line is the loop condition:
                // while(uint256(mc < end) + cb == 2)
                } eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(
        bytes storage _preBytes,
        bytes memory _postBytes
    )
        internal
        view
        returns (bool)
    {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }
}
SignatureVerifier.sol 47 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IERC1271} from "../interfaces/IERC1271.sol";

library SignatureVerifier {
	/// @notice Thrown when the passed in signature is not a valid length
	error InvalidSignatureLength();

	/// @notice Thrown when the recovered signer is equal to the zero address
	error InvalidSignature();

	/// @notice Thrown when the recovered signer does not equal the claimedSigner
	error InvalidSigner();

	/// @notice Thrown when the recovered contract signature is incorrect
	error InvalidContractSignature();

	bytes32 constant UPPER_BIT_MASK = (0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);

	function verify(bytes calldata signature, bytes32 hash, address claimedSigner) internal view {
		bytes32 r;
		bytes32 s;
		uint8 v;

		if (claimedSigner.code.length == 0) {
			if (signature.length == 65) {
				(r, s) = abi.decode(signature, (bytes32, bytes32));
				v = uint8(signature[64]);
			} else if (signature.length == 64) {
				// EIP-2098
				bytes32 vs;
				(r, vs) = abi.decode(signature, (bytes32, bytes32));
				s = vs & UPPER_BIT_MASK;
				v = uint8(uint256(vs >> 255)) + 27;
			} else {
				revert InvalidSignatureLength();
			}
			address signer = ecrecover(hash, v, r, s);
			if (signer == address(0)) revert InvalidSignature();
			if (signer != claimedSigner) revert InvalidSigner();
		} else {
			bytes4 magicValue = IERC1271(claimedSigner).isValidSignature(hash, signature);
			if (magicValue != IERC1271.isValidSignature.selector) revert InvalidContractSignature();
		}
	}
}
SwiftStructs.sol 157 lines
struct Order {
	Status status;
	uint64 amountIn;
	uint16 destChainId;
}

struct OrderParams {
	uint8 payloadType;
	bytes32 trader;
	bytes32 destAddr;
	uint16 destChainId;
	bytes32 referrerAddr;
	bytes32 tokenOut;
	uint64 minAmountOut;
	uint64 gasDrop;
	uint64 cancelFee;
	uint64 refundFee;
	uint64 deadline;
	uint8 referrerBps;
	uint8 auctionMode;
	bytes32 random;
}

struct ExtraParams {
	uint16 srcChainId;
	bytes32 tokenIn;
	uint8 protocolBps;
	bytes32 customPayloadHash;
}

struct PermitParams {
	uint256 value;
	uint256 deadline;
	uint8 v;
	bytes32 r;
	bytes32 s;
}

struct Key {
	uint8 payloadType;
	bytes32 trader;
	uint16 srcChainId;
	bytes32 tokenIn;
	bytes32 destAddr;
	uint16 destChainId;
	bytes32 tokenOut;
	uint64 minAmountOut;
	uint64 gasDrop;
	uint64 cancelFee;
	uint64 refundFee;
	uint64 deadline;
	bytes32 referrerAddr;
	uint8 referrerBps;
	uint8 protocolBps;
	uint8 auctionMode;
	bytes32 random;
	bytes32 customPayloadHash;
}

struct PaymentParams {
	uint8 payloadType;
	bytes32 orderHash;
	uint64 promisedAmount;
	uint64 minAmountOut;
	address destAddr;
	address tokenOut;
	uint64 gasDrop;
	bool batch;
}

enum Status {
	CREATED,
	FULFILLED,
	SETTLED,
	UNLOCKED,
	CANCELED,
	REFUNDED
}

enum Action {
	INVALID,
	FULFILL,
	UNLOCK,
	REFUND,
	BATCH_UNLOCK,
	COMPRESSED_UNLOCK,
	SET_REFUND_VERIFIER,
	RESCUE
}

enum AuctionMode {
	INVALID,
	LIMIT_ORDER,
	ENGLISH
}

struct UnlockMsg {
	uint8 action;
	bytes32 orderHash;
	uint16 srcChainId;
	bytes32 tokenIn;
	bytes32	referrerAddr;
	uint8 referrerBps;
	uint8 protocolBps;		
	bytes32 unlockReceiver;
	bytes32 driver;
	uint64 fulfillTime;
}
uint constant UNLOCK_MSG_SIZE = 172;	// excluding the action field

struct RefundMsg {
	uint8 action;
	bytes32 orderHash;
	uint16 srcChainId;
	bytes32 tokenIn;
	bytes32 trader;
	bytes32 canceler;
	uint64 cancelFee;
	uint64 refundFee;
}

struct FulfillMsg {
	uint8 action;
	bytes32 orderHash;
	bytes32 driver;
	uint64 promisedAmount;
	uint16 penaltyPeriod;
}

struct TransferParams {
	address from;
	uint256 validAfter;
	uint256 validBefore;
}

struct UnlockParams {
	bytes32 recipient;
	bytes32 driver;
	bool batch;
}

struct RescueMsg {
	uint8 action;
	uint16 chainId;
	bytes32 orderHash;
	uint8 orderStatus;
	address token;
	uint64 amount;
}

struct RefundVerifier {
	uint8 action;
	uint16 chainId;
	address verifier;
	uint16 emitterChainId;
	bytes32 emitterAddr;
}
SwiftErrors.sol 27 lines
error Paused();
error Unauthorized();
error InvalidAction();
error InvalidBpsFee();
error InvalidOrderStatus();
error InvalidOrderHash();
error InvalidEmitterChain();
error InvalidEmitterAddress();
error InvalidSrcChain();
error OrderNotExists(bytes32 orderHash);
error SmallAmountIn();
error FeesTooHigh();
error InvalidGasDrop();
error InvalidDestChain();
error DuplicateOrder();
error InsufficientAmount();
error InvalidAmount();
error DeadlineViolation();
error InvalidWormholeFee();
error InvalidAuctionMode();
error InvalidEvmAddr();
error InvalidPayload();
error InvalidPayloadLength();
error EmitterAddressExists();
error InvalidBatchIndex();
error InvalidAuctionConfig();
error SequenceAlreadyUsed();
IERC20Permit.sol 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
IERC1271.sol 10 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC1271 {
    /// @dev Should return whether the signature provided is valid for the provided data
    /// @param hash      Hash of the data to be signed
    /// @param signature Signature byte array associated with _data
    /// @return magicValue The bytes4 magic value 0x1626ba7e
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

Read Contract

emitters 0x9e70a740 → bytes32
feeManager 0xd0fb0203 → address
getOrders 0x3d474866 → tuple[]
guardian 0x452a9320 → address
nextGuardian 0x4818e84d → address
orders 0x9c3f1e90 → uint8, uint64, uint16
parseFulfillPayload 0xf1d3ac4f → tuple
parseRefundPayload 0x39f3fbd9 → tuple
parseRefundVerifierPayload 0xd383e3e7 → tuple
parseRescuePayload 0xf6203990 → tuple
parseUnlockPayload 0xa5e1ea7d → tuple
paused 0x5c975abb → bool
refundEmitterAddr 0x8b2ab1b7 → bytes32
refundEmitterChainId 0xb854c12b → uint16
refundVerifier 0xbdacaae7 → address
rescueVault 0xcc8388d0 → address
usedVms 0x423c5f22 → bool
weth 0x3fc8cef3 → address
wormhole 0x84acd1bb → address

Write Contract 12 functions

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

changeGuardian 0x2fcb4f04
address newGuardian
claimGuardian 0x459656ee
No parameters
createOrderWithSig 0xd56ed6b2
address tokenIn
uint256 amountIn
tuple params
bytes customPayload
uint256 submissionFee
bytes signedOrderHash
tuple permitParams
returns: bytes32
createOrderWithToken 0x1a61af57
address tokenIn
uint256 amountIn
tuple params
bytes customPayload
returns: bytes32
refundOrder 0xac432e63
bytes encodedVm
bool fast
rescue 0xdae2fc12
bytes encodedVm
setEmitters 0xb365b191
uint16[] chainIds
bytes32[] addresses
setFeeManager 0x472d35b9
address _feeManager
setPause 0xbedb86fb
bool _pause
setRefundVerifier 0x46963505
bytes encodedVm
unlockCompressedBatch 0x2d67b5ea
bytes encodedVm
bytes encodedPayload
uint16[] indexes
unlockSingle 0x119abf67
bytes encodedVm

Recent Transactions

This address has 1 on-chain transactions, but only 0.7% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →