Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x1Fa7Cb4925086128f3bb9e26761C9C75dbAC3CD1
Balance 0 ETH
Nonce 1
Code Size 12369 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

12369 bytes
0x6080604052600436106101d15760003560e01c80638f4e4bd2116100f7578063b9713eb311610095578063d985e8fb11610064578063d985e8fb14610564578063f2d63826146105ac578063f2fde38b146105ca578063f5d0fc4b146105ea57600080fd5b8063b9713eb3146104d1578063c9fd874d14610504578063ce9e014a14610524578063d58cc07a1461054457600080fd5b8063a576b5da116100d1578063a576b5da1461044b578063a734f06e1461046b578063ab335bfc14610493578063b88daae9146104b157600080fd5b80638f4e4bd2146103da5780639e65075b146103f8578063a154a8b81461041857600080fd5b8063472d35b91161016f578063642a42181161013e578063642a421814610347578063715018a6146103655780638b8fbd921461037a5780638da5cb5b146103bc57600080fd5b8063472d35b9146102cb57806348f63f33146102eb57806351dc8b891461030b5780636284e4c41461032957600080fd5b8063278135f7116101ab578063278135f71461024f578063391146511461026f578063391e8ef61461028d5780633cac07ee146102ab57600080fd5b806309d5e989146101dd578063142989b6146101ff578063277dd70f1461023c57600080fd5b366101d857005b600080fd5b3480156101e957600080fd5b506101fd6101f8366004612bad565b61060a565b005b34801561020b57600080fd5b5060095461021f906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6101fd61024a366004612c13565b610641565b34801561025b57600080fd5b506101fd61026a366004612bad565b610967565b34801561027b57600080fd5b506005546001600160a01b031661021f565b34801561029957600080fd5b506002546001600160a01b031661021f565b3480156102b757600080fd5b506101fd6102c6366004612bad565b6109aa565b3480156102d757600080fd5b506101fd6102e6366004612bad565b6109ee565b3480156102f757600080fd5b506101fd610306366004612bad565b610a32565b34801561031757600080fd5b506003546001600160a01b031661021f565b34801561033557600080fd5b506008546001600160a01b031661021f565b34801561035357600080fd5b506009546001600160a01b031661021f565b34801561037157600080fd5b506101fd610a76565b34801561038657600080fd5b506103ae7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181565b604051908152602001610233565b3480156103c857600080fd5b506000546001600160a01b031661021f565b3480156103e657600080fd5b506006546001600160a01b031661021f565b34801561040457600080fd5b506101fd610413366004612c6e565b610a8a565b34801561042457600080fd5b507f00000000000000000000000054c375f28ce4b0c2b986d6256e4bc75d242a879361021f565b34801561045757600080fd5b506101fd610466366004612bad565b610d6f565b34801561047757600080fd5b5061021f73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b34801561049f57600080fd5b506004546001600160a01b031661021f565b3480156104bd57600080fd5b506101fd6104cc366004612d56565b610db3565b3480156104dd57600080fd5b507f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f61021f565b34801561051057600080fd5b506101fd61051f366004612d82565b610f88565b34801561053057600080fd5b506101fd61053f366004612bad565b61129d565b34801561055057600080fd5b506101fd61055f366004612dc5565b6112e1565b34801561057057600080fd5b506103ae6040805173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6020820152016040516020818303038152906040528051906020012081565b3480156105b857600080fd5b506007546001600160a01b031661021f565b3480156105d657600080fd5b506101fd6105e5366004612bad565b61155c565b3480156105f657600080fd5b506101fd610605366004612bad565b6115b0565b6106126115f4565b6009805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b3361064b8161163a565b8360200135610659816116ff565b6009546040517ff190e42700000000000000000000000000000000000000000000000000000000815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60048201526000916001600160a01b03169063f190e42790602401602060405180830381865afa1580156106cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106f39190612e07565b90506001600160a01b038116610735576040517f9eab8abf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c08101825233815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee602080830191909152349282018390528835606083015288013560808201526001600160a01b03831660a08201526107d19087908790610796906117d7565b6040518060400160405280600b81526020017f7a6b4c6f636b41737365740000000000000000000000000000000000000000008152506118f6565b6107de8760200135611a7b565b6005546040516000916001600160a01b03169083908381818185875af1925050503d806000811461082b576040519150601f19603f3d011682016040523d82523d6000602084013e610830565b606091505b50509050806108a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f6465706f7369744554483a207472616e73666572206661696c6564000000000060448201526064015b60405180910390fd5b60006108dc6040518060800160405280866001600160a01b031681526020018581526020018b6000013581526020018b60200135815250611b0b565b6040805133815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6020808301919091526001600160a01b0388168284015260608201879052600060808301528c013560a082015260c0810183905290519192507f6b7997bf71cdc7f24588bf376e3385af05308f33e538edbe61c4349a1de53c19919081900360e00190a1505050505050505050565b61096f6115f4565b6001600160a01b038116156109a7576005805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383161790555b50565b6109b26115f4565b6001600160a01b038116156109a757600480546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b6109f66115f4565b6001600160a01b038116156109a757600780546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b610a3a6115f4565b6001600160a01b038116156109a757600280546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b610a7e6115f4565b610a886000611bc7565b565b8260600151610a9881611c24565b610aa181611cfc565b8351610aac81611dd4565b610ab581611e91565b8460c00151610ac3816116ff565b42610acd81611ed3565b8660400151610adb81611f93565b60095460808901516040517f510ae6150000000000000000000000000000000000000000000000000000000081526001600160a01b039182166004820152600092919091169063510ae61590602401602060405180830381865afa158015610b47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6b9190612e07565b90506001600160a01b038116610bad576040517f9eab8abf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c5b8888610c206040518060e001604052808e6040015181526020018e608001516001600160a01b031681526020018e60a0015181526020018e6060015181526020018e600001516001600160a01b031681526020018e60c001518152602001866001600160a01b031681525061206b565b6040518060400160405280600c81526020017f7a6b556e6c6f636b4e6f746500000000000000000000000000000000000000008152506118f6565b610c688960c00151611a7b565b610c75896060015161219e565b6000610c91828b600001518c60a001518d602001516000612204565b90506000610ce26040518060a001604052808d60a0015181526020018481526020018d60c0015181526020018d608001516001600160a01b03168152602001856001600160a01b031681525061253d565b90507f17cb49f0eba4135a7939d60517a92eddadf8f25c30781b26004e20e82585a65f8b6080015184848e606001518f60c0015186604051610d5a969594939291906001600160a01b03968716815294909516602085015260408401929092526060830152608082015260a081019190915260c00190565b60405180910390a15050505050505050505050565b610d776115f4565b6001600160a01b038116156109a757600880546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b610dbb6115f4565b60008111610e4b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f4261736541737365744d616e616765723a20616d6f756e74206d75737420626560448201527f2067726561746572207468616e203000000000000000000000000000000000006064820152608401610897565b6001600160a01b0382161580610e7d57506001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b15610f6a576005546040516000916001600160a01b03169083908381818185875af1925050503d8060008114610ecf576040519150601f19603f3d011682016040523d82523d6000602084013e610ed4565b606091505b5050905080610f65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4261736541737365744d616e616765723a204661696c656420746f2073656e6460448201527f20457468657200000000000000000000000000000000000000000000000000006064820152608401610897565b505050565b600354610f84906001600160a01b038481169116836125ef565b5050565b8260600135610f9681611c24565b610f9f81611cfc565b610faf6040850160208601612bad565b610fb881611dd4565b610fc181611e91565b8460c00135610fcf816116ff565b8560400135610fdd81611f93565b6009546000906001600160a01b031663f190e427610ffe60208b018b612bad565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa15801561105a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061107e9190612e07565b90506001600160a01b0381166110c0576040517f9eab8abf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61118a878761114f6040518060e001604052808d6040013581526020018d60000160208101906110f09190612bad565b6001600160a01b031681526020018d6080013581526020018d6060013581526020018d60200160208101906111259190612bad565b6001600160a01b031681526020018d60c001358152602001866001600160a01b031681525061206b565b6040518060400160405280600a81526020017f7a6b4c6f636b4e6f7465000000000000000000000000000000000000000000008152506118f6565b6111978860c00135611a7b565b6111a4886060013561219e565b60006111d76111b660208b018b612bad565b6111c660408c0160208d01612bad565b8b608001358c60a001356000612204565b905060006112146040518060800160405280856001600160a01b031681526020018481526020016000801b81526020018c60c00135815250611b0b565b90507f6b7997bf71cdc7f24588bf376e3385af05308f33e538edbe61c4349a1de53c19600061124660208d018d612bad565b604080516001600160a01b03938416815291831660208301529186168183015260608082018690528d0135608082015260c0808e013560a0830152810184905290519081900360e00190a150505050505050505050565b6112a56115f4565b6001600160a01b038116156109a757600680546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b336112eb8161163a565b83606001356112f9816116ff565b6009546000906001600160a01b031663f190e42761131a6020890189612bad565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015611376573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139a9190612e07565b90506001600160a01b0381166113dc576040517f9eab8abf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61144f85856107966040518060c00160405280336001600160a01b031681526020018b60000160208101906114119190612bad565b6001600160a01b031681526020018b6020013581526020018b6040013581526020018b606001358152602001866001600160a01b03168152506117d7565b61145c8660600135611a7b565b6003546114909033906001600160a01b03166020890180359061147f908b612bad565b6001600160a01b0316929190612663565b60006114d06040518060800160405280846001600160a01b0316815260200189602001358152602001896040013581526020018960600135815250611b0b565b90507f6b7997bf71cdc7f24588bf376e3385af05308f33e538edbe61c4349a1de53c193361150160208a018a612bad565b604080516001600160a01b03938416815291831660208381019190915292861690820152908901356060828101919091526000608083015289013560a082015260c0810183905260e00160405180910390a150505050505050565b6115646115f4565b6001600160a01b0381166115a7576040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260006004820152602401610897565b6109a781611bc7565b6115b86115f4565b6001600160a01b038116156109a757600380546001600160a01b03831673ffffffffffffffffffffffffffffffffffffffff1990911617905550565b6000546001600160a01b03163314610a88576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610897565b6008546040517f65e4ad9e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152909116906365e4ad9e906044016020604051808303816000875af11580156116a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116c99190612e24565b6109a7576040517ff8a233f300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f63b43fcb000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f6001600160a01b0316906363b43fcb90602401602060405180830381865afa15801561177d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117a19190612e24565b6109a7576040517fc869abd500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051600680825260e08201909252606091600091906020820160c08036833701905050905061180b83600001516126a2565b8160008151811061181e5761181e612e46565b60200260200101818152505061183783602001516126a2565b8160018151811061184a5761184a612e46565b602002602001018181525050826040015160001b8160028151811061187157611871612e46565b60200260200101818152505082606001518160038151811061189557611895612e46565b6020026020010181815250508260800151816004815181106118b9576118b9612e46565b6020026020010181815250506118d28360a001516126a2565b816005815181106118e5576118e5612e46565b602090810291909101015292915050565b6002546040517f3de955440000000000000000000000000000000000000000000000000000000081526000916001600160a01b031690633de9554490611940908590600401612e99565b602060405180830381865afa15801561195d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119819190612e07565b6040517fea50d0e40000000000000000000000000000000000000000000000000000000081529091506001600160a01b0382169063ea50d0e4906119cd90889088908890600401612ecc565b602060405180830381865afa1580156119ea573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a0e9190612e24565b611a74576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e76616c69642070726f6f66000000000000000000000000000000000000006044820152606401610897565b5050505050565b6040517fc0dd0da5000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f6001600160a01b03169063c0dd0da5906024015b600060405180830381600087803b158015611af757600080fd5b505af1158015611a74573d6000803e3d6000fd5b60408101516000819003611b3357611b30826000015183602001518460600151612709565b90505b611b3c81612722565b815160035460208401516040517f40c10f190000000000000000000000000000000000000000000000000000000081526001600160a01b03928316600482015260248101919091529116906340c10f1990604401600060405180830381600087803b158015611baa57600080fd5b505af1158015611bbe573d6000803e3d6000fd5b50505050919050565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040517f5c77480e000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f6001600160a01b031690635c77480e90602401602060405180830381865afa158015611ca2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cc69190612e24565b6109a7576040517f672c107500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f2dee3016000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f6001600160a01b031690632dee301690602401602060405180830381865afa158015611d7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9e9190612e24565b6109a7576040517f9281498500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546040517ffb04c9390000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301529091169063fb04c93990602401602060405180830381865afa158015611e37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e5b9190612e24565b6109a7576040517f57bb83ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336001600160a01b038216146109a7576040517f688a40f900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6009546040517feb906c0d000000000000000000000000000000000000000000000000000000008152600481018390526000916001600160a01b03169063eb906c0d90602401602060405180830381865afa158015611f36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f5a9190612e24565b905080610f84576040517f60b0d7a600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f83402ee4000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f6001600160a01b0316906383402ee490602401602060405180830381865afa158015612011573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120359190612e24565b6109a7576040517fdff6282700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160078082526101008201909252606091600091906020820160e0803683370190505090508260000151816000815181106120ab576120ab612e46565b6020026020010181815250506120c483602001516126a2565b816001815181106120d7576120d7612e46565b602002602001018181525050826040015160001b816002815181106120fe576120fe612e46565b60200260200101818152505082606001518160038151811061212257612122612e46565b60200260200101818152505061213b83608001516126a2565b8160048151811061214e5761214e612e46565b6020026020010181815250508260a001518160058151811061217257612172612e46565b60200260200101818152505061218b8360c001516126a2565b816006815181106118e5576118e5612e46565b6040517f1d9a74e7000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f6001600160a01b031690631d9a74e790602401611add565b6007546040517fcf0737e90000000000000000000000000000000000000000000000000000000081526004810185905260248101849052604481018390526000918291829182916001600160a01b039091169063cf0737e990606401606060405180830381865afa15801561227d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122a19190612f45565b9194509250905081156123ee577fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b038a1601612364576005546007546040517f0357371d0000000000000000000000000000000000000000000000000000000081526001600160a01b03918216600482015260248101859052911690630357371d90604401600060405180830381600087803b15801561234757600080fd5b505af115801561235b573d6000803e3d6000fd5b505050506123ee565b6003546007546040517f8bfb07c90000000000000000000000000000000000000000000000000000000081526001600160a01b038c81166004830152918216602482015260448101859052911690638bfb07c990606401600060405180830381600087803b1580156123d557600080fd5b505af11580156123e9573d6000803e3d6000fd5b505050505b8015612530577fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b038a16016124a8576005546040517f0357371d0000000000000000000000000000000000000000000000000000000081526001600160a01b038a811660048301526024820184905290911690630357371d90604401600060405180830381600087803b15801561248b57600080fd5b505af115801561249f573d6000803e3d6000fd5b50505050612530565b6003546040517f8bfb07c90000000000000000000000000000000000000000000000000000000081526001600160a01b038b811660048301528a811660248301526044820184905290911690638bfb07c990606401600060405180830381600087803b15801561251757600080fd5b505af115801561252b573d6000803e3d6000fd5b505050505b5090979650505050505050565b600080612557836080015184602001518560400151612709565b905061256281612722565b606083015160035484516040517f9dc29fac0000000000000000000000000000000000000000000000000000000081526001600160a01b0392831660048201526024810191909152911690639dc29fac90604401600060405180830381600087803b1580156125d057600080fd5b505af11580156125e4573d6000803e3d6000fd5b509295945050505050565b6040516001600160a01b03838116602483015260448201839052610f6591859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061281b565b6040516001600160a01b03848116602483015283811660448301526064820183905261269c9186918216906323b872dd9060840161261c565b50505050565b604080516001600160a01b03831660208201526000916003910160408051601f19818403018152908290526126d691612f73565b602060405180830381855afa1580156126f3573d6000803e3d6000fd5b5050604051516001600160a01b03169392505050565b60006127188484846000612897565b90505b9392505050565b6040517f6ae987ed000000000000000000000000000000000000000000000000000000008152600481018290527f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f6001600160a01b031690636ae987ed90602401600060405180830381600087803b15801561279d57600080fd5b505af11580156127b1573d6000803e3d6000fd5b50506040517fb0fb374d000000000000000000000000000000000000000000000000000000008152600481018490527f000000000000000000000000152f1051c8d37fba9a362fc9b32a0eef8496202f6001600160a01b0316925063b0fb374d9150602401611add565b60006128306001600160a01b03841683612a5e565b905080516000141580156128555750808060200190518101906128539190612e24565b155b15610f65576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610897565b60006001600160a01b03851615806128ad575083155b806128b6575082155b156128ed576040517fdb61814600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051600480825260a082019092526000916020820160808036833701905050905082600181111561292257612922612f8f565b8160008151811061293557612935612e46565b60200260200101818152505061294a866126a2565b60001c8160018151811061296057612960612e46565b602002602001018181525050848160028151811061298057612980612e46565b6020026020010181815250508360001c816003815181106129a3576129a3612e46565b60209081029190910101526040517f3badda130000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000054c375f28ce4b0c2b986d6256e4bc75d242a87931690633badda1390612a13908490600401612fbe565b602060405180830381865afa158015612a30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a549190613002565b9695505050505050565b606061271b8383600084600080856001600160a01b03168486604051612a849190612f73565b60006040518083038185875af1925050503d8060008114612ac1576040519150601f19603f3d011682016040523d82523d6000602084013e612ac6565b606091505b5091509150612a54868383606082612ae657612ae182612b46565b61271b565b8151158015612afd57506001600160a01b0384163b155b15612b3f576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610897565b508061271b565b805115612b565780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03811681146109a757600080fd5b8035612ba881612b88565b919050565b600060208284031215612bbf57600080fd5b813561271b81612b88565b60008083601f840112612bdc57600080fd5b50813567ffffffffffffffff811115612bf457600080fd5b602083019150836020828501011115612c0c57600080fd5b9250929050565b60008060008385036060811215612c2957600080fd5b6040811215612c3757600080fd5b50839250604084013567ffffffffffffffff811115612c5557600080fd5b612c6186828701612bca565b9497909650939450505050565b6000806000838503610100811215612c8557600080fd5b60e0811215612c9357600080fd5b5060405160e0810167ffffffffffffffff8282108183111715612cdf577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b81604052612cec87612b9d565b8352602087013560208401526040870135604084015260608701356060840152612d1860808801612b9d565b608084015260a087013560a084015260c087013560c084015282955060e0870135925080831115612d4857600080fd5b5050612c6186828701612bca565b60008060408385031215612d6957600080fd5b8235612d7481612b88565b946020939093013593505050565b6000806000838503610100811215612d9957600080fd5b60e0811215612da757600080fd5b5083925060e084013567ffffffffffffffff811115612c5557600080fd5b600080600083850360a0811215612ddb57600080fd5b6080811215612de957600080fd5b50839250608084013567ffffffffffffffff811115612c5557600080fd5b600060208284031215612e1957600080fd5b815161271b81612b88565b600060208284031215612e3657600080fd5b8151801515811461271b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60005b83811015612e90578181015183820152602001612e78565b50506000910152565b6020815260008251806020840152612eb8816040850160208701612e75565b601f01601f19169190910160400192915050565b60408152826040820152828460608301376000606084830101526000601f19601f8501168201606081016020606085840301818601528186518084526080850191508288019450600093505b80841015612f385784518252938201936001939093019290820190612f18565b5098975050505050505050565b600080600060608486031215612f5a57600080fd5b8351925060208401519150604084015190509250925092565b60008251612f85818460208701612e75565b9190910192915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020808252825182820181905260009190848201906040850190845b81811015612ff657835183529284019291840191600101612fda565b50909695505050505050565b60006020828403121561301457600080fd5b505191905056fea2646970667358221220618548c48a5a55269769833a636d4efae03e6eb3e5125faebd6fd66a486cecbd64736f6c63430008140033

