Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xB78B75f3cF6F22F1d336d5b5Ece2e918502bbdAE
Balance 0 ETH
Nonce 1
Code Size 10146 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

10146 bytes
0x608060405234801561000f575f80fd5b5060043610610111575f3560e01c80638129fc1c1161009e5780638da5cb5b1161006e5780638da5cb5b14610322578063cd99240014610347578063df51b3591461035a578063e30c39781461036d578063f2fde38b1461037e575f80fd5b80638129fc1c146102b95780638456cb59146102c1578063898861b0146102c95780638bfa0549146102dc575f80fd5b806371eedb88116100e457806371eedb881461015657806375151b6314610169578063768c6ec01461017c57806379ba509714610290578063809804f714610298575f80fd5b80633f4ba83a1461011557806340ebc6771461011f5780635c975abb14610132578063715018a61461014e575b5f80fd5b61011d610391565b005b61011d61012d36600461231b565b6103a3565b60cd5460ff165b60405190151581526020015b60405180910390f35b61011d610595565b610139610164366004612345565b6105a6565b610139610177366004612365565b6108c6565b61028361018a366004612385565b60408051610140810182525f80825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081018290526101008101829052610120810191909152505f90815260ff602081815260409283902083516101408101855281546001600160a01b0390811682526001830154811693820193909352600282015483169481019490945260038101546060850152600481015460808501526005810154808416151560a0860152610100808204909416151560c086015262010000900490911660e084015260068101546001600160601b0316918301919091526007015461012082015290565b604051610145919061239c565b61011d6108f4565b6102ab6102a6366004612461565b61096e565b604051908152602001610145565b61011d610e6f565b61011d610f88565b61011d6102d736600461252d565b610f98565b6102ef6102ea366004612365565b61122c565b60405161014591908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b6033546001600160a01b03165b6040516001600160a01b039091168152602001610145565b61011d61035536600461256b565b6112a0565b6101396103683660046125b4565b6113bb565b6065546001600160a01b031661032f565b61011d61038c366004612365565b611931565b6103996119a2565b6103a16119fc565b565b6103ab6119a2565b6001600160a01b0381166103fe5760405162461bcd60e51b8152602060048201526015602482015274476174657761793a207a65726f206164647265737360581b60448201526064015b60405180910390fd5b5f8267747265617375727960c01b036104b4576098546001600160a01b03808416600160401b90920416036104835760405162461bcd60e51b815260206004820152602560248201527f476174657761793a207472656173757279206164647265737320616c726561646044820152641e481cd95d60da1b60648201526084016103f5565b506098805468010000000000000000600160e01b031916600160401b6001600160a01b038416021790556001610554565b826930b3b3b932b3b0ba37b960b11b03610554576099546001600160a01b038084169116036105355760405162461bcd60e51b815260206004820152602760248201527f476174657761793a2061676772656761746f72206164647265737320616c726560448201526618591e481cd95d60ca1b60648201526084016103f5565b50609980546001600160a01b0319166001600160a01b03831617905560015b8015610590576040516001600160a01b0383169084907fbbc5b96e57cfecb3dbeeadf92e87f15e58e64fcd75cbe256dcc5d9ef2e51e8a4905f90a35b505050565b61059d6119a2565b6103a15f611a4e565b6099545f906001600160a01b031633146105f35760405162461bcd60e51b815260206004820152600e60248201526d27b7363ca0b3b3b932b3b0ba37b960911b60448201526064016103f5565b5f82815260ff602081905260409091206005015416156106465760405162461bcd60e51b815260206004820152600e60248201526d13dc99195c919d5b199a5b1b195960921b60448201526064016103f5565b5f82815260ff60208190526040909120600501546101009004161561069d5760405162461bcd60e51b815260206004820152600d60248201526c13dc99195c9499599d5b991959609a1b60448201526064016103f5565b5f82815260ff60205260409020600401548311156106f55760405162461bcd60e51b81526020600482015260156024820152744665654578636565647350726f746f636f6c46656560581b60448201526064016103f5565b8215610789575f82815260ff60205260409081902060010154609854915163a9059cbb60e01b8152600160401b9092046001600160a01b03908116600484015260248301869052169063a9059cbb906044016020604051808303815f875af1158015610763573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107879190612607565b505b5f82815260ff6020526040812060058101805461ff0019166101001790556006810180546bffffffffffffffffffffffff19169055600701546107cd90859061263a565b5f84815260ff60205260409020600181015460058201546003909201549293506001600160a01b039081169263a9059cbb9262010000900490911690610813908561264d565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303815f875af115801561085b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061087f9190612607565b50827f0736fe428e1747ca8d387c2e6fa1a31a0cde62d3a167c40a46ade59a3cdc828e856040516108b291815260200190565b60405180910390a260019150505b92915050565b6001600160a01b0381165f908152609a60205260408120546001036108ed57506001919050565b505f919050565b60655433906001600160a01b031681146109625760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b60648201526084016103f5565b61096b81611a4e565b50565b5f610977611a67565b6109848989868989611aad565b5f8290036109c95760405162461bcd60e51b8152602060048201526012602482015271092dcecc2d8d2c89acae6e6c2ceca90c2e6d60731b60448201526064016103f5565b6001600160a01b0389166323b872dd33306109e4898d61264d565b6040516001600160e01b031960e086901b1681526001600160a01b03938416600482015292909116602483015260448201526064016020604051808303815f875af1158015610a35573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a599190612607565b50335f90815261010060205260408120805491610a7583612660565b9091555050335f8181526101006020908152604091829020548251918201939093529081019190915246606082015260800160408051601f1981840301815291815281516020928301205f81815260ff9093529120549091506001600160a01b031615610b195760405162461bcd60e51b81526020600482015260126024820152714f72646572416c726561647945786973747360701b60448201526064016103f5565b5f876001600160601b0316606403610b7157505f85610b6c5760405162461bcd60e51b815260206004820152600f60248201526e53656e64657246656549735a65726f60881b60448201526064016103f5565b610c29565b6001600160a01b038a165f908152609b602090815260409182902082516080810184528154815260018201549281019290925260028101549282019290925260039091015460608201819052610c095760405162461bcd60e51b815260206004820152601d60248201527f546f6b656e46656553657474696e67734e6f74436f6e6669677572656400000060448201526064016103f5565b6097546060820151610c1b908c612678565b610c25919061268f565b9150505b604051806101400160405280336001600160a01b031681526020018b6001600160a01b03168152602001886001600160a01b031681526020018781526020018281526020015f151581526020015f15158152602001866001600160a01b0316815260200160975467ffffffffffffffff166001600160601b031681526020018a81525060ff5f8481526020019081526020015f205f820151815f015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506020820151816001015f6101000a8154816001600160a01b0302191690836001600160a01b031602179055506040820151816002015f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550606082015181600301556080820151816004015560a0820151816005015f6101000a81548160ff02191690831515021790555060c08201518160050160016101000a81548160ff02191690831515021790555060e08201518160050160026101000a8154816001600160a01b0302191690836001600160a01b03160217905550610100820151816006015f6101000a8154816001600160601b0302191690836001600160601b03160217905550610120820151816007015590505060ff5f8381526020019081526020015f20600701548a6001600160a01b0316866001600160a01b03167f40ccd1ceb111a3c186ef9911e1b876dc1f789ed331b86097b3b8851055b6a13784868d8a8a604051610e5a9594939291906126ae565b60405180910390a45098975050505050505050565b5f54610100900460ff1615808015610e8d57505f54600160ff909116105b80610ea65750303b158015610ea657505f5460ff166001145b610f095760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016103f5565b5f805460ff191660011790558015610f2a575f805461ff0019166101001790555b620186a0609755610f39611bf2565b610f41611c20565b801561096b575f805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b610f906119a2565b6103a1611c4e565b610fa06119a2565b6001600160a01b0385165f908152609a60205260409020546001146110075760405162461bcd60e51b815260206004820152601c60248201527f476174657761793a20746f6b656e206e6f7420737570706f727465640000000060448201526064016103f5565b6097548411156110655760405162461bcd60e51b815260206004820152602360248201527f476174657761793a20696e76616c69642073656e64657220746f2070726f76696044820152623232b960e91b60648201526084016103f5565b6097548311156110c75760405162461bcd60e51b815260206004820152602760248201527f476174657761793a20696e76616c69642070726f766964657220746f206167676044820152663932b3b0ba37b960c91b60648201526084016103f5565b6097548211156111275760405162461bcd60e51b815260206004820152602560248201527f476174657761793a20696e76616c69642073656e64657220746f20616767726560448201526433b0ba37b960d91b60648201526084016103f5565b60975481111561118c5760405162461bcd60e51b815260206004820152602a60248201527f476174657761793a20696e76616c69642070726f766964657220746f206167676044820152690e4cacec2e8dee440ccf60b31b60648201526084016103f5565b6040805160808082018352868252602080830187815283850187815260608086018881526001600160a01b038d165f818152609b8752899020975188559351600188015591516002870155905160039095019490945584518981529182018890529381018690529182018490527fd4d646cffa66ebf695b792bd660c97076ed45a889e14d544eb8ab8a44b34a59c91015b60405180910390a25050505050565b61125360405180608001604052805f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152609b6020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b6112a86119a2565b6001600160a01b0382166112f65760405162461bcd60e51b8152602060048201526015602482015274476174657761793a207a65726f206164647265737360581b60448201526064016103f5565b80600114806113055750806002145b6113515760405162461bcd60e51b815260206004820152601760248201527f476174657761793a20696e76616c69642073746174757300000000000000000060448201526064016103f5565b82643a37b5b2b760d91b03610590576001600160a01b0382165f818152609a6020526040908190208390555184907fcfa976492af7c14a916cc3a239f4c9c75bbd7f5f0e398beb41d892c7eeccae4c906113ae9085815260200190565b60405180910390a3505050565b6099545f906001600160a01b031633146114085760405162461bcd60e51b815260206004820152600e60248201526d27b7363ca0b3b3b932b3b0ba37b960911b60448201526064016103f5565b5f85815260ff6020819052604090912060050154161561145b5760405162461bcd60e51b815260206004820152600e60248201526d13dc99195c919d5b199a5b1b195960921b60448201526064016103f5565b5f85815260ff6020819052604090912060050154610100900416156114b25760405162461bcd60e51b815260206004820152600d60248201526c13dc99195c9499599d5b991959609a1b60448201526064016103f5565b6097548267ffffffffffffffff1611156115055760405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a59149958985d1954195c98d95b9d60621b60448201526064016103f5565b5f85815260ff6020526040902060018101546006909101546001600160a01b03909116906001600160601b031667ffffffffffffffff8516158015906115555750808567ffffffffffffffff1611155b6115985760405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a5914d95d1d1b1954195c98d95b9d60621b60448201526064016103f5565b5f87815260ff60205260408120600601805467ffffffffffffffff881692906115cb9084906001600160601b03166126fa565b82546101009290920a6001600160601b038181021990931691831602179091555f89815260ff602052604081206006015490911690039050611652575f87815260ff6020526040902060058101805460ff19166001179055600301541580159061164457505f87815260ff602052604090206004015415155b156116525761165287611c8b565b5f87815260ff60205260409020600301541580159061167f57505f87815260ff6020526040902060040154155b1561168f5761168f878787611ec3565b5f87815260ff602052604081206007015482906116b79067ffffffffffffffff891690612678565b6116c1919061268f565b90508060ff5f8a81526020019081526020015f206007015f8282546116e6919061263a565b90915550505f88815260ff602052604090206004015415611850575f88815260ff602090815260408083206001908101546001600160a01b03168452609b83528184208251608081018452815481529181015493820193909352600283015491810191909152600390910154606082018190526097549192919061176a9085612678565b611774919061268f565b9050611780818461263a565b925067ffffffffffffffff8716156117d1576097545f906117ab67ffffffffffffffff8a1684612678565b6117b5919061268f565b90506117c1818361263a565b91506117cd818561264d565b9350505b60985460405163a9059cbb60e01b8152600160401b9091046001600160a01b0390811660048301526024820183905286169063a9059cbb906044016020604051808303815f875af1158015611828573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061184c9190612607565b5050505b60405163a9059cbb60e01b81526001600160a01b0388811660048301526024820183905284169063a9059cbb906044016020604051808303815f875af115801561189c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118c09190612607565b50866001600160a01b0316887f57c683de2e7c8263c7f57fd108416b9bdaa7a6e7f2e4e7102c3b6f9e37f1cc378b898960405161191a9392919092835267ffffffffffffffff918216602084015216604082015260600190565b60405180910390a350600198975050505050505050565b6119396119a2565b606580546001600160a01b0383166001600160a01b0319909116811790915561196a6033546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6033546001600160a01b031633146103a15760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016103f5565b611a04612205565b60cd805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b606580546001600160a01b031916905561096b8161224e565b60cd5460ff16156103a15760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016103f5565b6001600160a01b0385165f908152609a6020526040902054600114611b085760405162461bcd60e51b8152602060048201526011602482015270151bdad95b939bdd14dd5c1c1bdc9d1959607a1b60448201526064016103f5565b835f03611b465760405162461bcd60e51b815260206004820152600c60248201526b416d6f756e7449735a65726f60a01b60448201526064016103f5565b6001600160a01b038316611b8f5760405162461bcd60e51b815260206004820152601060248201526f5468726f775a65726f4164647265737360801b60448201526064016103f5565b8015611beb576001600160a01b038216611beb5760405162461bcd60e51b815260206004820152601960248201527f496e76616c696453656e646572466565526563697069656e740000000000000060448201526064016103f5565b5050505050565b5f54610100900460ff16611c185760405162461bcd60e51b81526004016103f590612721565b6103a161229f565b5f54610100900460ff16611c465760405162461bcd60e51b81526004016103f590612721565b6103a16122ce565b611c56611a67565b60cd805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258611a313390565b5f81815260ff602081815260408084206001808201546001600160a01b03168652609b84528286208351608081018552815481529181015482860152600281015493820193845260039081015460608301528787529490935292909201546097549251919390929091611cfe908261263a565b611d089084612678565b611d12919061268f565b90505f611d1f828461263a565b90508115611db2575f85815260ff6020526040908190206001810154600290910154915163a9059cbb60e01b81526001600160a01b0392831660048201526024810185905291169063a9059cbb906044016020604051808303815f875af1158015611d8c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611db09190612607565b505b8015611e46575f85815260ff60205260409081902060010154609854915163a9059cbb60e01b8152600160401b9092046001600160a01b03908116600484015260248301849052169063a9059cbb906044016020604051808303815f875af1158015611e20573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e449190612607565b505b5f85815260ff602052604080822060020154905184926001600160a01b03909216917f44f6938ca4a10313aabb76f874cced61e35710a734a126e4afb34461bf8c250191a3604080518381526020810183905286917f88592047496a7850992dc5e8cd92a9b633cef0d191a4f5e87fd745c7d382630a910161121d565b5f83815260ff602081815260408084206001808201546001600160a01b03168652609b845282862083516080810185528154815291810154828601526002810154938201939093526003928301546060820152888652939092520154609754825192939192611f329084612678565b611f3c919061268f565b90505f6097548567ffffffffffffffff1683611f589190612678565b611f62919061268f565b90505f609754856020015183611f789190612678565b611f82919061268f565b90505f611f8f848661263a565b90508015801590611fb757505f89815260ff60205260409020600601546001600160601b0316155b15612047575f89815260ff6020526040908190206001810154600290910154915163a9059cbb60e01b81526001600160a01b0392831660048201526024810184905291169063a9059cbb906044016020604051808303815f875af1158015612021573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120459190612607565b505b81156120db575f89815260ff60205260409081902060010154609854915163a9059cbb60e01b8152600160401b9092046001600160a01b03908116600484015260248301859052169063a9059cbb906044016020604051808303815f875af11580156120b5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120d99190612607565b505b6120e5828461263a565b92508215612172575f89815260ff60205260409081902060010154905163a9059cbb60e01b81526001600160a01b038a81166004830152602482018690529091169063a9059cbb906044016020604051808303815f875af115801561214c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121709190612607565b505b5f89815260ff602052604080822060020154905183926001600160a01b03909216917f44f6938ca4a10313aabb76f874cced61e35710a734a126e4afb34461bf8c250191a3604080518281526020810185905290810183905289907f831c7cc0006d91462607c476603366c48469d125de6228c0791a7090efd7f7a49060600160405180910390a2505050505050505050565b60cd5460ff166103a15760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016103f5565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff166122c55760405162461bcd60e51b81526004016103f590612721565b6103a133611a4e565b5f54610100900460ff166122f45760405162461bcd60e51b81526004016103f590612721565b60cd805460ff19169055565b80356001600160a01b0381168114612316575f80fd5b919050565b5f806040838503121561232c575f80fd5b8235915061233c60208401612300565b90509250929050565b5f8060408385031215612356575f80fd5b50508035926020909101359150565b5f60208284031215612375575f80fd5b61237e82612300565b9392505050565b5f60208284031215612395575f80fd5b5035919050565b81516001600160a01b03168152610140810160208301516123c860208401826001600160a01b03169052565b5060408301516123e360408401826001600160a01b03169052565b50606083015160608301526080830151608083015260a083015161240b60a084018215159052565b5060c083015161241f60c084018215159052565b5060e083015161243a60e08401826001600160a01b03169052565b50610100838101516001600160601b03811684830152505061012092830151919092015290565b5f805f805f805f8060e0898b031215612478575f80fd5b61248189612300565b97506020890135965060408901356001600160601b03811681146124a3575f80fd5b95506124b160608a01612300565b9450608089013593506124c660a08a01612300565b925060c089013567ffffffffffffffff808211156124e2575f80fd5b818b0191508b601f8301126124f5575f80fd5b813581811115612503575f80fd5b8c6020828501011115612514575f80fd5b6020830194508093505050509295985092959890939650565b5f805f805f60a08688031215612541575f80fd5b61254a86612300565b97602087013597506040870135966060810135965060800135945092505050565b5f805f6060848603121561257d575f80fd5b8335925061258d60208501612300565b9150604084013590509250925092565b803567ffffffffffffffff81168114612316575f80fd5b5f805f805f60a086880312156125c8575f80fd5b85359450602086013593506125df60408701612300565b92506125ed6060870161259d565b91506125fb6080870161259d565b90509295509295909350565b5f60208284031215612617575f80fd5b8151801515811461237e575f80fd5b634e487b7160e01b5f52601160045260245ffd5b818103818111156108c0576108c0612626565b808201808211156108c0576108c0612626565b5f6001820161267157612671612626565b5060010190565b80820281158282048414176108c0576108c0612626565b5f826126a957634e487b7160e01b5f52601260045260245ffd5b500490565b8581528460208201526001600160601b038416604082015260806060820152816080820152818360a08301375f81830160a090810191909152601f909201601f19160101949350505050565b6001600160601b0382811682821603908082111561271a5761271a612626565b5092915050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b60608201526080019056fea2646970667358221220a761a2bd09b4dd582f624e8d64919e86d81ba23a0979bde621bf2ed5775658a964736f6c63430008140033