Verified Source Code Full Match

Compiler: v0.8.20+commit.a1b79de6 EVM: paris Optimization: Yes (4000 runs)
IMimc254.sol 27 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;


interface IMimc254 {
    enum NoteDomainSeparator {
        FUNGIBLE,
        NON_FUNGIBLE
    }

    function mimcBn254(uint256[] memory array) external view returns (uint256);

    /*function mimcBn254ForNote(
        uint256[3] memory array,
        NoteDomainSeparator domainSeparator
    ) external view returns (uint256);

    function mimcBn254ForTree(
        uint256[3] memory _array
    ) external view returns (uint256);

    function mimcBn254ForRoute(
        uint256[12] memory _array
    ) external view returns (uint256);*/
    
}
IVerifier.sol 10 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;

interface IVerifier {
    function verify(
        bytes calldata _proof,
        bytes32[] calldata _publicInputs
    ) external view returns (bool);
}
BaseAssetManager.sol 392 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IAssetPool} from "../interfaces/IAssetPool.sol";
import {IVerifier} from "../interfaces/IVerifier.sol";
import {IVerifierHub} from "../interfaces/IVerifierHub.sol";
import {IRelayerHub} from "../interfaces/IRelayerHub.sol";
import {IFeeManager} from "../interfaces/IFeeManager.sol";
import {IComplianceManager} from "../interfaces/IComplianceManager.sol";
import {IMerkleTreeOperator} from "../interfaces/IMerkleTreeOperator.sol";
import {IMimc254} from "../interfaces/IMimc254.sol";
import {BaseInputBuilder} from "./BaseInputBuilder.sol";

/**
 * @title BaseAssetManager
 * @dev Base contract for asset managers.
 */
abstract contract BaseAssetManager is Ownable, BaseInputBuilder {
    using SafeERC20 for IERC20;

    struct FundReleaseDetails {
        address assetAddress;
        address payable recipient;
        address payable relayer;
        uint256 relayerGasFee;
        uint256 amount;
    }

    IVerifierHub internal _verifierHub;
    IAssetPool internal _assetPoolERC20;
    IAssetPool internal _assetPoolERC721;
    IAssetPool internal _assetPoolETH;
    IRelayerHub internal _relayerHub;
    IFeeManager internal _feeManager;
    IComplianceManager internal _complianceManager;
    IMerkleTreeOperator internal immutable _merkleTreeOperator;
    IMimc254 internal immutable _mimc254;

    address public constant ETH_ADDRESS =
        0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    bytes32 public constant ASSET_ETH = keccak256(abi.encode(ETH_ADDRESS));

    uint256 public constant P =
        21888242871839275222246405745257275088548364400416034343698204186575808495617;

    error RelayerNotRegistered();
    error NullifierUsed();
    error NullifierLocked();
    error MerkleRootNotAllowed();
    error NoteFooterUsed();
    error NoteAlreadyCreated();
    error InvalidNoteParameters();
    error ZeroAddress();
    error NoteFooterDuplicated();
    error RelayerMismatch();

    // we dont use it for now
    modifier onlyETHAssetPool() {
        require(
            msg.sender == address(_assetPoolETH),
            "BaseAssetManager: Only ETH Asset Pool"
        );
        _;
    }

    constructor(
        address assetPoolERC20,
        address assetPoolERC721,
        address assetPoolETH,
        address verifierHub,
        address relayerHub,
        address feeManager,
        address complianceManager,
        address merkleTreeOperator,
        address mimc254,
        address initialOwner
    ) Ownable(initialOwner) {
        if (
            assetPoolERC20 == address(0) ||
            assetPoolERC721 == address(0) ||
            assetPoolETH == address(0) ||
            verifierHub == address(0) ||
            relayerHub == address(0) ||
            feeManager == address(0) ||
            complianceManager == address(0) ||
            merkleTreeOperator == address(0) ||
            mimc254 == address(0) ||
            initialOwner == address(0)
        ) {
            revert ZeroAddress();
        }
        _assetPoolERC20 = IAssetPool(assetPoolERC20);
        _assetPoolERC721 = IAssetPool(assetPoolERC721);
        _assetPoolETH = IAssetPool(assetPoolETH);
        _verifierHub = IVerifierHub(verifierHub);
        _relayerHub = IRelayerHub(relayerHub);
        _feeManager = IFeeManager(feeManager);
        _complianceManager = IComplianceManager(complianceManager);
        _merkleTreeOperator = IMerkleTreeOperator(merkleTreeOperator);
        _mimc254 = IMimc254(mimc254);
    }

    receive() external payable {}

    /**
     * @dev Transfers the asset to the asset pool if there are
     *      any remaining assets due to network failures.
     */
    function releaseToAsssetPool(
        address asset,
        uint256 amount
    ) external onlyOwner {
        require(amount > 0, "BaseAssetManager: amount must be greater than 0");
        if (asset == address(0) || asset == ETH_ADDRESS) {
            (bool success, ) = address(_assetPoolETH).call{value: amount}("");
            require(success, "BaseAssetManager: Failed to send Ether");
        } else {
            IERC20(asset).safeTransfer(address(_assetPoolERC20), amount);
        }
    }

    function setAssetPoolERC20(address assetPoolERC20) public onlyOwner {
        if (assetPoolERC20 != address(0)) {
            _assetPoolERC20 = IAssetPool(assetPoolERC20);
        }
    }

    function setAssetPoolERC721(address assetPoolERC721) public onlyOwner {
        if (assetPoolERC721 != address(0)) {
            _assetPoolERC721 = IAssetPool(assetPoolERC721);
        }
    }

    function setAssetPoolETH(address assetPoolETH) public onlyOwner {
        if (assetPoolETH != address(0)) {
            _assetPoolETH = IAssetPool(assetPoolETH);
        }
    }

    function setVerifierHub(address verifierHub) public onlyOwner {
        if (verifierHub != address(0)) {
            _verifierHub = IVerifierHub(verifierHub);
        }
    }

    function setRelayerHub(address relayerHub) public onlyOwner {
        if (relayerHub != address(0)) {
            _relayerHub = IRelayerHub(relayerHub);
        }
    }

    function setFeeManager(address feeManager) public onlyOwner {
        if (feeManager != address(0)) {
            _feeManager = IFeeManager(feeManager);
        }
    }

    function setComplianceManager(address complianceManager) public onlyOwner {
        if (complianceManager != address(0)) {
            _complianceManager = IComplianceManager(complianceManager);
        }
    }

    function getAssetPoolERC20() public view returns (address) {
        return address(_assetPoolERC20);
    }

    function getAssetPoolERC721() public view returns (address) {
        return address(_assetPoolERC721);
    }

    function getAssetPoolETH() public view returns (address) {
        return address(_assetPoolETH);
    }

    function getVerifierHub() public view returns (address) {
        return address(_verifierHub);
    }

    function getRelayerHub() public view returns (address) {
        return address(_relayerHub);
    }

    function getFeeManager() public view returns (address) {
        return address(_feeManager);
    }

    function getComplianceManager() public view returns (address) {
        return address(_complianceManager);
    }

    function getMerkleTreeOperator() public view returns (address) {
        return address(_merkleTreeOperator);
    }

    function getMimc254() public view returns (address) {
        return address(_mimc254);
    }

    function _postDeposit(bytes32 _noteCommitment) internal {
        _merkleTreeOperator.setNoteCommitmentCreated(_noteCommitment);
        _merkleTreeOperator.appendMerkleLeaf(bytes32(_noteCommitment));
    }

    function _postWithdraw(bytes32 _nullifier) internal {
        _merkleTreeOperator.setNullifierUsed(_nullifier);
    }

    function _setNullifierLock(bytes32 _nullifier, bool _locked) internal {
        _merkleTreeOperator.setNullifierLocked(_nullifier, _locked);
    }

    function _registerNoteFooter(bytes32 _noteFooter) internal {
        _merkleTreeOperator.setNoteFooterUsed(_noteFooter);
    }

    function _releaseERC20WithFee(
        address _asset,
        address _to,
        address _relayer,
        uint256 _relayerGasFee,
        uint256 _amount
    ) internal returns (uint256, uint256, uint256) {
        (
            uint256 actualAmount,
            uint256 serviceFee,
            uint256 relayerRefund
        ) = _feeManager.calculateFee(_amount, _relayerGasFee);

        _assetPoolERC20.release(_asset, _to, actualAmount);

        if (relayerRefund > 0) {
            _assetPoolERC20.release(_asset, _relayer, relayerRefund);
        }
        if (serviceFee > 0) {
            _assetPoolERC20.release(_asset, address(_feeManager), serviceFee);
        }

        return (actualAmount, serviceFee, relayerRefund);
    }

    function _releaseETHWithFee(
        address payable _to,
        address payable _relayer,
        uint256 _relayerGasFee,
        uint256 _amount
    ) internal returns (uint256, uint256, uint256) {
        (
            uint256 actualAmount,
            uint256 serviceFee,
            uint256 relayerRefund
        ) = _feeManager.calculateFee(_amount, _relayerGasFee);

        _assetPoolETH.release(_to, actualAmount);

        if (relayerRefund > 0) {
            _assetPoolETH.release(_relayer, relayerRefund);
        }
        if (serviceFee > 0) {
            _assetPoolETH.release(payable(address(_feeManager)), serviceFee);
        }

        return (actualAmount, serviceFee, relayerRefund);
    }

    function _releaseFunds(
        FundReleaseDetails memory details
    ) internal returns (uint256, uint256, uint256) {
        if (
            details.assetAddress == ETH_ADDRESS ||
            details.assetAddress == address(0)
        ) {
            return
                _releaseETHWithFee(
                    details.recipient,
                    details.relayer,
                    details.relayerGasFee,
                    details.amount
                );
        } else {
            return
                _releaseERC20WithFee(
                    details.assetAddress,
                    details.recipient,
                    details.relayer,
                    details.relayerGasFee,
                    details.amount
                );
        }
    }

    function _verifyProof(
        bytes calldata _proof,
        bytes32[] memory _inputs,
        string memory verifierType
    ) internal view {
        IVerifier verifier = _verifierHub.getVerifier(verifierType);
        require(verifier.verify(_proof, _inputs), "invalid proof");
    }

    function _buildNoteForERC20(
        address asset,
        uint256 amount,
        bytes32 noteFooter
    ) internal view returns (bytes32) {
        return
            _buildNote(
                asset,
                amount,
                noteFooter,
                IMimc254.NoteDomainSeparator.FUNGIBLE
            );
    }

    function _buildNoteForERC721(
        address asset,
        uint256 tokenId,
        bytes32 noteFooter
    ) internal view returns (bytes32) {
        return
            _buildNote(
                asset,
                tokenId,
                noteFooter,
                IMimc254.NoteDomainSeparator.NON_FUNGIBLE
            );
    }

    function _validateRelayerIsRegistered(address relayer) internal view {
        if (!_relayerHub.isRelayerRegistered(relayer)) {
            revert RelayerNotRegistered();
        }
    }

    function _validateNullifierIsNotUsed(bytes32 nullifier) internal view {
        if (!_merkleTreeOperator.nullifierIsNotUsed(nullifier)) {
            revert NullifierUsed();
        }
    }

    function _validateNullifierIsNotLocked(bytes32 nullifier) internal view {
        if (!_merkleTreeOperator.nullifierIsNotLocked(nullifier)) {
            revert NullifierLocked();
        }
    }

    function _validateMerkleRootIsAllowed(bytes32 merkleRoot) internal view {
        if (!_merkleTreeOperator.merkleRootIsAllowed(merkleRoot)) {
            revert MerkleRootNotAllowed();
        }
    }

    function _validateNoteFooterIsNotUsed(bytes32 noteFooter) internal view {
        if (!_merkleTreeOperator.noteFooterIsNotUsed(noteFooter)) {
            revert NoteFooterUsed();
        }
    }

    function _validateNoteIsNotCreated(bytes32 noteCommitment) internal view {
        if (!_merkleTreeOperator.noteIsNotCreated(noteCommitment)) {
            revert NoteAlreadyCreated();
        }
    }

    function _validateSenderIsRelayer(address relayer) internal view {
        if (msg.sender != relayer) {
            revert RelayerMismatch();
        }
    }

    function _buildNote(
        address asset,
        uint256 amount,
        bytes32 noteFooter,
        IMimc254.NoteDomainSeparator domainSeparator
    ) private view returns (bytes32) {
        if (asset == address(0) || amount == 0 || noteFooter == bytes32(0)) {
            revert InvalidNoteParameters();
        }
        uint256[] memory array = new uint256[](4);
        array[0] = uint256(domainSeparator);
        array[1] = uint256(_bytifyToNoir(asset));
        array[2] = amount;
        array[3] = uint256(noteFooter);
        return bytes32(_mimc254.mimcBn254(array));
    }
}
BaseInputBuilder.sol 19 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/**
 * @title BaseInputBuilder
 * @dev Base contract for ZK verify input builders.
 */