Verified Source Code Full Match

Compiler: v0.8.20+commit.a1b79de6 EVM: shanghai Optimization: Yes (200 runs)
Ownable2StepUpgradeable.sol 71 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./OwnableUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
    address private _pendingOwner;

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

    function __Ownable2Step_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable2Step_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
OwnableUpgradeable.sol 95 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

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

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

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

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

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
Initializable.sol 166 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}
PausableUpgradeable.sol 117 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}
AddressUpgradeable.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 AddressUpgradeable {
    /**
     * @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);
        }
    }
}
ContextUpgradeable.sol 41 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)

pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";

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

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}
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);
}
Gateway.sol 347 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import '@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol';

import {GatewaySettingManager} from './GatewaySettingManager.sol';
import {IGateway, IERC20} from './interfaces/IGateway.sol';

/**
 * @title Gateway
 * @notice This contract serves as a gateway for creating orders and managing settlements.
 */
contract Gateway is IGateway, GatewaySettingManager, PausableUpgradeable {
	struct fee {
		uint256 protocolFee;
		uint256 liquidityProviderAmount;
	}

	mapping(bytes32 => Order) private order;
	mapping(address => uint256) private _nonce;
	uint256[50] private __gap;

	/// @custom:oz-upgrades-unsafe-allow constructor
	constructor() {
		_disableInitializers();
	}

	/**
	 * @dev Initialize function.
	 */
	function initialize() external initializer {
		MAX_BPS = 100_000;
		__Ownable2Step_init();
		__Pausable_init();
	}

	/**
	 * @dev Modifier that allows only the aggregator to call a function.
	 */
	modifier onlyAggregator() {
		require(msg.sender == _aggregatorAddress, 'OnlyAggregator');
		_;
	}

	/* ##################################################################
                                OWNER FUNCTIONS
    ################################################################## */
	/**
	 * @dev Pause the contract.
	 */
	function pause() external onlyOwner {
		_pause();
	}

	/**
	 * @dev Unpause the contract.
	 */
	function unpause() external onlyOwner {
		_unpause();
	}

	/* ##################################################################
                                USER CALLS
    ################################################################## */
	/** @dev See {createOrder-IGateway}. */
	function createOrder(
		address _token,
		uint256 _amount,
		uint96 _rate,
		address _senderFeeRecipient,
		uint256 _senderFee,
		address _refundAddress,
		string calldata messageHash
	) external whenNotPaused returns (bytes32 orderId) {
		// checks that are required
		_handler(_token, _amount, _refundAddress, _senderFeeRecipient, _senderFee);

		// validate messageHash
		require(bytes(messageHash).length != 0, 'InvalidMessageHash');

		// transfer token from msg.sender to contract
		IERC20(_token).transferFrom(msg.sender, address(this), _amount + _senderFee);

		// increase users nonce to avoid replay attacks
		_nonce[msg.sender]++;

		// generate transaction id for the transaction with chain id
		orderId = keccak256(abi.encode(msg.sender, _nonce[msg.sender], block.chainid));

		require(order[orderId].sender == address(0), 'OrderAlreadyExists');

		// update transaction
		uint256 _protocolFee;
		if (_rate == 100) {
			// local transfer (rate = 1)
			_protocolFee = 0;
			require(_senderFee > 0, 'SenderFeeIsZero');
		} else {
			// fx transfer (rate != 1) - use token-specific providerToAggregatorFx
			TokenFeeSettings memory settings = _tokenFeeSettings[_token];
			require(settings.providerToAggregatorFx > 0, 'TokenFeeSettingsNotConfigured');
			_protocolFee = (_amount * settings.providerToAggregatorFx) / MAX_BPS;
		}
		order[orderId] = Order({
			sender: msg.sender,
			token: _token,
			senderFeeRecipient: _senderFeeRecipient,
			senderFee: _senderFee,
			protocolFee: _protocolFee,
			isFulfilled: false,
			isRefunded: false,
			refundAddress: _refundAddress,
			currentBPS: uint64(MAX_BPS),
			amount: _amount
		});

		// emit order created event
		emit OrderCreated(
			_refundAddress,
			_token,
			order[orderId].amount,
			_protocolFee,
			orderId,
			_rate,
			messageHash
		);
	}

	/**
	 * @dev Internal function to handle order creation.
	 * @param _token The address of the token being traded.
	 * @param _amount The amount of tokens being traded.
	 * @param _refundAddress The address to refund the tokens in case of cancellation.
	 * @param _senderFeeRecipient The address of the recipient for the sender fee.
	 * @param _senderFee The amount of the sender fee.
	 */
	function _handler(
		address _token,
		uint256 _amount,
		address _refundAddress,
		address _senderFeeRecipient,
		uint256 _senderFee
	) internal view {
		require(_isTokenSupported[_token] == 1, 'TokenNotSupported');
		require(_amount != 0, 'AmountIsZero');
		require(_refundAddress != address(0), 'ThrowZeroAddress');

		if (_senderFee != 0) {
			require(_senderFeeRecipient != address(0), 'InvalidSenderFeeRecipient');
		}
	}

	/* ##################################################################
                                AGGREGATOR FUNCTIONS
    ################################################################## */
	/** @dev See {settle-IGateway}. */
	function settle(
		bytes32 _splitOrderId,
		bytes32 _orderId,
		address _liquidityProvider,
		uint64 _settlePercent,
		uint64 _rebatePercent
	) external onlyAggregator returns (bool) {
		// ensure the transaction has not been fulfilled
		require(!order[_orderId].isFulfilled, 'OrderFulfilled');
		require(!order[_orderId].isRefunded, 'OrderRefunded');
		require(_rebatePercent <= MAX_BPS, 'InvalidRebatePercent');

		// load the token into memory
		address token = order[_orderId].token;

		// subtract sum of amount based on the input _settlePercent
		uint256 currentOrderBPS = order[_orderId].currentBPS;
		require(_settlePercent > 0 && _settlePercent <= currentOrderBPS, "InvalidSettlePercent");
		order[_orderId].currentBPS -= _settlePercent;

		if (order[_orderId].currentBPS == 0) {
			// update the transaction to be fulfilled
			order[_orderId].isFulfilled = true;

			if (order[_orderId].senderFee != 0 && order[_orderId].protocolFee != 0) {
				// fx transfer - sender keeps all fee
				_handleFxTransferFeeSplitting(_orderId);
			}
		}

		if (order[_orderId].senderFee != 0 && order[_orderId].protocolFee == 0) {
			// local transfer - split sender fee
			_handleLocalTransferFeeSplitting(_orderId, _liquidityProvider, _settlePercent);
		}

		// transfer to liquidity provider
		uint256 liquidityProviderAmount = (order[_orderId].amount * _settlePercent) /
			currentOrderBPS;
		order[_orderId].amount -= liquidityProviderAmount;

		if (order[_orderId].protocolFee != 0) {
			// FX transfer - use token-specific providerToAggregatorFx
			TokenFeeSettings memory settings = _tokenFeeSettings[order[_orderId].token];
			uint256 protocolFee = (liquidityProviderAmount * settings.providerToAggregatorFx) /
				MAX_BPS;
			liquidityProviderAmount -= protocolFee;

			if (_rebatePercent != 0) {
				// calculate rebate amount
				uint256 rebateAmount = (protocolFee * _rebatePercent) / MAX_BPS;
				protocolFee -= rebateAmount;
				liquidityProviderAmount += rebateAmount;
			}

			// transfer protocol fee
			IERC20(token).transfer(treasuryAddress, protocolFee);
		}

		IERC20(token).transfer(_liquidityProvider, liquidityProviderAmount);

		// emit settled event
		emit OrderSettled(
			_splitOrderId,
			_orderId,
			_liquidityProvider,
			_settlePercent,
			_rebatePercent
		);

		return true;
	}

	/** @dev See {refund-IGateway}. */
	function refund(uint256 _fee, bytes32 _orderId) external onlyAggregator returns (bool) {
		// ensure the transaction has not been fulfilled
		require(!order[_orderId].isFulfilled, 'OrderFulfilled');
		require(!order[_orderId].isRefunded, 'OrderRefunded');
		require(order[_orderId].protocolFee >= _fee, 'FeeExceedsProtocolFee');

		if (_fee > 0) {
			// transfer refund fee to the treasury
			IERC20(order[_orderId].token).transfer(treasuryAddress, _fee);
		}

		// reset state values
		order[_orderId].isRefunded = true;
		order[_orderId].currentBPS = 0;

		// deduct fee from order amount
		uint256 refundAmount = order[_orderId].amount - _fee;

		// transfer refund amount and sender fee to the refund address
		IERC20(order[_orderId].token).transfer(
			order[_orderId].refundAddress,
			refundAmount + order[_orderId].senderFee
		);

		// emit refunded event
		emit OrderRefunded(_fee, _orderId);

		return true;
	}

	/* ##################################################################
                                VIEW CALLS
    ################################################################## */
	/** @dev See {getOrderInfo-IGateway}. */
	function getOrderInfo(bytes32 _orderId) external view returns (Order memory) {
		return order[_orderId];
	}

	/** @dev See {isTokenSupported-IGateway}. */
	function isTokenSupported(address _token) external view returns (bool) {
		if (_isTokenSupported[_token] == 1) return true;
		return false;
	}

	/**
	 * @dev Handles fee splitting for local transfers (rate = 1).
	 * @param _orderId The order ID to process.
	 * @param _liquidityProvider The address of the liquidity provider who fulfilled the order.
	 */
	function _handleLocalTransferFeeSplitting(
		bytes32 _orderId,
		address _liquidityProvider,
		uint64 _settlePercent
	) internal {
		TokenFeeSettings memory settings = _tokenFeeSettings[order[_orderId].token];
		uint256 senderFee = order[_orderId].senderFee;

		// Calculate splits based on config
		uint256 providerAmount = (senderFee * settings.senderToProvider) / MAX_BPS;
		uint256 currentProviderAmount = (providerAmount * _settlePercent) / MAX_BPS;
		uint256 aggregatorAmount = (currentProviderAmount * settings.providerToAggregator) /
			MAX_BPS;
		uint256 senderAmount = senderFee - providerAmount;

		// Transfer sender portion
		if (senderAmount != 0 && order[_orderId].currentBPS == 0) {
			IERC20(order[_orderId].token).transfer(
				order[_orderId].senderFeeRecipient,
				senderAmount
			);
		}

		// Transfer aggregator portion to treasury
		if (aggregatorAmount != 0) {
			IERC20(order[_orderId].token).transfer(treasuryAddress, aggregatorAmount);
		}

		// Transfer provider portion to the liquidity provider who fulfilled the order
		currentProviderAmount = currentProviderAmount - aggregatorAmount;
		if (currentProviderAmount != 0) {
			IERC20(order[_orderId].token).transfer(_liquidityProvider, currentProviderAmount);
		}

		// Emit events
		emit SenderFeeTransferred(order[_orderId].senderFeeRecipient, senderAmount);
		emit LocalTransferFeeSplit(_orderId, senderAmount, currentProviderAmount, aggregatorAmount);
	}

	/**
	 * @dev Handles fee splitting for FX transfers (rate != 1).
	 * @param _orderId The order ID to process.
	 */
	function _handleFxTransferFeeSplitting(bytes32 _orderId) internal {
		TokenFeeSettings memory settings = _tokenFeeSettings[order[_orderId].token];
		uint256 senderFee = order[_orderId].senderFee;

		// Calculate sender portion based on senderToAggregator setting
		uint256 senderAmount = (senderFee * (MAX_BPS - settings.senderToAggregator)) / MAX_BPS;
		uint256 aggregatorAmount = senderFee - senderAmount;

		// Transfer sender portion
		if (senderAmount > 0) {
			IERC20(order[_orderId].token).transfer(
				order[_orderId].senderFeeRecipient,
				senderAmount
			);
		}

		// Transfer aggregator portion to treasury
		if (aggregatorAmount > 0) {
			IERC20(order[_orderId].token).transfer(treasuryAddress, aggregatorAmount);
		}

		// Emit events
		emit SenderFeeTransferred(order[_orderId].senderFeeRecipient, senderAmount);
		emit FxTransferFeeSplit(_orderId, senderAmount, aggregatorAmount);
	}
}
GatewaySettingManager.sol 134 lines
// SPDX-License-Identifier: UNLICENSED

/**
 * @title GatewaySettingManager
 * @dev This contract manages the settings and configurations for the Gateway protocol.
 */
pragma solidity ^0.8.18;

import '@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol';

contract GatewaySettingManager is Ownable2StepUpgradeable {
	uint256 internal MAX_BPS;
	uint64 internal protocolFeePercent; // DEPRECATED — kept for proxy storage compatibility (do not remove)
	address internal treasuryAddress;
	address internal _aggregatorAddress;
	mapping(address => uint256) internal _isTokenSupported;

	// Token-specific fee settings
	struct TokenFeeSettings {
		uint256 senderToProvider; // % of sender fee that goes to provider (local mode)
		uint256 providerToAggregator; // % of provider's share that goes to aggregator (local mode)
		uint256 senderToAggregator; // % of sender fee that goes to aggregator (fx mode)
		uint256 providerToAggregatorFx; // % of transaction amount provider pays to aggregator (fx mode)
	}

	mapping(address => TokenFeeSettings) internal _tokenFeeSettings;

	uint256[49] private __gap;

	event SettingManagerBool(bytes32 indexed what, address indexed value, uint256 status);
	event ProtocolAddressUpdated(bytes32 indexed what, address indexed treasuryAddress);
	event SetFeeRecipient(address indexed treasuryAddress);
	event TokenFeeSettingsUpdated(
		address indexed token,
		uint256 senderToProvider,
		uint256 providerToAggregator,
		uint256 senderToAggregator,
		uint256 providerToAggregatorFx
	);

	/* ##################################################################
                                OWNER FUNCTIONS
    ################################################################## */

	/**
	 * @dev Sets the boolean value for a specific setting.
	 * @param what The setting to be updated.
	 * @param value The address or value associated with the setting.
	 * @param status The boolean value to be set.
	 * Requirements:
	 * - The value must not be a zero address.
	 */
	function settingManagerBool(bytes32 what, address value, uint256 status) external onlyOwner {
		require(value != address(0), 'Gateway: zero address');
		require(status == 1 || status == 2, 'Gateway: invalid status');
		if (what == 'token') {
			_isTokenSupported[value] = status;
			emit SettingManagerBool(what, value, status);
		}
	}

	/**
	 * @dev Updates a protocol address.
	 * @param what The address type to be updated (treasury or aggregator).
	 * @param value The new address to be set.
	 * Requirements:
	 * - The value must not be a zero address.
	 */
	function updateProtocolAddress(bytes32 what, address value) external onlyOwner {
		require(value != address(0), 'Gateway: zero address');
		bool updated;
		if (what == 'treasury') {
			require(treasuryAddress != value, 'Gateway: treasury address already set');
			treasuryAddress = value;
			updated = true;
		} else if (what == 'aggregator') {
			require(_aggregatorAddress != value, 'Gateway: aggregator address already set');
			_aggregatorAddress = value;
			updated = true;
		}
		if (updated) {
			emit ProtocolAddressUpdated(what, value);
		}
	}

	/**
	 * @dev Sets token-specific fee settings for stablecoins.
	 * @param token The token address to configure.
	 * @param senderToProvider Percentage of sender fee that goes to provider (local mode).
	 * @param providerToAggregator Percentage of provider's share that goes to aggregator (local mode).
	 * @param senderToAggregator Percentage of sender fee that goes to aggregator (fx mode).
	 * @param providerToAggregatorFx Percentage of transaction amount provider pays to aggregator (fx mode).
	 * Requirements:
	 * - The token must be supported.
	 * - Fee percentages must be within valid ranges.
	 */
	function setTokenFeeSettings(
		address token,
		uint256 senderToProvider,
		uint256 providerToAggregator,
		uint256 senderToAggregator,
		uint256 providerToAggregatorFx
	) external onlyOwner {
		require(_isTokenSupported[token] == 1, 'Gateway: token not supported');
		require(senderToProvider <= MAX_BPS, 'Gateway: invalid sender to provider');
		require(providerToAggregator <= MAX_BPS, 'Gateway: invalid provider to aggregator');
		require(senderToAggregator <= MAX_BPS, 'Gateway: invalid sender to aggregator');
		require(providerToAggregatorFx <= MAX_BPS, 'Gateway: invalid provider to aggregator fx');

		_tokenFeeSettings[token] = TokenFeeSettings({
			senderToProvider: senderToProvider,
			providerToAggregator: providerToAggregator,
			senderToAggregator: senderToAggregator,
			providerToAggregatorFx: providerToAggregatorFx
		});

		emit TokenFeeSettingsUpdated(
			token,
			senderToProvider,
			providerToAggregator,
			senderToAggregator,
			providerToAggregatorFx
		);
	}

	/**
	 * @dev Gets token-specific fee settings.
	 * @param token The token address to query.
	 * @return TokenFeeSettings struct containing all fee settings for the token.
	 */
	function getTokenFeeSettings(address token) external view returns (TokenFeeSettings memory) {
		return _tokenFeeSettings[token];
	}
}
IGateway.sol 187 lines
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

/**
 * @title IGateway
 * @notice Interface for the Gateway contract.
 */
interface IGateway {
	/* ##################################################################
                                EVENTS
    ################################################################## */
	/**
	 * @dev Emitted when a deposit is made.
	 * @param sender The address of the sender.
	 * @param token The address of the deposited token.
	 * @param amount The amount of the deposit.
	 * @param orderId The ID of the order.
	 * @param rate The rate at which the deposit is made.
	 * @param messageHash The hash of the message.
	 */
	event OrderCreated(
		address indexed sender,
		address indexed token,
		uint256 indexed amount,
		uint256 protocolFee,
		bytes32 orderId,
		uint256 rate,
		string messageHash
	);

	/**
	 * @dev Emitted when an aggregator settles a transaction.
	 * @param splitOrderId The ID of the split order.
	 * @param orderId The ID of the order.
	 * @param liquidityProvider The address of the liquidity provider.
	 * @param settlePercent The percentage at which the transaction is settled.
	 * @param rebatePercent The percentage of the aggregator fee that is given back to the provider.
	 */
	event OrderSettled(
		bytes32 splitOrderId,
		bytes32 indexed orderId,
		address indexed liquidityProvider,
		uint64 settlePercent,
		uint64 rebatePercent
	);

	/**
	 * @dev Emitted when an aggregator refunds a transaction.
	 * @param fee The fee deducted from the refund amount.
	 * @param orderId The ID of the order.
	 */
	event OrderRefunded(uint256 fee, bytes32 indexed orderId);

	/**
	 * @dev Emitted when the sender's fee is transferred.
	 * @param sender The address of the sender.
	 * @param amount The amount of the fee transferred.
	 */
	event SenderFeeTransferred(address indexed sender, uint256 indexed amount);

	/**
	 * @dev Emitted when a local transfer fee is split.
	 * @param orderId The ID of the order.
	 * @param senderAmount The amount that goes to the sender.
	 * @param providerAmount The amount that goes to the provider.
	 * @param aggregatorAmount The amount that goes to the aggregator.
	 */
	event LocalTransferFeeSplit(
		bytes32 indexed orderId,
		uint256 senderAmount,
		uint256 providerAmount,
		uint256 aggregatorAmount
	);

	/**
	 * @dev Emitted when an FX transfer fee is split.
	 * @param orderId The ID of the order.
	 * @param senderAmount The amount that goes to the sender.
	 * @param aggregatorAmount The amount that goes to the aggregator.
	 */
	event FxTransferFeeSplit(
		bytes32 indexed orderId,
		uint256 senderAmount,
		uint256 aggregatorAmount
	);

	/* ##################################################################
                                STRUCTS
    ################################################################## */
	/**
	 * @dev Struct representing an order.
	 * @param sender The address of the sender.
	 * @param token The address of the token.
	 * @param senderFeeRecipient The address of the sender fee recipient.
	 * @param senderFee The fee to be paid to the sender fee recipient.
	 * @param protocolFee The protocol fee to be paid.
	 * @param isFulfilled Whether the order is fulfilled.
	 * @param isRefunded Whether the order is refunded.
	 * @param refundAddress The address to which the refund is made.
	 * @param currentBPS The current basis points.
	 * @param amount The amount of the order.
	 */
	struct Order {
		address sender;
		address token;
		address senderFeeRecipient;
		uint256 senderFee;
		uint256 protocolFee;
		bool isFulfilled;
		bool isRefunded;
		address refundAddress;
		uint96 currentBPS;
		uint256 amount;
	}

	/* ##################################################################
                                EXTERNAL CALLS
    ################################################################## */
	/**
	 * @notice Locks the sender's amount of token into Gateway.
	 * @dev Requirements:
	 * - `msg.sender` must approve Gateway contract on `_token` of at least `amount` before function call.
	 * - `_token` must be an acceptable token. See {isTokenSupported}.
	 * - `amount` must be greater than minimum.
	 * - `_refundAddress` refund address must not be zero address.
	 * @param _token The address of the token.
	 * @param _amount The amount in the decimal of `_token` to be locked.
	 * @param _rate The rate at which the sender intends to sell `_amount` of `_token`.
	 * @param _senderFeeRecipient The address that will receive `_senderFee` in `_token`.
	 * @param _senderFee The amount in the decimal of `_token` that will be paid to `_senderFeeRecipient`.
	 * @param _refundAddress The address that will receive `_amount` in `_token` when there is a need to refund.
	 * @param messageHash The hash of the message.
	 * @return _orderId The ID of the order.
	 */
	function createOrder(
		address _token,
		uint256 _amount,
		uint96 _rate,
		address _senderFeeRecipient,
		uint256 _senderFee,
		address _refundAddress,
		string calldata messageHash
	) external returns (bytes32 _orderId);

	/**
	 * @notice Settles a transaction and distributes rewards accordingly.
	 * @param _splitOrderId The ID of the split order.
	 * @param _orderId The ID of the transaction.
	 * @param _liquidityProvider The address of the liquidity provider.
	 * @param _settlePercent The rate at which the transaction is settled.
	 * @param _rebatePercent The percentage of the aggregator fee that is given back to the provider.
	 * @return bool the settlement is successful.
	 */
	function settle(
		bytes32 _splitOrderId,
		bytes32 _orderId,
		address _liquidityProvider,
		uint64 _settlePercent,
		uint64 _rebatePercent
	) external returns (bool);

	/**
	 * @notice Refunds to the specified refundable address.
	 * @dev Requirements:
	 * - Only aggregators can call this function.
	 * @param _fee The amount to be deducted from the amount to be refunded.
	 * @param _orderId The ID of the transaction.
	 * @return bool the refund is successful.
	 */
	function refund(uint256 _fee, bytes32 _orderId) external returns (bool);

	/**
	 * @notice Checks if a token is supported by Gateway.
	 * @param _token The address of the token to check.
	 * @return bool the token is supported.
	 */
	function isTokenSupported(address _token) external view returns (bool);

	/**
	 * @notice Gets the details of an order.
	 * @param _orderId The ID of the order.
	 * @return Order The order details.
	 */
	function getOrderInfo(bytes32 _orderId) external view returns (Order memory);
}

Read Contract

getOrderInfo 0x768c6ec0 → tuple
getTokenFeeSettings 0x8bfa0549 → tuple
isTokenSupported 0x75151b63 → bool
owner 0x8da5cb5b → address
paused 0x5c975abb → bool
pendingOwner 0xe30c3978 → address

Write Contract 12 functions

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

acceptOwnership 0x79ba5097
No parameters
createOrder 0x809804f7
address _token
uint256 _amount
uint96 _rate
address _senderFeeRecipient
uint256 _senderFee
address _refundAddress
string messageHash
returns: bytes32
initialize 0x8129fc1c
No parameters
pause 0x8456cb59
No parameters
refund 0x71eedb88
uint256 _fee
bytes32 _orderId
returns: bool
renounceOwnership 0x715018a6
No parameters
setTokenFeeSettings 0x898861b0
address token
uint256 senderToProvider
uint256 providerToAggregator
uint256 senderToAggregator
uint256 providerToAggregatorFx
settingManagerBool 0xcd992400
bytes32 what
address value
uint256 status
settle 0xdf51b359
bytes32 _splitOrderId
bytes32 _orderId
address _liquidityProvider
uint64 _settlePercent
uint64 _rebatePercent
returns: bool
transferOwnership 0xf2fde38b
address newOwner
unpause 0x3f4ba83a
No parameters
updateProtocolAddress 0x40ebc677
bytes32 what
address value

Recent Transactions

No transactions found for this address