contract BaseInputBuilder {
    uint256 internal _primeField;

    constructor(uint256 primeField) {
        _primeField = primeField;
    }

    function _bytifyToNoir(address value) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(ripemd160(abi.encode(value)))));
    }
}
IAssetPool.sol 14 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;

interface IAssetPool {
    function setAssetManager(address assetManager,bool registered) external;

    function release(address tokenOrNft, address to, uint256 amountOrNftId) external;

    function release(address payable to, uint256 amount) external;

    function getAssetManagerRegistration( address assetManager) 
        external view returns(bool);
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) 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 FailedInnerCall();
        }
    }
}
Context.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
IFeeManager.sol 32 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;

interface IFeeManager {
    function calculateFee(
        uint256 amount,
        uint256 relayerRefund
    ) external view returns (uint256, uint256, uint256);

    function calculateFeeForceServiceFee(
        uint256 amount,
        uint256 relayerRefund,
        uint256 serviceFeePercent
    ) external pure returns (uint256, uint256, uint256);

    function calculateFee(
        uint256[4] calldata amount,
        uint256[4] calldata relayerRefund
    )
        external
        view
        returns (uint256[4] memory, uint256[4] memory, uint256[4] memory);

    function calculateFeeForFSN(
        uint256[4] calldata amount,
        uint256[4] calldata relayerRefund
    )
        external
        view
        returns (uint256[] memory, uint256[4] memory, uint256[4] memory);
}
IRelayerHub.sol 7 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;

interface IRelayerHub {
    function isRelayerRegistered(address _relayer) external view returns (bool);
}
StakingAssetManager.sol 467 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

import {IMerkleTreeOperator} from "../core/interfaces/IMerkleTreeOperator.sol";
import {IComplianceManager} from "../core/interfaces/IComplianceManager.sol";
import {IStakingOperator} from "./interfaces/IStakingOperator.sol";
import {IStakingAssetManager} from "./interfaces/IStakingAssetManager.sol";
import {IZKToken} from "./interfaces/IZKToken.sol";

import {BaseAssetManager} from "../core/base/BaseAssetManager.sol";
import {StakingInputBuilder} from "./StakingInputBuilder.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @title Staking Asset Manager Contract
/// @notice Manages staking assets and operations for the system
contract StakingAssetManager is
    BaseAssetManager,
    StakingInputBuilder,
    IStakingAssetManager
{
    using SafeERC20 for IERC20;

    /// @notice The staking operator instance
    IStakingOperator public stakingOperator;

    /// @notice Checks if the address is compliant
    /// @param subject The address to check
    modifier isCompliant(address subject) {
        _validateIsCompliant(subject);
        _;
    }

    /// @notice Checks if the note footer is not used
    /// @param noteFooter The note footer to check
    modifier noteFooterIsNotUsed(bytes32 noteFooter) {
        _validateNoteFooterIsNotUsed(noteFooter);
        _;
    }

    /// @notice Checks if the note is not created
    /// @param note The note to check
    modifier noteIsNotCreated(bytes32 note) {
        _validateNoteIsNotCreated(note);
        _;
    }

    /// @notice Checks if the unlock is allowed
    /// @param timestamp The timestamp to check
    modifier onlyWhenUnlockAvailable(uint256 timestamp) {
        _validateUnlockIsAllowed(timestamp);
        _;
    }

    /// @notice Combines checks for nullifier
    /// @param nullifier The nullifier to check
    modifier checkNullifier(bytes32 nullifier) {
        _validateNullifierIsNotLocked(nullifier);
        _validateNullifierIsNotUsed(nullifier);
        _;
    }

    /// @notice Combines checks for relayer
    /// @param relayer The relayer address to check
    modifier checkRelayer(address relayer) {
        _validateRelayerIsRegistered(relayer);
        _validateSenderIsRelayer(relayer);
        _;
    }

    /// @notice Checks for merkle root
    /// @param merkleRoot The merkle root to check
    modifier checkMerkleRoot(bytes32 merkleRoot) {
        _validateMerkleRootIsAllowed(merkleRoot);
        _;
    }

    /**
     * @notice Initializes the contract with required addresses
     * @param assetPoolERC20_ The address of the ERC20 asset pool
     * @param assetPoolERC721_ The address of the ERC721 asset pool
     * @param assetPoolETH_ The address of the ETH asset pool
     * @param verifierHub_ The address of the verifier hub
     * @param relayerHub_ The address of the relayer hub
     * @param feeManager_ The address of the fee manager
     * @param complianceManager_ The address of the compliance manager
     * @param merkleTreeOperator_ The address of the Merkle tree operator
     * @param mimc254_ The address of the MiMC hash function
     * @param initialOwner_ The address of the initial owner
     * @param stakingOperator_ The address of the staking operator
     */
    constructor(
        address assetPoolERC20_,
        address assetPoolERC721_,
        address assetPoolETH_,
        address verifierHub_,
        address relayerHub_,
        address feeManager_,
        address complianceManager_,
        address merkleTreeOperator_,
        address mimc254_,
        address initialOwner_,
        address stakingOperator_
    )
        BaseAssetManager(
            assetPoolERC20_,
            assetPoolERC721_,
            assetPoolETH_,
            verifierHub_,
            relayerHub_,
            feeManager_,
            complianceManager_,
            merkleTreeOperator_,
            mimc254_,
            initialOwner_
        )
        StakingInputBuilder(P)
    {
        stakingOperator = IStakingOperator(stakingOperator_);
    }

    function getStakingOperator() external view returns (address) {
        return address(stakingOperator);
    }

    function setStakingOperator(address stakingOperator_) external onlyOwner {
        stakingOperator = IStakingOperator(stakingOperator_);
    }

    /**
     * @notice Locks assets using existing note
     * @param args The lock note arguments
     * @param proof The proof for the lock operation
     */
    function lockNote(
        LockNoteArgs calldata args,
        bytes calldata proof
    )
        external
        checkNullifier(args.nullifier)
        checkRelayer(args.relayer)
        noteFooterIsNotUsed(args.zkNoteFooter)
        checkMerkleRoot(args.merkleRoot)
    {
        address zkToken = stakingOperator.getCollateralToken(args.asset);
        if (zkToken == address(0)) {
            revert CollateralTokenMissing();
        }

        _verifyProof(
            proof,
            _buildLockNoteInputs(
                LockNoteRawInputs(
                    args.merkleRoot,
                    args.asset,
                    args.amount,
                    args.nullifier,
                    args.relayer,
                    args.zkNoteFooter,
                    zkToken
                )
            ),
            "zkLockNote"
        );

        _registerNoteFooter(args.zkNoteFooter);
        _postWithdraw(args.nullifier);

        uint256 amountToLock = _forwardFees(
            args.asset,
            args.relayer,
            args.amount,
            args.relayerGasFee,
            0
        );

        bytes32 note = _lock(
            LockArgs(zkToken, amountToLock, 0, args.zkNoteFooter)
        );

        emit Locked(
            address(0),
            args.asset,
            zkToken,
            amountToLock,
            args.nullifier,
            args.zkNoteFooter,
            note
        );
    }

    function lockERC20(
        LockERC20Args calldata args,
        bytes calldata proof
    ) external isCompliant(msg.sender) noteFooterIsNotUsed(args.zkNoteFooter) {
        address zkToken = stakingOperator.getCollateralToken(args.asset);
        if (zkToken == address(0)) {
            revert CollateralTokenMissing();
        }

        _verifyProof(
            proof,
            _buildLockAssetInputs(
                LockAssetRawInputs(
                    msg.sender,
                    args.asset,
                    args.amount,
                    args.zkNoteCommitment,
                    args.zkNoteFooter,
                    zkToken
                )
            ),
            "zkLockAsset"
        );
        
        _registerNoteFooter(args.zkNoteFooter);

        IERC20(args.asset).safeTransferFrom(
            msg.sender,
            address(_assetPoolERC20),
            args.amount
        );

        bytes32 note = _lock(
            LockArgs(
                zkToken,
                args.amount,
                args.zkNoteCommitment,
                args.zkNoteFooter
            )
        );

        emit Locked(msg.sender, args.asset, zkToken, args.amount, 0, args.zkNoteFooter, note);
    }

    function lockETH(
        LockETHArgs calldata args,
        bytes calldata proof
    )
        external
        payable
        isCompliant(msg.sender)
        noteFooterIsNotUsed(args.zkNoteFooter)
    {
        address zkToken = stakingOperator.getCollateralToken(ETH_ADDRESS);
        if (zkToken == address(0)) {
            revert CollateralTokenMissing();
        }

        uint256 amount = msg.value;

        _verifyProof(
            proof,
            _buildLockAssetInputs(
                LockAssetRawInputs(
                    msg.sender,
                    ETH_ADDRESS,
                    amount,
                    args.zkNoteCommitment,
                    args.zkNoteFooter,
                    zkToken
                )
            ),
            "zkLockAsset"
        );

        _registerNoteFooter(args.zkNoteFooter);

        (bool success, ) = address(_assetPoolETH).call{value: amount}("");
        require(success, "depositETH: transfer failed");

        bytes32 note = _lock(
            LockArgs(zkToken, amount, args.zkNoteCommitment, args.zkNoteFooter)
        );

        emit Locked(msg.sender, ETH_ADDRESS, zkToken, amount, 0, args.zkNoteFooter, note);
    }

    /**
     * @notice Unlocks assets
     * @param args The unlock arguments
     * @param proof The proof for the unlock operation
     */
    function unlock(
        UnlockNoteArgs memory args,
        bytes calldata proof
    )
        external
        checkNullifier(args.zkNoteNullifier)
        checkRelayer(args.relayer)
        noteFooterIsNotUsed(args.outNoteFooter)
        onlyWhenUnlockAvailable(block.timestamp)
        checkMerkleRoot(args.merkleRoot)
    {
        address originalToken = stakingOperator.getOriginalToken(
            args.zkNoteAsset
        );
        if (originalToken == address(0)) {
            revert CollateralTokenMissing();
        }

        _verifyProof(
            proof,
            _buildUnlockNoteInputs(
                UnlockNoteRawInputs(
                    args.merkleRoot,
                    args.zkNoteAsset,
                    args.zkNoteAmount,
                    args.zkNoteNullifier,
                    args.relayer,
                    args.outNoteFooter,
                    originalToken
                )
            ),
            "zkUnlockNote"
        );
        _registerNoteFooter(args.outNoteFooter);
        _postWithdraw(args.zkNoteNullifier);


        uint256 amountToUnlock = _forwardFees(
            originalToken,
            args.relayer,
            args.zkNoteAmount,
            args.relayerGasFee,
            0
        );

        bytes32 note = _unlock(
            UnlockArgs(
                args.zkNoteAmount,
                amountToUnlock,
                //args.zkNoteNullifier,
                args.outNoteFooter,
                args.zkNoteAsset,
                originalToken
            )
        );

        emit Unlocked(
            args.zkNoteAsset,
            originalToken,
            amountToUnlock,
            args.zkNoteNullifier,
            args.outNoteFooter,
            note
        );
    }

    struct LockArgs {
        address asset;
        uint256 amount;
        bytes32 zkNoteCommitment;
        bytes32 zkNoteFooter;
    }

    /**
     * @notice Internal function to lock assets
     * @param args The lock parameters
     * @return note The note commitment of the locked note
     */
    function _lock(LockArgs memory args) internal returns (bytes32 note) {
        note = args.zkNoteCommitment;

        if (note == 0) {
            note = _buildNoteForERC20(
                args.asset,
                args.amount,
                args.zkNoteFooter
            );
        }

        _postDeposit(note);

        IZKToken(args.asset).mint(address(_assetPoolERC20), args.amount);
    }

    struct UnlockArgs {
        uint256 fullAmount;
        uint256 amountToUnlock;
        //bytes32 zkNoteNullifier;
        bytes32 noteFooter;
        address zkToken;
        address originalToken;
    }

    /**
     * @notice Internal function to unlock assets
     * @param args The unlock parameters
     * @return The note of the unlocked asset
     */
    function _unlock(UnlockArgs memory args) internal returns (bytes32) {

        bytes32 note = _buildNoteForERC20(
            args.originalToken,
            args.amountToUnlock,
            args.noteFooter
        );

        _postDeposit(note);

        IZKToken(args.zkToken).burn(address(_assetPoolERC20), args.fullAmount);

        return note;
    }

    /**
     * @notice Internal function to validate if the address is compliant
     * @param subject The address to check
     */
    function _validateIsCompliant(address subject) internal {
        if (!_complianceManager.isAuthorized(address(this), subject)) {
            revert InvalidCompliance();
        }
    }

    function _validateUnlockIsAllowed(uint256 timestamp) internal view {
        bool isUnlockAllowed = stakingOperator.isUnlockAllowed(timestamp);
        if (!isUnlockAllowed) {
            revert UnlockNotAllowed();
        }
    }

    function _forwardFees(
        address asset,
        address relayer,
        uint256 amount,
        uint256 relayerGasFee,
        uint256 forceServiceFee
    ) internal returns (uint256) {
        (
            uint256 amountToLock,
            uint256 serviceFee,
            uint256 relayerRefund
        ) = _feeManager.calculateFeeForceServiceFee(
                amount,
                relayerGasFee,
                forceServiceFee
            );

        if (serviceFee > 0) {
            if (asset == ETH_ADDRESS) {
                _assetPoolETH.release(
                    payable(address(_feeManager)),
                    serviceFee
                );
            } else {
                _assetPoolERC20.release(
                    asset,
                    address(_feeManager),
                    serviceFee
                );
            }
        }

        if (relayerRefund > 0) {
            if (asset == ETH_ADDRESS) {
                _assetPoolETH.release(payable(relayer), relayerRefund);
            } else {
                _assetPoolERC20.release(asset, relayer, relayerRefund);
            }
        }

        return amountToLock;
    }
}
StakingInputBuilder.sol 129 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {BaseInputBuilder} from "../core/base/BaseInputBuilder.sol";

/// @title Staking Input Builder Contract
/// @notice Provides functionality to build inputs for staking operations
contract StakingInputBuilder is BaseInputBuilder {
    /// @notice Structure to hold raw inputs for lock operation
    /// @param merkleRoot The merkle root of the note
    /// @param inAsset The address of the asset being locked
    /// @param inAmount The amount of the asset being locked
    /// @param inNullifier The nullifier of the note
    /// @param relayer The address of the relayer
    /// @param outZkNoteFooter The footer of the note being created
    /// @param outZkAsset The address of the asset being created
    struct LockNoteRawInputs {
        bytes32 merkleRoot;
        address inAsset;
        uint256 inAmount;
        bytes32 inNullifier;
        address relayer;
        bytes32 outZkNoteFooter;
        address outZkAsset;
    }

    /// @notice Structure to hold raw inputs for lock asset operation
    /// @param owner The address of the asset owner
    /// @param asset The address of the asset being locked
    /// @param amount The amount of the asset being locked
    /// @param outZkNote The note being created
    /// @param outZkNoteFooter The footer of the note being created
    /// @param outZkAsset The address of the asset being created
    struct LockAssetRawInputs {
        address owner;
        address asset;
        uint256 amount;
        bytes32 outZkNote;
        bytes32 outZkNoteFooter;
        address outZkAsset;
    }

    /// @notice Structure to hold raw inputs for unlock operation
    /// @param merkleRoot The merkle root of the note
    /// @param inZkAsset The address of the asset being unlocked
    /// @param inZkAmount The amount of the asset being unlocked
    /// @param inZkNullifier The nullifier of the note
    /// @param relayer The address of the relayer
    /// @param outNoteFooter The footer of the note being created
    /// @param outAsset The address of the asset being created
    struct UnlockNoteRawInputs {
        bytes32 merkleRoot;
        address inZkAsset;
        uint256 inZkAmount;
        bytes32 inZkNullifier;
        address relayer;
        bytes32 outNoteFooter;
        address outAsset;
    }

    /**
     * @notice Constructor to initialize the contract with prime field parameter
     * @param primeField The prime field used for input building
     */
    constructor(uint256 primeField) BaseInputBuilder(primeField) {}

    /**
     * @notice Builds the inputs for the lock operation
     * @param _rawInputs The raw inputs for the lock operation
     * @return inputs An array of bytes32 representing the built inputs
     */
    function _buildLockNoteInputs(
        LockNoteRawInputs memory _rawInputs
    ) internal pure returns (bytes32[] memory) {
        bytes32[] memory inputs = new bytes32[](7);

        inputs[0] = (_rawInputs.merkleRoot);
        inputs[1] = _bytifyToNoir(_rawInputs.inAsset);
        inputs[2] = bytes32(_rawInputs.inAmount);
        inputs[3] = _rawInputs.inNullifier;
        inputs[4] = _bytifyToNoir(_rawInputs.relayer);
        inputs[5] = _rawInputs.outZkNoteFooter;
        inputs[6] = _bytifyToNoir(_rawInputs.outZkAsset);

        return inputs;
    }

    /**
     * @notice Builds the inputs for the lock asset operation
     * @param _rawInputs The raw inputs for the lock asset operation
     * @return inputs An array of bytes32 representing the built inputs
     */
    function _buildLockAssetInputs(
        LockAssetRawInputs memory _rawInputs
    ) internal pure returns (bytes32[] memory) {
        bytes32[] memory inputs = new bytes32[](6);

        inputs[0] = _bytifyToNoir(_rawInputs.owner);
        inputs[1] = _bytifyToNoir(_rawInputs.asset);
        inputs[2] = bytes32(_rawInputs.amount);
        inputs[3] = _rawInputs.outZkNote;
        inputs[4] = _rawInputs.outZkNoteFooter;
        inputs[5] = _bytifyToNoir(_rawInputs.outZkAsset);

        return inputs;
    }

    /**
     * @notice Builds the inputs for the unlock operation
     * @param _rawInputs The raw inputs for the unlock operation
     * @return inputs An array of bytes32 representing the built inputs
     */
    function _buildUnlockNoteInputs(
        UnlockNoteRawInputs memory _rawInputs
    ) internal pure returns (bytes32[] memory) {
        bytes32[] memory inputs = new bytes32[](7);

        inputs[0] = (_rawInputs.merkleRoot);
        inputs[1] = _bytifyToNoir(_rawInputs.inZkAsset);
        inputs[2] = bytes32(_rawInputs.inZkAmount);
        inputs[3] = _rawInputs.inZkNullifier;
        inputs[4] = _bytifyToNoir(_rawInputs.relayer);
        inputs[5] = _rawInputs.outNoteFooter;
        inputs[6] = _bytifyToNoir(_rawInputs.outAsset);

        return inputs;
    }
}
IZKToken.sol 23 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

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

/// @title ZKToken Interface
/// @notice Interface for ZKToken, an ERC20 token with minting and burning capabilities
interface IZKToken is IERC20 {
    /**
     * @notice Mints new tokens
     * @param account The address to receive the minted tokens
     * @param amount The amount of tokens to mint
     */
    function mint(address account, uint256 amount) external;

    /**
     * @notice Burns tokens from a specified address
     * @param account The address from which to burn tokens
     * @param amount The amount of tokens to burn
     */
    function burn(address account, uint256 amount) external;
}
Ownable.sol 100 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.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.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @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 {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @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 {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _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);
    }
}
IVerifierHub.sol 15 lines
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IVerifier} from "./IVerifier.sol";

interface IVerifierHub {
    function setVerifier(string memory verifierName, address addr) external;

    function getVerifierNames() external returns (string[] memory);

    function getVerifier(
        string memory verifierName
    ) external view returns (IVerifier);
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @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 value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}
IComplianceManager.sol 8 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;


interface IComplianceManager {
    function isAuthorized(address observer, address subject) external returns (bool);
}
IMerkleTreeOperator.sol 39 lines
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.20;

interface IMerkleTreeOperator {
    function appendMerkleLeaf(bytes32 leaf) external;
    function setNoteCommitmentCreated(bytes32 commitment) external;
    function setNullifierUsed(bytes32 nullifier) external;
    function setNullifierLocked(bytes32 nullifier, bool locked) external;
    function setNoteFooterUsed(bytes32 noteFooter) external;

    function isRelayerRegistered(address _relayer) external view returns (bool);

    function merkleRootIsAllowed(
        bytes32 _merkleRoot
    ) external view returns (bool);

    function nullifierIsNotUsed(
        bytes32 _nullifier
    ) external view returns (bool);
   
    function nullifierIsNotLocked(
        bytes32 _nullifier
    ) external view returns (bool);

    function noteIsNotCreated(
        bytes32 _noteCommitment
    ) external view returns (bool);

    function noteFooterIsNotUsed(
        bytes32 _noteFooter
    ) external view returns (bool);

    function getMerkleRoot() external view returns (bytes32);

    function getMerklePath(
        bytes32 _noteCommitment
    ) external view returns (bytes32[] memory, bool[] memory, bytes32);
}
IStakingOperator.sol 78 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/// @title Staking Operator Interface
/// @notice Interface for managing staking operations including unlock windows and collateral tokens
interface IStakingOperator {
    /// @notice Structure to define the unlock window
    /// @param start The start timestamp of the unlock window
    /// @param duration The duration of the unlock window
    struct UnlockWindow {
        uint256 start;
        uint256 duration;
    }

    /// @notice Event emitted when the unlock window is set
    /// @param start The start timestamp of the unlock window
    /// @param duration The duration of the unlock window
    event UnlockWindowSet(uint256 start, uint256 duration);

    /// @notice Event emitted when a collateral token is set
    /// @param original The address of the original token
    /// @param wrapped The address of the collateral (wrapped) token
    event CollateralTokenSet(address original, address wrapped);

    /// @notice Error thrown when an invalid collateral token is set
    error InvalidCollateralToken();
    /// @notice Error thrown when an invalid original token is already set
    error CollateralTokenAlreadySet();
    /// @notice Error thrown when an invalid unlock window duration is set
    error InvalidUnlockWindowDuration();
    /// @notice Error thrown when an invalid unlock window start is set
    error InvalidUnlockWindowStart();

    /**
     * @notice Gets the collateral token for an original token
     * @param original The address of the original token
     * @return The address of the collateral (wrapped) token
     */
    function getCollateralToken(
        address original
    ) external view returns (address);

    /**
     * @notice Gets the original token for a collateral token
     * @param wrapped The address of the wrapped token
     * @return The address of the original token
     */
    function getOriginalToken(address wrapped) external view returns (address);

    /**
     * @notice Checks if unlock is allowed at the current timestamp
     * @param currentTimestamp The current timestamp to check
     * @return True if unlock is allowed, false otherwise
     */
    function isUnlockAllowed(
        uint256 currentTimestamp
    ) external view returns (bool);

    /**
     * @notice Sets the collateral token for an original token
     * @param original The address of the original token
     * @param wrapped The address of the collateral (wrapped) token
     * @param force Flag to force set the collateral token
     * @dev Reverts if original and wrapped tokens are the same or if either address is zero
     */
    function setCollateralToken(address original, address wrapped, bool force) external;

    /**
     * @notice Sets the unlock window parameters
     * @param unlockWindowStart The start timestamp of the unlock window
     * @param unlockWindowDuration The duration of the unlock window
     */
    function setUnlockWindow(
        uint256 unlockWindowStart,
        uint256 unlockWindowDuration
    ) external;
}
IStakingAssetManager.sol 89 lines
// SPDX-License-Identifier: MIT

pragma solidity 0.8.20;

/// @title Staking Asset Manager Interface
/// @notice Interface for managing staking assets and related operations
interface IStakingAssetManager {
    struct LockNoteArgs {
        address asset; // The address of the asset to be locked
        address relayer; // The address of the relayer handling the transaction
        bytes32 merkleRoot; // The Merkle root for the note
        bytes32 nullifier; // A unique identifier to prevent double-spending
        uint256 amount; // The amount of the asset to be locked
        uint256 relayerGasFee; // The gas fee to be paid to the relayer
        bytes32 zkNoteFooter; // Footer information for the out zkNote
    }

    struct LockERC20Args {
        address asset; // The address of the asset to be locked
        uint256 amount; // The amount of the asset to be locked
        bytes32 zkNoteCommitment; // The commitment hash for the note
        bytes32 zkNoteFooter; // Footer information for the zkNote
    }

    struct LockETHArgs {
        bytes32 zkNoteCommitment; // The commitment hash for the note
        bytes32 zkNoteFooter; // Footer information for the zkNote
    }

    struct UnlockNoteArgs {
        address relayer;
        uint256 relayerGasFee;
        bytes32 merkleRoot;
        bytes32 zkNoteNullifier;
        address zkNoteAsset;
        uint256 zkNoteAmount;
        bytes32 outNoteFooter;
    }

    /// @notice Error thrown when the collateral token is missing
    error CollateralTokenMissing();

    /// @notice Error thrown when compliance validation fails
    error InvalidCompliance();

    /// @notice Error thrown when there is insufficient ZK token balance
    error InsufficientZKTokenBalance();

    /// @notice Error thrown when unlock is not allowed
    error UnlockNotAllowed();

    /**
     * @notice Event emitted when assets are locked
     * @param locker The address of the wallet locking the assets
     * @param assetIn The address of the asset being locked (collateral)
     * @param assetOut The address of the asset being locked (zkAsset)
     * @param amountOut The amount of asset locked
     * @param noteNullifierIn The nullifier of the note being locked
     * @param noteFooter The footer of the note being locked
     * @param noteCommitmentOut The commitment of the note being locked
     */
    event Locked(
        address locker,
        address assetIn,
        address assetOut,
        uint256 amountOut,
        bytes32 noteNullifierIn,
        bytes32 noteFooter,
        bytes32 noteCommitmentOut
    );

    /**
     * @notice Event emitted when assets are unlocked
     * @param assetIn The address of the asset being unlocked
     * @param assetOut The address of the asset being unlocked
     * @param amountOut The amount of asset unlocked
     * @param noteNullifierIn The nullifier of the note being unlocked
     * @param noteFooter The footer of the note being unlocked
     * @param noteCommitmentOut The commitment of the note being unlocked
     */
    event Unlocked(
        address assetIn,
        address assetOut,
        uint256 amountOut,
        bytes32 noteNullifierIn,
        bytes32 noteFooter,
        bytes32 noteCommitmentOut
    );
}
SafeERC20.sol 118 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../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 An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @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.encodeCall(token.approve, (spender, value));

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

    /**
     * @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);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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(token).code.length > 0;
    }
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @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.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
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].
     *
     * CAUTION: See Security Considerations above.
     */
    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);
}

Read Contract

ASSET_ETH 0xd985e8fb → bytes32
ETH_ADDRESS 0xa734f06e → address
P 0x8b8fbd92 → uint256
getAssetPoolERC20 0x51dc8b89 → address
getAssetPoolERC721 0xab335bfc → address
getAssetPoolETH 0x39114651 → address
getComplianceManager 0x6284e4c4 → address
getFeeManager 0xf2d63826 → address
getMerkleTreeOperator 0xb9713eb3 → address
getMimc254 0xa154a8b8 → address
getRelayerHub 0x8f4e4bd2 → address
getStakingOperator 0x642a4218 → address
getVerifierHub 0x391e8ef6 → address
owner 0x8da5cb5b → address
stakingOperator 0x142989b6 → address

Write Contract 15 functions

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

lockERC20 0xc9fdc4e4
tuple args
bytes proof
lockETH 0x388129cb
tuple args
bytes proof
lockNote 0x18bec489
tuple args
bytes proof
releaseToAsssetPool 0xb88daae9
address asset
uint256 amount
renounceOwnership 0x715018a6
No parameters
setAssetPoolERC20 0xf5d0fc4b
address assetPoolERC20
setAssetPoolERC721 0x3cac07ee
address assetPoolERC721
setAssetPoolETH 0x278135f7
address assetPoolETH
setComplianceManager 0xa576b5da
address complianceManager
setFeeManager 0x472d35b9
address feeManager
setRelayerHub 0xce9e014a
address relayerHub
setStakingOperator 0x09d5e989
address stakingOperator_
setVerifierHub 0x48f63f33
address verifierHub
transferOwnership 0xf2fde38b
address newOwner
unlock 0x0e47deed
tuple args
bytes proof

Recent Transactions

No transactions found for this address