Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x57f11FE3dc35Cabfc1E7a00Cde8b6552aF6e4e62
Balance 0 ETH
Nonce 1
Code Size 17735 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

17735 bytes
0x60806040526004361061011f5760003560e01c80638da5cb5b116100a0578063e6fb317f11610064578063e6fb317f1461034e578063f25c4fa91461036e578063f5b3f4241461038e578063f851a440146103ae578063fb0289ca146103ce57600080fd5b80638da5cb5b146102ae5780639969e9ee146102ce57806399e2a63b146102ee578063bd42d8481461030e578063d4b120031461032e57600080fd5b806363fb0b96116100e757806363fb0b96146101e7578063704b6c02146101fa578063745a7b3b1461021a5780637b1039991461023a5780638a3455701461026e57600080fd5b806319d40b08146101245780631ccb65ff14610161578063216f5a891461018357806329df02cf146101a35780635be6d062146101c3575b600080fd5b34801561013057600080fd5b50600254610144906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561016d57600080fd5b5061018161017c366004611fe7565b6103ee565b005b34801561018f57600080fd5b5061018161019e366004612891565b6105de565b3480156101af57600080fd5b506101816101be366004612d7d565b6108ee565b3480156101cf57600080fd5b506101d960045481565b604051908152602001610158565b6101816101f5366004612ee2565b610c70565b34801561020657600080fd5b50610181610215366004611fe7565b610f9c565b34801561022657600080fd5b50610181610235366004611fe7565b611030565b34801561024657600080fd5b506101447f000000000000000000000000991ce3079450f466ba3b6e6beccd1880e2a8858181565b34801561027a57600080fd5b5061029e610289366004611fe7565b60056020526000908152604090205460ff1681565b6040519015158152602001610158565b3480156102ba57600080fd5b50600154610144906001600160a01b031681565b3480156102da57600080fd5b506101816102e9366004612f4d565b6110ac565b3480156102fa57600080fd5b50610181610309366004613149565b61139a565b34801561031a57600080fd5b50610181610329366004613320565b611666565b34801561033a57600080fd5b50610144610349366004613506565b6118e9565b34801561035a57600080fd5b5061018161036936600461371a565b611913565b34801561037a57600080fd5b50610181610389366004613925565b611b86565b34801561039a57600080fd5b5061029e6103a9366004611fe7565b611e3f565b3480156103ba57600080fd5b50600054610144906001600160a01b031681565b3480156103da57600080fd5b506101816103e9366004611fe7565b611e71565b6000546001600160a01b031633146104195760405163b5c42b3b60e01b815260040160405180910390fd5b6001600160a01b03811660009081526005602052604090205460ff16610462576040516342ad5b3f60e01b81526001600160a01b03821660048201526024015b60405180910390fd5b60005b60035481101561056a57816001600160a01b03166003828154811061048c5761048c6139e9565b6000918252602090912001546001600160a01b03160361055857600380546104b690600190613a15565b815481106104c6576104c66139e9565b600091825260209091200154600380546001600160a01b0390921691839081106104f2576104f26139e9565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600380548061053157610531613a28565b600082815260209020810160001990810180546001600160a01b031916905501905561056a565b8061056281613a3e565b915050610465565b506001600160a01b0381166000908152600560205260408120805460ff19169055600480549161059983613a57565b90915550506040516001600160a01b03821681527f09c1ab3a33736061b9ab4db19afb80345fb417d89a6b531651d8c51291bf507f906020015b60405180910390a150565b3360009081526005602052604090205460ff1661060e5760405163198f48cb60e31b815260040160405180910390fd5b8551855181141580610621575084518114155b8061062d575083518114155b80610639575081518114155b1561065757604051637db491eb60e01b815260040160405180910390fd5b6000816001600160401b038111156106715761067161200b565b60405190808252806020026020018201604052801561069a578160200160208202803683370190505b5090506000826001600160401b038111156106b7576106b761200b565b6040519080825280602002602001820160405280156106ea57816020015b60608152602001906001900390816106d55790505b50905060005b8381101561088b57600089828151811061070c5761070c6139e9565b60200260200101519050600089838151811061072a5761072a6139e9565b602002602001015190508b8381518110610746576107466139e9565b6020026020010151858481518110610760576107606139e9565b60200260200101906001600160a01b031690816001600160a01b03168152505081818a8581518110610794576107946139e9565b60200260200101518a86815181106107ae576107ae6139e9565b60200260200101518a87815181106107c8576107c86139e9565b60200260200101516040516024016107e4959493929190613d1d565b60408051601f198184030181529190526020810180516001600160e01b031663f0806a7f60e01b1790528451859085908110610822576108226139e9565b6020026020010181905250806040015181602001516001600160a01b0316836001600160a01b03167f62d08c7b40038caee4cbb8d156cdba7f6c9cc8e53163de4b9c5d986f253fd7d860405160405180910390a45050808061088390613a3e565b9150506106f0565b506040516331fd85cb60e11b815230906363fb0b96906108b19085908590600401613d9f565b600060405180830381600087803b1580156108cb57600080fd5b505af11580156108df573d6000803e3d6000fd5b50505050505050505050505050565b3360009081526005602052604090205460ff1661091e5760405163198f48cb60e31b815260040160405180910390fd5b8651865181141580610931575085518114155b8061093d575084518114155b80610949575082518114155b80610955575083518114155b80610961575081518114155b1561097f57604051637db491eb60e01b815260040160405180910390fd5b6000816001600160401b038111156109995761099961200b565b6040519080825280602002602001820160405280156109c2578160200160208202803683370190505b5090506000826001600160401b038111156109df576109df61200b565b604051908082528060200260200182016040528015610a1257816020015b60608152602001906001900390816109fd5790505b50905060005b83811015610c0c578a8181518110610a3257610a326139e9565b6020026020010151838281518110610a4c57610a4c6139e9565b60200260200101906001600160a01b031690816001600160a01b031681525050898181518110610a7e57610a7e6139e9565b6020026020010151898281518110610a9857610a986139e9565b6020026020010151898381518110610ab257610ab26139e9565b6020026020010151898481518110610acc57610acc6139e9565b6020026020010151898581518110610ae657610ae66139e9565b6020026020010151898681518110610b0057610b006139e9565b6020026020010151604051602401610b1d96959493929190613e89565b60408051601f198184030181529190526020810180516001600160e01b031663f1a9e60360e01b1790528251839083908110610b5b57610b5b6139e9565b6020026020010181905250888181518110610b7857610b786139e9565b602002602001015160200151898281518110610b9657610b966139e9565b6020026020010151600001516001600160a01b03168b8381518110610bbd57610bbd6139e9565b60200260200101516001600160a01b03167f60072b5585d4e6f71a97d87f0e001a6482f2cf1d00c33a30437a3b6f6ebd0a8f60405160405180910390a480610c0481613a3e565b915050610a18565b506040516331fd85cb60e11b815230906363fb0b9690610c329085908590600401613d9f565b600060405180830381600087803b158015610c4c57600080fd5b505af1158015610c60573d6000803e3d6000fd5b5050505050505050505050505050565b828114610c905760405163c1e637c960e01b815260040160405180910390fd5b6040516332afe8a160e21b81523360048201527f000000000000000000000000991ce3079450f466ba3b6e6beccd1880e2a885816001600160a01b03169063cabfa28490602401602060405180830381865afa158015610cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d189190613fe1565b610d375760405163252c827360e01b8152336004820152602401610459565b60005b808214610f95576000858583818110610d5557610d556139e9565b9050602002016020810190610d6a9190611fe7565b6001600160a01b031603610d8057600101610d3a565b30858583818110610d9357610d936139e9565b9050602002016020810190610da89190611fe7565b6001600160a01b031614610ec4577f000000000000000000000000991ce3079450f466ba3b6e6beccd1880e2a885816001600160a01b031663907caa00868684818110610df757610df76139e9565b9050602002016020810190610e0c9190611fe7565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa158015610e50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e749190613fe1565b610ec457848482818110610e8a57610e8a6139e9565b9050602002016020810190610e9f9190611fe7565b6040516347ccabe760e01b81526001600160a01b039091166004820152602401610459565b600080868684818110610ed957610ed96139e9565b9050602002016020810190610eee9190611fe7565b6001600160a01b0316858585818110610f0957610f096139e9565b9050602002810190610f1b9190613ffe565b604051610f29929190614044565b6000604051808303816000865af19150503d8060008114610f66576040519150601f19603f3d011682016040523d82523d6000602084013e610f6b565b606091505b509150915081610f8b578051600003610f8357600080fd5b805181602001fd5b5050600101610d3a565b5050505050565b6000546001600160a01b03163314610fc75760405163b5c42b3b60e01b815260040160405180910390fd5b600054604080516001600160a01b03928316815291831660208301527fbf265e8326285a2747e33e54d5945f7111f2b5edb826eb8c08d4677779b3ff97910160405180910390a1600080546001600160a01b0319166001600160a01b0392909216919091179055565b6000546001600160a01b0316331461105b5760405163b5c42b3b60e01b815260040160405180910390fd5b6001600160a01b03811660009081526005602052604090205460ff16156110a057604051631c9ab5b560e01b81526001600160a01b0382166004820152602401610459565b6110a981611eea565b50565b3360009081526005602052604090205460ff166110dc5760405163198f48cb60e31b815260040160405180910390fd5b84518451811415806110ef575082518114155b806110fb575081518114155b1561111957604051637db491eb60e01b815260040160405180910390fd5b6000816001600160401b038111156111335761113361200b565b60405190808252806020026020018201604052801561115c578160200160208202803683370190505b5090506000826001600160401b038111156111795761117961200b565b6040519080825280602002602001820160405280156111ac57816020015b60608152602001906001900390816111975790505b50905060005b838110156113385760008882815181106111ce576111ce6139e9565b6020026020010151905060008883815181106111ec576111ec6139e9565b60200260200101519050600088848151811061120a5761120a6139e9565b602002602001015190508b8481518110611226576112266139e9565b6020026020010151868581518110611240576112406139e9565b60200260200101906001600160a01b031690816001600160a01b0316815250508282828a8781518110611275576112756139e9565b60200260200101516040516024016112909493929190614054565b60408051601f198184030181529190526020810180516001600160e01b031663aaec71d760e01b17905285518690869081106112ce576112ce6139e9565b6020026020010181905250816020015182600001516001600160a01b0316846001600160a01b03167fbeb1a0a003f297419afd0c65340a684e8c5e9b576a1a705a4ac19762f8e89c0460405160405180910390a4505050808061133090613a3e565b9150506111b2565b506040516331fd85cb60e11b815230906363fb0b969061135e9085908590600401613d9f565b600060405180830381600087803b15801561137857600080fd5b505af115801561138c573d6000803e3d6000fd5b505050505050505050505050565b3360009081526005602052604090205460ff166113ca5760405163198f48cb60e31b815260040160405180910390fd5b83518351811415806113dd575082518114155b806113e9575081518114155b1561140757604051637db491eb60e01b815260040160405180910390fd5b6000816001600160401b038111156114215761142161200b565b60405190808252806020026020018201604052801561144a578160200160208202803683370190505b5090506000826001600160401b038111156114675761146761200b565b60405190808252806020026020018201604052801561149a57816020015b60608152602001906001900390816114855790505b50905060005b838110156116055760008782815181106114bc576114bc6139e9565b6020026020010151905060008783815181106114da576114da6139e9565b602002602001015190508983815181106114f6576114f66139e9565b6020026020010151858481518110611510576115106139e9565b60200260200101906001600160a01b031690816001600160a01b0316815250508181888581518110611544576115446139e9565b602002602001015160405160240161155e93929190614115565b60408051601f198184030181529190526020810180516001600160e01b0316634a2462b560e11b179052845185908590811061159c5761159c6139e9565b6020026020010181905250806040015181602001516001600160a01b0316836001600160a01b03167f845400de7ed0a439c0685ef185e4e48ab874482b2bbba826983593c7b9bb114b60405160405180910390a4505080806115fd90613a3e565b9150506114a0565b506040516331fd85cb60e11b815230906363fb0b969061162b9085908590600401613d9f565b600060405180830381600087803b15801561164557600080fd5b505af1158015611659573d6000803e3d6000fd5b5050505050505050505050565b3360009081526005602052604090205460ff166116965760405163198f48cb60e31b815260040160405180910390fd5b83518351811415806116a9575082518114155b806116b5575081518114155b156116d357604051637db491eb60e01b815260040160405180910390fd5b6000816001600160401b038111156116ed576116ed61200b565b604051908082528060200260200182016040528015611716578160200160208202803683370190505b5090506000826001600160401b038111156117335761173361200b565b60405190808252806020026020018201604052801561176657816020015b60608152602001906001900390816117515790505b50905060005b83811015611605576000878281518110611788576117886139e9565b6020026020010151905060008783815181106117a6576117a66139e9565b602002602001015190508983815181106117c2576117c26139e9565b60200260200101518584815181106117dc576117dc6139e9565b60200260200101906001600160a01b031690816001600160a01b0316815250508181888581518110611810576118106139e9565b602002602001015160405160240161182a93929190614144565b60408051601f198184030181529190526020810180516001600160e01b031663814a1f1560e01b1790528451859085908110611868576118686139e9565b6020908102919091018101919091526080820151805183518051908401519284015160408051948552948401526001600160a01b039182169390821692918616917fa7618589b4ad2eca6c42db5d1b67fe22fd78e950354363e80700e97e021614c6910160405180910390a4505080806118e190613a3e565b91505061176c565b600381815481106118f957600080fd5b6000918252602090912001546001600160a01b0316905081565b3360009081526005602052604090205460ff166119435760405163198f48cb60e31b815260040160405180910390fd5b8351835181141580611956575082518114155b80611962575081518114155b1561198057604051637db491eb60e01b815260040160405180910390fd5b6000816001600160401b0381111561199a5761199a61200b565b6040519080825280602002602001820160405280156119c3578160200160208202803683370190505b5090506000826001600160401b038111156119e0576119e061200b565b604051908082528060200260200182016040528015611a1357816020015b60608152602001906001900390816119fe5790505b50905060005b83811015611605576000868281518110611a3557611a356139e9565b602002602001015190506000888381518110611a5357611a536139e9565b60200260200101519050898381518110611a6f57611a6f6139e9565b6020026020010151858481518110611a8957611a896139e9565b60200260200101906001600160a01b031690816001600160a01b0316815250508082888581518110611abd57611abd6139e9565b6020026020010151604051602401611ad7939291906143bf565b60408051601f198184030181529190526020810180516001600160e01b0316631c03256560e31b1790528451859085908110611b1557611b156139e9565b60200260200101819052508160200151604001518260200151602001516001600160a01b0316826001600160a01b03167fb26a0db4cf85bbeaf7c8a6dcf47ab1bab082afd520f0120d76781ec53f67df6e60405160405180910390a450508080611b7e90613a3e565b915050611a19565b3360009081526005602052604090205460ff16611bb65760405163198f48cb60e31b815260040160405180910390fd5b8551855181141580611bc9575084518114155b80611bd5575083518114155b80611be1575082518114155b80611bed575081518114155b15611c0b57604051637db491eb60e01b815260040160405180910390fd5b6000816001600160401b03811115611c2557611c2561200b565b604051908082528060200260200182016040528015611c4e578160200160208202803683370190505b5090506000826001600160401b03811115611c6b57611c6b61200b565b604051908082528060200260200182016040528015611c9e57816020015b6060815260200190600190039081611c895790505b50905060005b8381101561088b576000898281518110611cc057611cc06139e9565b602002602001015190506000898381518110611cde57611cde6139e9565b602002602001015190508b8381518110611cfa57611cfa6139e9565b6020026020010151858481518110611d1457611d146139e9565b60200260200101906001600160a01b031690816001600160a01b03168152505081818a8581518110611d4857611d486139e9565b60200260200101518a8681518110611d6257611d626139e9565b60200260200101518a8781518110611d7c57611d7c6139e9565b6020026020010151604051602401611d989594939291906144b9565b60408051601f198184030181529190526020810180516001600160e01b03166338f6f92760e01b1790528451859085908110611dd657611dd66139e9565b6020026020010181905250806040015181602001516001600160a01b0316836001600160a01b03167f24a5e161bb55a0a758c5ce5e6621fc0494f946bd2e70162ed7d785c7b914079160405160405180910390a450508080611e3790613a3e565b915050611ca4565b6001546000906001600160a01b0383811691161480611e6b57506002546001600160a01b038381169116145b92915050565b6001546001600160a01b03163314611e9c576040516374a2152760e01b815260040160405180910390fd5b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f5e5dee98c8f51165e719725b40ebe485de8ce07b39ad552e40a31d58b5be7646906020016105d3565b6001600160a01b038116611f1157604051631d8d67b960e11b815260040160405180910390fd5b6001600160a01b0381166000818152600560205260408120805460ff1916600190811790915560038054918201815582527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0180546001600160a01b0319169092179091556004805491611f8483613a3e565b90915550506040516001600160a01b03821681527f7b24afe53ea3c0eb8558ae101198e86c392b4055609b2be0e0b23b89b2b8f013906020016105d3565b6001600160a01b03811681146110a957600080fd5b8035611fe281611fc2565b919050565b600060208284031215611ff957600080fd5b813561200481611fc2565b9392505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156120435761204361200b565b60405290565b604051606081016001600160401b03811182821017156120435761204361200b565b604051608081016001600160401b03811182821017156120435761204361200b565b60405160a081016001600160401b03811182821017156120435761204361200b565b60405161014081016001600160401b03811182821017156120435761204361200b565b60405160c081016001600160401b03811182821017156120435761204361200b565b60405161010081016001600160401b03811182821017156120435761204361200b565b604051601f8201601f191681016001600160401b038111828210171561213f5761213f61200b565b604052919050565b60006001600160401b038211156121605761216061200b565b5060051b60200190565b600082601f83011261217b57600080fd5b8135602061219061218b83612147565b612117565b82815260059290921b840181019181810190868411156121af57600080fd5b8286015b848110156121d35780356121c681611fc2565b83529183019183016121b3565b509695505050505050565b6000604082840312156121f057600080fd5b6121f8612021565b9050813561220581611fc2565b808252506020820135602082015292915050565b60006080828403121561222b57600080fd5b612233612049565b905061223f83836121de565b8152604082013561224f81611fc2565b6020820152606091909101356040820152919050565b600082601f83011261227657600080fd5b8135602061228661218b83612147565b82815260079290921b840181019181810190868411156122a557600080fd5b8286015b848110156121d3576122bb8882612219565b8352918301916080016122a9565b600082601f8301126122da57600080fd5b813560206122ea61218b83612147565b82815260059290921b8401810191818101908684111561230957600080fd5b8286015b848110156121d357803561232081611fc2565b835291830191830161230d565b80356001600160801b0381168114611fe257600080fd5b600082601f83011261235557600080fd5b81356001600160401b0381111561236e5761236e61200b565b612381601f8201601f1916602001612117565b81815284602083860101111561239657600080fd5b816020850160208301376000918101602001919091529392505050565b6000608082840312156123c557600080fd5b6123cd61206b565b905081356001600160401b03808211156123e657600080fd5b6123f2858386016122c9565b83526124006020850161232d565b60208401526124116040850161232d565b6040840152606084013591508082111561242a57600080fd5b5061243784828501612344565b60608301525092915050565b600082601f83011261245457600080fd5b8135602061246461218b83612147565b82815260059290921b8401810191818101908684111561248357600080fd5b8286015b848110156121d35780356001600160401b03808211156124a75760008081fd5b9088019060a0828b03601f19018113156124c15760008081fd5b6124c961208d565b878401356124d681611fc2565b80825250604080850135898301526060808601358284015260809150818601356124ff81611fc2565b908301529184013591838311156125165760008081fd5b6125248d8a85880101612344565b908201528652505050918301918301612487565b60006060828403121561254a57600080fd5b612552612049565b9050813561255f81611fc2565b8152602082013561256f81611fc2565b6020820152604082013562ffffff8116811461258a57600080fd5b604082015292915050565b8035600281900b8114611fe257600080fd5b6000604082840312156125b957600080fd5b6125c1612021565b905081356001600160401b03808211156125da57600080fd5b6125e685838601612443565b835260208401359150808211156125fc57600080fd5b90830190610180828603121561261157600080fd5b6126196120af565b61262283611fd7565b81526020830135602082015261263b8660408501612538565b604082015261264c60a08401612595565b606082015261265d60c08401612595565b608082015260e083013560a08201526101008084013560c08301526101208085013560e0840152610140850135828401526101608501359150838211156126a357600080fd5b6126af88838701612344565b9083015250602084015250909392505050565b600082601f8301126126d357600080fd5b813560206126e361218b83612147565b82815260059290921b8401810191818101908684111561270257600080fd5b8286015b848110156121d35780356001600160401b03808211156127265760008081fd5b908801906040828b03601f19018113156127405760008081fd5b612748612021565b878401358381111561275a5760008081fd5b6127688d8a838801016123b3565b82525090830135908282111561277e5760008081fd5b61278c8c89848701016125a7565b818901528652505050918301918301612706565b80151581146110a957600080fd5b600082601f8301126127bf57600080fd5b813560206127cf61218b83612147565b82815260059290921b840181019181810190868411156127ee57600080fd5b8286015b848110156121d3578035612805816127a0565b83529183019183016127f2565b600082601f83011261282357600080fd5b8135602061283361218b83612147565b82815260059290921b8401810191818101908684111561285257600080fd5b8286015b848110156121d35780356001600160401b038111156128755760008081fd5b6128838986838b01016122c9565b845250918301918301612856565b60008060008060008060c087890312156128aa57600080fd5b86356001600160401b03808211156128c157600080fd5b6128cd8a838b0161216a565b975060208901359150808211156128e357600080fd5b6128ef8a838b0161216a565b9650604089013591508082111561290557600080fd5b6129118a838b01612265565b9550606089013591508082111561292757600080fd5b6129338a838b016126c2565b9450608089013591508082111561294957600080fd5b6129558a838b016127ae565b935060a089013591508082111561296b57600080fd5b5061297889828a01612812565b9150509295509295509295565b600082601f83011261299657600080fd5b813560206129a661218b83612147565b82815260069290921b840181019181810190868411156129c557600080fd5b8286015b848110156121d3576129db88826121de565b8352918301916040016129c9565b600082601f8301126129fa57600080fd5b81356020612a0a61218b83612147565b82815260059290921b84018101918181019086841115612a2957600080fd5b8286015b848110156121d35780356001600160401b0380821115612a4d5760008081fd5b908801906060828b03601f1901811315612a675760008081fd5b612a6f612049565b8784013583811115612a815760008081fd5b612a8f8d8a83880101612443565b82525060408085013584811115612aa65760008081fd5b612ab48e8b83890101612344565b838b015250918401359183831115612acc5760008081fd5b612ada8d8a858801016122c9565b908201528652505050918301918301612a2d565b600082601f830112612aff57600080fd5b81356020612b0f61218b83612147565b82815260059290921b84018101918181019086841115612b2e57600080fd5b8286015b848110156121d35780358352918301918301612b32565b600082601f830112612b5a57600080fd5b612b6761218b8335612147565b82358082526020808301929160051b850101851015612b8557600080fd5b602084015b6020853560051b860101811015612d74576001600160401b038082351115612bb157600080fd5b81358601601f196060828a0382011215612bca57600080fd5b612bd2612049565b8360208401351115612be357600080fd5b612bf58a602080860135860101612344565b81528360408401351115612c0857600080fd5b60408301358301604083828d03011215612c2157600080fd5b612c29612021565b8560208301351115612c3a57600080fd5b6020820135820160c085828f03011215612c5357600080fd5b612c5b6120d2565b9450612c6960208201611fd7565b8552612c7760408201611fd7565b60208601528660608201351115612c8d57600080fd5b612ca08d602060608401358401016122c9565b6040860152608081013560608601528660a08201351115612cc057600080fd5b612cd38d602060a0840135840101612aee565b60808601528660c08201351115612ce957600080fd5b612cfc8d602060c0840135840101612344565b60a0860152508381528560408301351115612d1657600080fd5b612d298c60206040850135850101612443565b602082015280602084015250508360608401351115612d4757600080fd5b612d5a8a602060608601358601016122c9565b604082015286525050602093840193919091019050612b8a565b50949350505050565b600080600080600080600060e0888a031215612d9857600080fd5b87356001600160401b0380821115612daf57600080fd5b612dbb8b838c016122c9565b985060208a0135915080821115612dd157600080fd5b612ddd8b838c0161216a565b975060408a0135915080821115612df357600080fd5b612dff8b838c01612985565b965060608a0135915080821115612e1557600080fd5b612e218b838c016129e9565b955060808a0135915080821115612e3757600080fd5b612e438b838c01612812565b945060a08a0135915080821115612e5957600080fd5b612e658b838c01612b49565b935060c08a0135915080821115612e7b57600080fd5b50612e888a828b01612812565b91505092959891949750929550565b60008083601f840112612ea957600080fd5b5081356001600160401b03811115612ec057600080fd5b6020830191508360208260051b8501011115612edb57600080fd5b9250929050565b60008060008060408587031215612ef857600080fd5b84356001600160401b0380821115612f0f57600080fd5b612f1b88838901612e97565b90965094506020870135915080821115612f3457600080fd5b50612f4187828801612e97565b95989497509550505050565b600080600080600060a08688031215612f6557600080fd5b85356001600160401b0380821115612f7c57600080fd5b612f8889838a016122c9565b96506020880135915080821115612f9e57600080fd5b612faa89838a0161216a565b95506040880135915080821115612fc057600080fd5b612fcc89838a01612985565b94506060880135915080821115612fe257600080fd5b612fee89838a016129e9565b9350608088013591508082111561300457600080fd5b5061301188828901612812565b9150509295509295909350565b60006080828403121561303057600080fd5b61303861206b565b905081356001600160401b038082111561305157600080fd5b61305d858386016123b3565b8352602084013591508082111561307357600080fd5b61307f85838601612443565b6020840152604084013591508082111561309857600080fd5b6130a4858386016122c9565b604084015260608401359150808211156130bd57600080fd5b50612437848285016122c9565b600082601f8301126130db57600080fd5b813560206130eb61218b83612147565b82815260059290921b8401810191818101908684111561310a57600080fd5b8286015b848110156121d35780356001600160401b0381111561312d5760008081fd5b61313b8986838b010161301e565b84525091830191830161310e565b6000806000806080858703121561315f57600080fd5b84356001600160401b038082111561317657600080fd5b6131828883890161216a565b9550602087013591508082111561319857600080fd5b6131a48883890161216a565b945060408701359150808211156131ba57600080fd5b6131c688838901612265565b935060608701359150808211156131dc57600080fd5b506131e9878288016130ca565b91505092959194509250565b60006040828403121561320757600080fd5b61320f612021565b905081356001600160401b038082111561322857600080fd5b61323485838601612443565b8352602084013591508082111561324a57600080fd5b9083019060c0828603121561325e57600080fd5b6132666120d2565b61326f83611fd7565b815261327d60208401611fd7565b602082015260408301358281111561329457600080fd5b6132a0878286016122c9565b6040830152506060830135828111156132b857600080fd5b6132c487828601612aee565b6060830152506080830135828111156132dc57600080fd5b6132e887828601612aee565b60808301525060a08301358281111561330057600080fd5b61330c87828601612344565b60a083015250602084015250909392505050565b6000806000806080858703121561333657600080fd5b6001600160401b03808635111561334c57600080fd5b61335987873588016122c9565b945060208601358181111561336d57600080fd5b6133798882890161216a565b94505060408601358181111561338e57600080fd5b8601601f8101881361339f57600080fd5b6133ac61218b8235612147565b81358082526020808301929160051b8401018a8111156133cb57600080fd5b602084015b818110156134d45785813511156133e657600080fd5b80358501610100818e03601f190112156133ff57600080fd5b6134076120d2565b6134148e602084016121de565b815260608201358881111561342857600080fd5b6134378f602083860101612344565b60208301525060808201358881111561344f57600080fd5b61345e8f6020838601016122c9565b60408301525060a08201358881111561347657600080fd5b6134858f6020838601016131f5565b6060830152506134988e60c084016121de565b6080820152610100820135888111156134b057600080fd5b6134bf8f602083860101612344565b60a083015250855250602093840193016133d0565b509095505050506060860135818111156134ed57600080fd5b6134f988828901612812565b9250505092959194509250565b60006020828403121561351857600080fd5b5035919050565b60006060828403121561353157600080fd5b613539612049565b905081356001600160401b038082111561355257600080fd5b908301906040828603121561356657600080fd5b61356e612021565b82358281111561357d57600080fd5b8301610100818803121561359057600080fd5b6135986120f4565b6135a182611fd7565b8152602080830135818301526135b96040840161232d565b604083015260608301356060830152608083013560808301526135de60a0840161232d565b60a08301526135ef60c0840161232d565b60c083015260e08301358581111561360657600080fd5b6136128a828601612344565b60e0840152508184528086013592508483111561362e57600080fd5b61363a89848801612443565b818501528387528088013595508486111561365457600080fd5b61366089878a016122c9565b90870152505050604084013591508082111561367b57600080fd5b5061368884828501612344565b60408301525092915050565b6000608082840312156136a657600080fd5b6136ae61206b565b905081356001600160401b03808211156136c757600080fd5b6136d3858386016122c9565b835260208401359150808211156136e957600080fd5b6136f585838601612aee565b6020840152604084013591508082111561370e57600080fd5b612411858386016125a7565b6000806000806080858703121561373057600080fd5b6001600160401b03808635111561374657600080fd5b613753878735880161216a565b945060208601358181111561376757600080fd5b6137738882890161216a565b94505060408601358181111561378857600080fd5b8601601f8101881361379957600080fd5b6137a661218b8235612147565b81358082526020808301929160051b8401018a8111156137c557600080fd5b602084015b818110156134d45785813511156137e057600080fd5b80358501610100818e03601f190112156137f957600080fd5b61380161208d565b61380d60208301611fd7565b815261381c8e60408401612219565b602082015260c08201358881111561383357600080fd5b6138428f60208386010161301e565b60408301525060e08201358881111561385a57600080fd5b6138698f60208386010161351f565b6060830152506101008201358881111561388257600080fd5b6138918f602083860101613694565b608083015250855250602093840193016137ca565b600082601f8301126138b757600080fd5b813560206138c761218b83612147565b82815260059290921b840181019181810190868411156138e657600080fd5b8286015b848110156121d35780356001600160401b038111156139095760008081fd5b6139178986838b010161351f565b8452509183019183016138ea565b60008060008060008060c0878903121561393e57600080fd5b86356001600160401b038082111561395557600080fd5b6139618a838b0161216a565b9750602089013591508082111561397757600080fd5b6139838a838b0161216a565b9650604089013591508082111561399957600080fd5b6139a58a838b01612265565b955060608901359150808211156139bb57600080fd5b6139c78a838b016130ca565b945060808901359150808211156139dd57600080fd5b6129558a838b016138a6565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115611e6b57611e6b6139ff565b634e487b7160e01b600052603160045260246000fd5b600060018201613a5057613a506139ff565b5060010190565b600081613a6657613a666139ff565b506000190190565b613a8c82825180516001600160a01b03168252602090810151910152565b60208101516001600160a01b03166040838101919091520151606090910152565b600081518084526020808501945080840160005b83811015613ae65781516001600160a01b031687529582019590820190600101613ac1565b509495945050505050565b6000815180845260005b81811015613b1757602081850181015186830182015201613afb565b506000602082860101526020601f19601f83011685010191505092915050565b6000815160808452613b4c6080850182613aad565b905060208301516001600160801b038082166020870152806040860151166040870152505060608301518482036060860152613b888282613af1565b95945050505050565b600081518084526020808501808196508360051b8101915082860160005b85811015613c1b578284038952815180516001600160a01b0390811686528682015187870152604080830151908701526060808301519091169086015260809081015160a091860182905290613c0781870183613af1565b9a87019a9550505090840190600101613baf565b5091979650505050505050565b6000815160408452613c3d6040850182613b91565b905060208301518482036020860152610180613c628383516001600160a01b03169052565b6020828101518482015260408084015180516001600160a01b039081168388015292810151909216606086015281015162ffffff166080850152506060820151613cb160a085018260020b9052565b506080820151613cc660c085018260020b9052565b5060a082015160e084015260c0820151610100818186015260e0840151915061012082818701528185015161014087015280850151945050505080610160840152613d1381840183613af1565b9695505050505050565b6001600160a01b03861681526000610100613d3b6020840188613a6e565b8060a08401528551604082850152613d57610140850182613b37565b915050602086015160ff1984830301610120850152613d768282613c28565b91505084151560c084015282810360e0840152613d938185613aad565b98975050505050505050565b604081526000613db26040830185613aad565b6020838203818501528185518084528284019150828160051b85010183880160005b83811015613e0257601f19878403018552613df0838351613af1565b94860194925090850190600101613dd4565b50909998505050505050505050565b6000815160608452613e266060850182613b91565b905060208301518482036020860152613e3f8282613af1565b91505060408301518482036040860152613b888282613aad565b600081518084526020808501945080840160005b83811015613ae657815187529582019590820190600101613e6d565b6001600160a01b0387811682526000906020613eba8185018a80516001600160a01b03168252602090810151910152565b60e06060850152613ece60e0850189613e11565b8481036080860152613ee08189613aad565b905084810360a0860152865160608252613efd6060830182613af1565b9050828801518282038484015280516040835285815116604084015285858201511660608401526040810151955060c06080840152613f40610100840187613aad565b9550606081015160a08401526080810151603f19808589030160c0860152613f688883613e59565b975060a08301519250808589030160e08601525050613f878682613af1565b95505083810151905081850384830152613fa18582613b91565b9450505060408701519150808303604082015250613fbf8282613aad565b91505082810360c0840152613fd48185613aad565b9998505050505050505050565b600060208284031215613ff357600080fd5b8151612004816127a0565b6000808335601e1984360301811261401557600080fd5b8301803591506001600160401b0382111561402f57600080fd5b602001915036819003821315612edb57600080fd5b8183823760009101908152919050565b6001600160a01b0385168152614080602082018580516001600160a01b03168252602090810151910152565b60a06060820152600061409660a0830185613e11565b82810360808401526140a88185613aad565b979650505050505050565b60008151608084526140c86080850182613b37565b9050602083015184820360208601526140e18282613b91565b915050604083015184820360408601526140fb8282613aad565b91505060608301518482036060860152613b888282613aad565b6001600160a01b038416815261412e6020820184613a6e565b60c060a08201526000613b8860c08301846140b3565b600060018060a01b038086168352602060608185015261417b60608501875180516001600160a01b03168252602090810151910152565b808601516101008060a0870152614196610160870183613af1565b9150604080890151605f19808986030160c08a01526141b58583613aad565b945060608b01519150808986030160e08a015281518386526141d984870182613b91565b9050868301519250858103878701528783511681528787840151168782015283830151975060c08482015261421160c0820189613aad565b975060608301519650808803606082015261422c8888613e59565b97506080830151965080880360808201526142478888613e59565b975060a0830151965080880360a0820152506142638787613af1565b965060808b0151955061428b848a018780516001600160a01b03168252602090810151910152565b60a08b0151955080898803016101408a015250506142a98585613af1565b94508685038188015250505050613d138185613aad565b600081516060845280516040606086015260018060a01b0381511660a0860152602081015160c0860152604081015161430460e08701826001600160801b03169052565b5060608101516101008181880152608083015161012088015260a0830151915061433a6101408801836001600160801b03169052565b60c08301516001600160801b031661016088015260e0909201516101808701929092525061436c6101a0860182613af1565b905060208201519150605f1985820301608086015261438b8183613b91565b915050602083015184820360208601526143a58282613aad565b91505060408301518482036040860152613b888282613af1565b6001600160a01b038481168252606060208084018290528551909216908301528301516000906143f26080840182613a6e565b5060408401516101008381015261440d6101608401826140b3565b90506060850151605f19808584030161012086015261442c83836142c0565b9250608087015191508085840301610140860152508051608083526144546080840182613aad565b90506020820151838203602085015261446d8282613e59565b915050604082015183820360408501526144878282613c28565b9150506060820151915082810360608401526144a38183613af1565b925050508281036040840152613d138185613aad565b6001600160a01b038616815260006101006144d76020840188613a6e565b8060a08401526144e9818401876140b3565b905082810360c08401526144fd81866142c0565b905082810360e0840152613d938185613aad56fea2646970667358221220cf42de7638788592812c862d9cee59a7c6d584fc9098ad9b1dd4a0d9bfffef2264736f6c63430008130033

Verified Source Code Full Match

Compiler: v0.8.19+commit.7dd6d404 EVM: paris Optimization: Yes (200 runs)
Automation.sol 403 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IUniswapV3Pool } from
    "contracts/interfaces/external/uniswap/IUniswapV3Pool.sol";

import { Admin } from "contracts/base/Admin.sol";
import { NonDelegateMulticall } from "contracts/base/NonDelegateMulticall.sol";
import { Sickle } from "contracts/Sickle.sol";
import { SickleRegistry } from "contracts/SickleRegistry.sol";
import { IAutomation } from "contracts/interfaces/IAutomation.sol";
import { INftAutomation } from "contracts/interfaces/INftAutomation.sol";
import {
    NftRebalance,
    NftPosition,
    NftHarvest,
    NftWithdraw,
    NftCompound
} from "contracts/structs/NftFarmStrategyStructs.sol";
import {
    Farm,
    HarvestParams,
    WithdrawParams,
    CompoundParams
} from "contracts/structs/FarmStrategyStructs.sol";

// @title Automation contract for automating farming strategies
// @notice This contract allows users to automate their farming strategies
// by enabling auto-compound or auto-harvest for non-NFT positions.
// Only one of Auto-Compound or Auto-Harvest can be enabled:
// all user positions will be either auto-compounded or auto-harvested.
// For NFT positions, all automation settings are handled by NftSettingsRegistry
// instead.
// The contract also allows an approved automator to compound, harvest, exit or
// rebalance farming positions on behalf of users.
// @dev This contract is expected to be used by an external automation bot
// that will call the compoundFor, harvestFor, and rebalanceFor functions.
// The automation bot is expected to be the EOA of the approved automator.
// The approved automator is set by the protocol admin.
contract Automation is Admin, NonDelegateMulticall {
    error InvalidInputLength();
    error NotApprovedAutomator();
    error InvalidAutomator();
    error ApprovedAutomatorNotSet(address approvedAutomator);
    error ApprovedAutomatorAlreadySet(address approvedAutomator);

    event HarvestedFor(
        Sickle indexed sickle,
        address indexed stakingContract,
        uint256 indexed poolIndex
    );
    event CompoundedFor(
        Sickle indexed sickle,
        address indexed claimStakingContract,
        uint256 claimPoolIndex,
        address indexed depositStakingContract,
        uint256 depositPoolIndex
    );
    event ExitedFor(
        Sickle indexed sickle,
        address indexed stakingContract,
        uint256 indexed poolIndex
    );

    event NftHarvestedFor(
        Sickle indexed sickle,
        address indexed nftAddress,
        uint256 indexed tokenId
    );
    event NftCompoundedFor(
        Sickle indexed sickle,
        address indexed nftAddress,
        uint256 indexed tokenId
    );
    event NftExitedFor(
        Sickle indexed sickle,
        address indexed nftAddress,
        uint256 indexed tokenId
    );
    event NftRebalancedFor(
        Sickle indexed sickle,
        address indexed nftAddress,
        uint256 indexed tokenId
    );

    event ApprovedAutomatorSet(address approvedAutomator);
    event ApprovedAutomatorRevoked(address approvedAutomator);

    address[] public approvedAutomators;
    uint256 public approvedAutomatorsLength;
    mapping(address => bool) public isApprovedAutomator;

    constructor(
        SickleRegistry registry_,
        address payable approvedAutomator_,
        address admin_
    ) Admin(admin_) NonDelegateMulticall(registry_) {
        _setApprovedAutomator(approvedAutomator_);
    }

    modifier onlyApprovedAutomator() {
        if (!isApprovedAutomator[msg.sender]) revert NotApprovedAutomator();
        _;
    }

    // Admin functions

    /// @notice Update approved automator address.
    /// @dev Controls which external address is allowed to
    /// compound farming positions for Sickles. This is expected to be the EOA
    /// of an automation bot.
    /// @custom:access Restricted to protocol admin.
    function setApprovedAutomator(
        address payable approvedAutomator_
    ) external onlyAdmin {
        if (isApprovedAutomator[approvedAutomator_]) {
            revert ApprovedAutomatorAlreadySet(approvedAutomator_);
        }
        _setApprovedAutomator(approvedAutomator_);
    }

    function revokeApprovedAutomator(
        address approvedAutomator_
    ) external onlyAdmin {
        if (!isApprovedAutomator[approvedAutomator_]) {
            revert ApprovedAutomatorNotSet(approvedAutomator_);
        }
        for (uint256 i; i < approvedAutomators.length; i++) {
            if (approvedAutomators[i] == approvedAutomator_) {
                approvedAutomators[i] =
                    approvedAutomators[approvedAutomators.length - 1];
                approvedAutomators.pop();
                break;
            }
        }
        isApprovedAutomator[approvedAutomator_] = false;
        approvedAutomatorsLength--;
        emit ApprovedAutomatorRevoked(approvedAutomator_);
    }

    // Automator functions

    function compoundFor(
        IAutomation[] memory strategies,
        Sickle[] memory sickles,
        CompoundParams[] memory params,
        address[][] memory sweepTokens
    ) external onlyApprovedAutomator {
        uint256 strategiesLength = strategies.length;
        if (
            strategiesLength != sickles.length
                || strategiesLength != params.length
                || strategiesLength != sweepTokens.length
        ) {
            revert InvalidInputLength();
        }

        address[] memory targets = new address[](strategiesLength);
        bytes[] memory data = new bytes[](strategiesLength);
        for (uint256 i; i < strategiesLength; i++) {
            Sickle sickle = sickles[i];
            CompoundParams memory param = params[i];
            targets[i] = address(strategies[i]);
            data[i] = abi.encodeCall(
                IAutomation.compoundFor, (sickle, param, sweepTokens[i])
            );
            emit CompoundedFor(
                sickle,
                param.claimFarm.stakingContract,
                param.claimFarm.poolIndex,
                param.depositFarm.stakingContract,
                param.depositFarm.poolIndex
            );
        }
        this.multicall(targets, data);
    }

    function harvestFor(
        IAutomation[] memory strategies,
        Sickle[] memory sickles,
        Farm[] memory farms,
        HarvestParams[] memory params,
        address[][] memory sweepTokens
    ) external onlyApprovedAutomator {
        uint256 strategiesLength = strategies.length;
        if (
            strategiesLength != sickles.length
                || strategiesLength != params.length
                || strategiesLength != sweepTokens.length
        ) {
            revert InvalidInputLength();
        }

        address[] memory targets = new address[](strategiesLength);
        bytes[] memory data = new bytes[](strategiesLength);
        for (uint256 i; i < strategiesLength; i++) {
            Sickle sickle = sickles[i];
            Farm memory farm = farms[i];
            HarvestParams memory param = params[i];
            targets[i] = address(strategies[i]);
            data[i] = abi.encodeCall(
                IAutomation.harvestFor, (sickle, farm, param, sweepTokens[i])
            );
            emit HarvestedFor(sickle, farm.stakingContract, farm.poolIndex);
        }
        this.multicall(targets, data);
    }

    function exitFor(
        IAutomation[] memory strategies,
        Sickle[] memory sickles,
        Farm[] memory farms,
        HarvestParams[] memory harvestParams,
        address[][] memory harvestSweepTokens,
        WithdrawParams[] memory withdrawParams,
        address[][] memory withdrawSweepTokens
    ) external onlyApprovedAutomator {
        uint256 strategiesLength = strategies.length;
        if (
            strategiesLength != sickles.length
                || strategiesLength != farms.length
                || strategiesLength != harvestParams.length
                || strategiesLength != withdrawParams.length
                || strategiesLength != harvestSweepTokens.length
                || strategiesLength != withdrawSweepTokens.length
        ) {
            revert InvalidInputLength();
        }

        address[] memory targets = new address[](strategiesLength);
        bytes[] memory data = new bytes[](strategiesLength);
        for (uint256 i; i < strategiesLength; i++) {
            targets[i] = address(strategies[i]);
            data[i] = abi.encodeCall(
                IAutomation.exitFor,
                (
                    sickles[i],
                    farms[i],
                    harvestParams[i],
                    harvestSweepTokens[i],
                    withdrawParams[i],
                    withdrawSweepTokens[i]
                )
            );
            emit ExitedFor(
                sickles[i], farms[i].stakingContract, farms[i].poolIndex
            );
        }
        this.multicall(targets, data);
    }

    // NFT Automator functions
    // Validation is done in the NftAutomation contract

    function harvestFor(
        INftAutomation[] memory strategies,
        Sickle[] memory sickles,
        NftPosition[] memory positions,
        NftHarvest[] memory params
    ) external onlyApprovedAutomator {
        uint256 strategiesLength = strategies.length;
        if (
            strategiesLength != sickles.length
                || strategiesLength != positions.length
                || strategiesLength != params.length
        ) {
            revert InvalidInputLength();
        }

        address[] memory targets = new address[](strategiesLength);
        bytes[] memory data = new bytes[](strategiesLength);
        for (uint256 i; i < strategiesLength; i++) {
            Sickle sickle = sickles[i];
            NftPosition memory position = positions[i];
            targets[i] = address(strategies[i]);
            data[i] = abi.encodeCall(
                INftAutomation.harvestFor, (sickle, position, params[i])
            );
            emit NftHarvestedFor(
                sickle, address(position.nft), position.tokenId
            );
        }
        this.multicall(targets, data);
    }

    function compoundFor(
        INftAutomation[] memory strategies,
        Sickle[] memory sickles,
        NftPosition[] memory positions,
        NftCompound[] memory params,
        bool[] memory inPlace,
        address[][] memory sweepTokens
    ) external onlyApprovedAutomator {
        uint256 strategiesLength = strategies.length;
        if (
            strategiesLength != sickles.length
                || strategiesLength != positions.length
                || strategiesLength != params.length
                || strategiesLength != sweepTokens.length
        ) {
            revert InvalidInputLength();
        }

        address[] memory targets = new address[](strategiesLength);
        bytes[] memory data = new bytes[](strategiesLength);
        for (uint256 i; i < strategiesLength; i++) {
            Sickle sickle = sickles[i];
            NftPosition memory position = positions[i];
            targets[i] = address(strategies[i]);
            data[i] = abi.encodeCall(
                INftAutomation.compoundFor,
                (sickle, position, params[i], inPlace[i], sweepTokens[i])
            );
            emit NftCompoundedFor(
                sickle, address(position.nft), position.tokenId
            );
        }
        this.multicall(targets, data);
    }

    function exitFor(
        INftAutomation[] memory strategies,
        Sickle[] memory sickles,
        NftPosition[] memory positions,
        NftHarvest[] memory harvestParams,
        NftWithdraw[] memory withdrawParams,
        address[][] memory sweepTokens
    ) external onlyApprovedAutomator {
        uint256 strategiesLength = strategies.length;
        if (
            strategiesLength != sickles.length
                || strategiesLength != positions.length
                || strategiesLength != harvestParams.length
                || strategiesLength != withdrawParams.length
                || strategiesLength != sweepTokens.length
        ) {
            revert InvalidInputLength();
        }

        address[] memory targets = new address[](strategiesLength);
        bytes[] memory data = new bytes[](strategiesLength);
        for (uint256 i; i < strategiesLength; i++) {
            Sickle sickle = sickles[i];
            NftPosition memory position = positions[i];
            targets[i] = address(strategies[i]);
            data[i] = abi.encodeCall(
                INftAutomation.exitFor,
                (
                    sickle,
                    position,
                    harvestParams[i],
                    withdrawParams[i],
                    sweepTokens[i]
                )
            );
            emit NftExitedFor(sickle, address(position.nft), position.tokenId);
        }
        this.multicall(targets, data);
    }

    function rebalanceFor(
        INftAutomation[] memory strategies,
        Sickle[] memory sickles,
        NftRebalance[] memory params,
        address[][] memory sweepTokens
    ) external onlyApprovedAutomator {
        uint256 strategiesLength = strategies.length;
        if (
            strategiesLength != sickles.length
                || strategiesLength != params.length
                || strategiesLength != sweepTokens.length
        ) {
            revert InvalidInputLength();
        }

        address[] memory targets = new address[](strategiesLength);
        bytes[] memory data = new bytes[](strategiesLength);
        for (uint256 i; i < strategiesLength; i++) {
            NftRebalance memory param = params[i];
            Sickle sickle = sickles[i];
            targets[i] = address(strategies[i]);
            data[i] = abi.encodeCall(
                INftAutomation.rebalanceFor, (sickle, param, sweepTokens[i])
            );
            emit NftRebalancedFor(
                sickle, address(param.position.nft), param.position.tokenId
            );
        }
        this.multicall(targets, data);
    }

    // Internal

    function _setApprovedAutomator(
        address payable approvedAutomator_
    ) internal {
        if (approvedAutomator_ == address(0)) revert InvalidAutomator();
        isApprovedAutomator[approvedAutomator_] = true;
        approvedAutomators.push(approvedAutomator_);
        approvedAutomatorsLength++;
        emit ApprovedAutomatorSet(approvedAutomator_);
    }
}
IUniswapV3Pool.sol 203 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods
/// will always return the same values
interface IUniswapV3PoolImmutables {
    /// @notice The contract that deployed the pool, which must adhere to the
    /// IUniswapV3Factory interface
    /// @return The contract address
    function factory() external view returns (address);

    /// @notice The first of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token0() external view returns (address);

    /// @notice The second of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token1() external view returns (address);

    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
    /// @return The fee
    function fee() external view returns (uint24);

    /// @notice The pool tick spacing
    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and
    /// always positive
    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick,
    /// i.e., ..., -6, -3, 0, 3, 6, ...
    /// This value is an int24 to avoid casting even though it is always
    /// positive.
    /// @return The tick spacing
    function tickSpacing() external view returns (int24);

    /// @notice The maximum amount of position liquidity that can use any tick
    /// in the range
    /// @dev This parameter is enforced per tick to prevent liquidity from
    /// overflowing a uint128 at any point, and
    /// also prevents out-of-range liquidity from being used to prevent adding
    /// in-range liquidity to a pool
    /// @return The max amount of liquidity per tick
    function maxLiquidityPerTick() external view returns (uint128);
}

/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any
/// frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
    /// @notice The 0th storage slot in the pool stores many values, and is
    /// exposed as a single method to save gas
    /// when accessed externally.
    /// @return sqrtPriceX96 The current price of the pool as a
    /// sqrt(token1/token0) Q64.96 value
    /// @return tick The current tick of the pool, i.e. according to the last
    /// tick transition that was run.
    /// This value may not always be equal to
    /// SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
    /// boundary.
    /// @return observationIndex The index of the last oracle observation that
    /// was written,
    /// @return observationCardinality The current maximum number of
    /// observations stored in the pool,
    /// @return observationCardinalityNext The next maximum number of
    /// observations, to be updated when the observation.
    /// @return feeProtocol The protocol fee for both tokens of the pool.
    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted
    /// 4 bits and the protocol fee of token0
    /// is the lower 4 bits. Used as the denominator of a fraction of the swap
    /// fee, e.g. 4 means 1/4th of the swap fee.
    /// unlocked Whether the pool is currently locked to reentrancy
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );

    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit
    /// of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal0X128() external view returns (uint256);

    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit
    /// of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal1X128() external view returns (uint256);

    /// @notice The amounts of token0 and token1 that are owed to the protocol
    /// @dev Protocol fees will never exceed uint128 max in either token
    function protocolFees()
        external
        view
        returns (uint128 token0, uint128 token1);

    /// @notice The currently in range liquidity available to the pool
    /// @dev This value has no relationship to the total liquidity across all
    /// ticks
    /// @return The liquidity at the current price of the pool
    function liquidity() external view returns (uint128);

    /// @notice Look up information about a specific tick in the pool
    /// @param tick The tick to look up
    /// @return liquidityGross the total amount of position liquidity that uses
    /// the pool either as tick lower or
    /// tick upper
    /// @return liquidityNet how much liquidity changes when the pool price
    /// crosses the tick,
    /// @return feeGrowthOutside0X128 the fee growth on the other side of the
    /// tick from the current tick in token0,
    /// @return feeGrowthOutside1X128 the fee growth on the other side of the
    /// tick from the current tick in token1,
    /// @return tickCumulativeOutside the cumulative tick value on the other
    /// side of the tick from the current tick
    /// @return secondsPerLiquidityOutsideX128 the seconds spent per liquidity
    /// on the other side of the tick from the current tick,
    /// @return secondsOutside the seconds spent on the other side of the tick
    /// from the current tick,
    /// @return initialized Set to true if the tick is initialized, i.e.
    /// liquidityGross is greater than 0, otherwise equal to false.
    /// Outside values can only be used if the tick is initialized, i.e. if
    /// liquidityGross is greater than 0.
    /// In addition, these values are only relative and must be used only in
    /// comparison to previous snapshots for
    /// a specific position.
    function ticks(int24 tick)
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint256 feeGrowthOutside0X128,
            uint256 feeGrowthOutside1X128,
            int56 tickCumulativeOutside,
            uint160 secondsPerLiquidityOutsideX128,
            uint32 secondsOutside,
            bool initialized
        );

    /// @notice Returns 256 packed tick initialized boolean values. See
    /// TickBitmap for more information
    function tickBitmap(int16 wordPosition) external view returns (uint256);

    /// @notice Returns the information about a position by the position's key
    /// @param key The position's key is a hash of a preimage composed by the
    /// owner, tickLower and tickUpper
    /// @return liquidity The amount of liquidity in the position,
    /// @return feeGrowthInside0LastX128 fee growth of token0 inside the tick
    /// range as of the last mint/burn/poke,
    /// @return feeGrowthInside1LastX128 fee growth of token1 inside the tick
    /// range as of the last mint/burn/poke,
    /// @return tokensOwed0 the computed amount of token0 owed to the position
    /// as of the last mint/burn/poke,
    /// @return tokensOwed1 the computed amount of token1 owed to the position
    /// as of the last mint/burn/poke
    function positions(bytes32 key)
        external
        view
        returns (
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    /// @notice Returns data about a specific observation index
    /// @param index The element of the observations array to fetch
    /// @dev You most likely want to use #observe() instead of this method to
    /// get an observation as of some amount of time
    /// ago, rather than at a specific index in the array.
    /// @return blockTimestamp The timestamp of the observation,
    /// @return tickCumulative the tick multiplied by seconds elapsed for the
    /// life of the pool as of the observation timestamp,
    /// @return secondsPerLiquidityCumulativeX128 the seconds per in range
    /// liquidity for the life of the pool as of the observation timestamp,
    /// @return initialized whether the observation has been initialized and the
    /// values are safe to use
    function observations(uint256 index)
        external
        view
        returns (
            uint32 blockTimestamp,
            int56 tickCumulative,
            uint160 secondsPerLiquidityCumulativeX128,
            bool initialized
        );
}

interface IUniswapV3Pool is IUniswapV3PoolImmutables, IUniswapV3PoolState {
    function flash(
        address recipient,
        uint256 amount0,
        uint256 amount1,
        bytes calldata data
    ) external;
}
Admin.sol 48 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/// @title Admin contract
/// @author vfat.tools
/// @notice Provides an administration mechanism allowing restricted functions
abstract contract Admin {
    /// ERRORS ///

    /// @notice Thrown when the caller is not the admin
    error NotAdminError(); //0xb5c42b3b

    /// EVENTS ///

    /// @notice Emitted when a new admin is set
    /// @param oldAdmin Address of the old admin
    /// @param newAdmin Address of the new admin
    event AdminSet(address oldAdmin, address newAdmin);

    /// STORAGE ///

    /// @notice Address of the current admin
    address public admin;

    /// MODIFIERS ///

    /// @dev Restricts a function to the admin
    modifier onlyAdmin() {
        if (msg.sender != admin) revert NotAdminError();
        _;
    }

    /// WRITE FUNCTIONS ///

    /// @param admin_ Address of the admin
    constructor(address admin_) {
        emit AdminSet(admin, admin_);
        admin = admin_;
    }

    /// @notice Sets a new admin
    /// @param newAdmin Address of the new admin
    /// @custom:access Restricted to protocol admin.
    function setAdmin(address newAdmin) external onlyAdmin {
        emit AdminSet(admin, newAdmin);
        admin = newAdmin;
    }
}
NonDelegateMulticall.sol 80 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { SickleStorage } from "contracts/base/SickleStorage.sol";
import { SickleRegistry } from "contracts/SickleRegistry.sol";

/// @title Multicall contract
/// @author vfat.tools
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract NonDelegateMulticall is SickleStorage {
    /// ERRORS ///

    error MulticallParamsMismatchError(); // 0xc1e637c9

    /// @notice Thrown when the target contract is not whitelisted
    /// @param target Address of the non-whitelisted target
    error TargetNotWhitelisted(address target); // 0x47ccabe7

    /// @notice Thrown when the caller is not whitelisted
    /// @param caller Address of the non-whitelisted caller
    error CallerNotWhitelisted(address caller); // 0x252c8273

    /// STORAGE ///

    /// @notice Address of the SickleRegistry contract
    /// @dev Needs to be immutable so that it's accessible for Sickle proxies
    SickleRegistry public immutable registry;

    /// INITIALIZATION ///

    /// @param registry_ Address of the SickleRegistry contract
    constructor(SickleRegistry registry_) initializer {
        registry = registry_;
    }

    /// WRITE FUNCTIONS ///

    /// @notice Batch multiple calls together (calls or delegatecalls)
    /// @param targets Array of targets to call
    /// @param data Array of data to pass with the calls
    function multicall(
        address[] calldata targets,
        bytes[] calldata data
    ) external payable {
        if (targets.length != data.length) {
            revert MulticallParamsMismatchError();
        }

        if (!registry.isWhitelistedCaller(msg.sender)) {
            revert CallerNotWhitelisted(msg.sender);
        }

        for (uint256 i = 0; i != data.length;) {
            if (targets[i] == address(0)) {
                unchecked {
                    ++i;
                }
                continue; // No-op
            }

            if (targets[i] != address(this)) {
                if (!registry.isWhitelistedTarget(targets[i])) {
                    revert TargetNotWhitelisted(targets[i]);
                }
            }

            (bool success, bytes memory result) = targets[i].call(data[i]);

            if (!success) {
                if (result.length == 0) revert();
                assembly {
                    revert(add(32, result), mload(result))
                }
            }
            unchecked {
                ++i;
            }
        }
    }
}
Sickle.sol 69 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { SickleStorage } from "contracts/base/SickleStorage.sol";
import { Multicall } from "contracts/base/Multicall.sol";
import { SickleRegistry } from "contracts/SickleRegistry.sol";

/// @title Sickle contract
/// @author vfat.tools
/// @notice Sickle facilitates farming and interactions with Masterchef
/// contracts
/// @dev Base contract inheriting from all the other "manager" contracts
contract Sickle is SickleStorage, Multicall {
    /// @notice Function to receive ETH
    receive() external payable { }

    /// @param sickleRegistry_ Address of the SickleRegistry contract
    constructor(
        SickleRegistry sickleRegistry_
    ) initializer Multicall(sickleRegistry_) {
        _Sickle_initialize(address(0), address(0));
    }

    /// @param sickleOwner_ Address of the Sickle owner
    function initialize(
        address sickleOwner_,
        address approved_
    ) external initializer {
        _Sickle_initialize(sickleOwner_, approved_);
    }

    /// INTERNALS ///

    function _Sickle_initialize(
        address sickleOwner_,
        address approved_
    ) internal {
        SickleStorage._SickleStorage_initialize(sickleOwner_, approved_);
    }

    function onERC721Received(
        address, // operator
        address, // from
        uint256, // tokenId
        bytes calldata // data
    ) external pure returns (bytes4) {
        return this.onERC721Received.selector;
    }

    function onERC1155Received(
        address, // operator
        address, // from
        uint256, // id
        uint256, // value
        bytes calldata // data
    ) external pure returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    function onERC1155BatchReceived(
        address, // operator
        address, // from
        uint256[] calldata, // ids
        uint256[] calldata, // values
        bytes calldata // data
    ) external pure returns (bytes4) {
        return this.onERC1155BatchReceived.selector;
    }
}
SickleRegistry.sol 139 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { Admin } from "contracts/base/Admin.sol";

library SickleRegistryEvents {
    event CollectorChanged(address newCollector);
    event FeesUpdated(bytes32[] feeHashes, uint256[] feesInBP);
    event ReferralCodeCreated(bytes32 indexed code, address indexed referrer);

    // Multicall caller and target whitelist status changes
    event CallerStatusChanged(address caller, bool isWhitelisted);
    event TargetStatusChanged(address target, bool isWhitelisted);
}

/// @title SickleRegistry contract
/// @author vfat.tools
/// @notice Manages the whitelisted contracts and the collector address
contract SickleRegistry is Admin {
    /// ERRORS ///

    error ArrayLengthMismatch(); // 0xa24a13a6
    error FeeAboveMaxLimit(); // 0xd6cf7b5e
    error InvalidReferralCode(); // 0xe55b4629

    /// STORAGE ///

    /// @notice Address of the fee collector
    address public collector;

    /// @notice Tracks the contracts that can be called through Sickle multicall
    /// @return True if the contract is a whitelisted target
    mapping(address => bool) public isWhitelistedTarget;

    /// @notice Tracks the contracts that can call Sickle multicall
    /// @return True if the contract is a whitelisted caller
    mapping(address => bool) public isWhitelistedCaller;

    /// @notice Keeps track of the referrers and their associated code
    mapping(bytes32 => address) public referralCodes;

    /// @notice Mapping for fee hashes (hash of the strategy contract addresses
    /// and the function selectors) and their associated fees
    /// @return The fee in basis points to apply to the transaction amount
    mapping(bytes32 => uint256) public feeRegistry;

    /// WRITE FUNCTIONS ///

    /// @param admin_ Address of the admin
    /// @param collector_ Address of the collector
    constructor(address admin_, address collector_) Admin(admin_) {
        collector = collector_;
    }

    /// @notice Updates the whitelist status for multiple multicall targets
    /// @param targets Addresses of the contracts to update
    /// @param isApproved New status for the contracts
    /// @custom:access Restricted to protocol admin.
    function setWhitelistedTargets(
        address[] calldata targets,
        bool isApproved
    ) external onlyAdmin {
        for (uint256 i; i < targets.length;) {
            isWhitelistedTarget[targets[i]] = isApproved;
            emit SickleRegistryEvents.TargetStatusChanged(
                targets[i], isApproved
            );

            unchecked {
                ++i;
            }
        }
    }

    /// @notice Updates the fee collector address
    /// @param newCollector Address of the new fee collector
    /// @custom:access Restricted to protocol admin.
    function updateCollector(address newCollector) external onlyAdmin {
        collector = newCollector;
        emit SickleRegistryEvents.CollectorChanged(newCollector);
    }

    /// @notice Update the whitelist status for multiple multicall callers
    /// @param callers Addresses of the callers
    /// @param isApproved New status for the caller
    /// @custom:access Restricted to protocol admin.
    function setWhitelistedCallers(
        address[] calldata callers,
        bool isApproved
    ) external onlyAdmin {
        for (uint256 i; i < callers.length;) {
            isWhitelistedCaller[callers[i]] = isApproved;
            emit SickleRegistryEvents.CallerStatusChanged(
                callers[i], isApproved
            );

            unchecked {
                ++i;
            }
        }
    }

    /// @notice Associates a referral code to the address of the caller
    function setReferralCode(bytes32 referralCode) external {
        if (referralCodes[referralCode] != address(0)) {
            revert InvalidReferralCode();
        }

        referralCodes[referralCode] = msg.sender;
        emit SickleRegistryEvents.ReferralCodeCreated(referralCode, msg.sender);
    }

    /// @notice Update the fees for multiple strategy functions
    /// @param feeHashes Array of fee hashes
    /// @param feesArray Array of fees to apply (in basis points)
    /// @custom:access Restricted to protocol admin.
    function setFees(
        bytes32[] calldata feeHashes,
        uint256[] calldata feesArray
    ) external onlyAdmin {
        if (feeHashes.length != feesArray.length) {
            revert ArrayLengthMismatch();
        }

        for (uint256 i = 0; i < feeHashes.length;) {
            if (feesArray[i] <= 500) {
                // maximum fee of 5%
                feeRegistry[feeHashes[i]] = feesArray[i];
            } else {
                revert FeeAboveMaxLimit();
            }
            unchecked {
                ++i;
            }
        }

        emit SickleRegistryEvents.FeesUpdated(feeHashes, feesArray);
    }
}
IAutomation.sol 34 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { Sickle } from "contracts/Sickle.sol";
import {
    Farm,
    HarvestParams,
    CompoundParams,
    WithdrawParams
} from "contracts/structs/FarmStrategyStructs.sol";

interface IAutomation {
    function harvestFor(
        Sickle sickle,
        Farm calldata farm,
        HarvestParams calldata params,
        address[] calldata sweepTokens
    ) external;

    function compoundFor(
        Sickle sickle,
        CompoundParams calldata params,
        address[] calldata sweepTokens
    ) external;

    function exitFor(
        Sickle sickle,
        Farm calldata farm,
        HarvestParams calldata harvestParams,
        address[] calldata harvestSweepTokens,
        WithdrawParams calldata withdrawParams,
        address[] calldata withdrawSweepTokens
    ) external;
}
INftAutomation.sol 43 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IUniswapV3Pool } from
    "contracts/interfaces/external/uniswap/IUniswapV3Pool.sol";
import { Sickle } from "contracts/Sickle.sol";
import {
    NftPosition,
    NftRebalance,
    NftHarvest,
    NftWithdraw,
    NftCompound
} from "contracts/structs/NftFarmStrategyStructs.sol";

interface INftAutomation {
    function rebalanceFor(
        Sickle sickle,
        NftRebalance calldata rebalance,
        address[] calldata sweepTokens
    ) external;

    function harvestFor(
        Sickle sickle,
        NftPosition calldata position,
        NftHarvest calldata params
    ) external;

    function compoundFor(
        Sickle sickle,
        NftPosition calldata position,
        NftCompound calldata params,
        bool inPlace,
        address[] memory sweepTokens
    ) external;

    function exitFor(
        Sickle sickle,
        NftPosition calldata position,
        NftHarvest calldata harvestParams,
        NftWithdraw calldata withdrawParams,
        address[] memory sweepTokens
    ) external;
}
NftFarmStrategyStructs.sol 70 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IUniswapV3Pool } from
    "contracts/interfaces/external/uniswap/IUniswapV3Pool.sol";
import { INonfungiblePositionManager } from
    "contracts/interfaces/external/uniswap/INonfungiblePositionManager.sol";
import { NftZapIn, NftZapOut } from "contracts/structs/NftZapStructs.sol";
import { SwapParams } from "contracts/structs/LiquidityStructs.sol";
import { Farm } from "contracts/structs/FarmStrategyStructs.sol";

struct NftPosition {
    Farm farm;
    INonfungiblePositionManager nft;
    uint256 tokenId;
}

struct NftIncrease {
    address[] tokensIn;
    uint256[] amountsIn;
    NftZapIn zap;
    bytes extraData;
}

struct NftDeposit {
    Farm farm;
    INonfungiblePositionManager nft;
    NftIncrease increase;
}

struct NftWithdraw {
    NftZapOut zap;
    address[] tokensOut;
    bytes extraData;
}

struct SimpleNftHarvest {
    address[] rewardTokens;
    uint128 amount0Max;
    uint128 amount1Max;
    bytes extraData;
}

struct NftHarvest {
    SimpleNftHarvest harvest;
    SwapParams[] swaps;
    address[] outputTokens;
    address[] sweepTokens;
}

struct NftCompound {
    SimpleNftHarvest harvest;
    NftZapIn zap;
}

struct NftRebalance {
    IUniswapV3Pool pool;
    NftPosition position;
    NftHarvest harvest;
    NftWithdraw withdraw;
    NftIncrease increase;
}

struct NftMove {
    IUniswapV3Pool pool;
    NftPosition position;
    NftHarvest harvest;
    NftWithdraw withdraw;
    NftDeposit deposit;
}
FarmStrategyStructs.sol 57 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { ZapIn, ZapOut } from "contracts/libraries/ZapLib.sol";
import { SwapParams } from "contracts/structs/LiquidityStructs.sol";

struct Farm {
    address stakingContract;
    uint256 poolIndex;
}

struct DepositParams {
    Farm farm;
    address[] tokensIn;
    uint256[] amountsIn;
    ZapIn zap;
    bytes extraData;
}

struct WithdrawParams {
    bytes extraData;
    ZapOut zap;
    address[] tokensOut;
}

struct HarvestParams {
    SwapParams[] swaps;
    bytes extraData;
    address[] tokensOut;
}

struct CompoundParams {
    Farm claimFarm;
    bytes claimExtraData;
    address[] rewardTokens;
    ZapIn zap;
    Farm depositFarm;
    bytes depositExtraData;
}

struct SimpleDepositParams {
    Farm farm;
    address lpToken;
    uint256 amountIn;
    bytes extraData;
}

struct SimpleHarvestParams {
    address[] rewardTokens;
    bytes extraData;
}

struct SimpleWithdrawParams {
    address lpToken;
    uint256 amountOut;
    bytes extraData;
}
SickleStorage.sol 74 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { Initializable } from
    "@openzeppelin/contracts/proxy/utils/Initializable.sol";

library SickleStorageEvents {
    event ApprovedAddressChanged(address newApproved);
}

/// @title SickleStorage contract
/// @author vfat.tools
/// @notice Base storage of the Sickle contract
/// @dev This contract needs to be inherited by stub contracts meant to be used
/// with `delegatecall`
abstract contract SickleStorage is Initializable {
    /// ERRORS ///

    /// @notice Thrown when the caller is not the owner of the Sickle contract
    error NotOwnerError(); // 0x74a21527

    /// @notice Thrown when the caller is not a strategy contract or the
    /// Flashloan Stub
    error NotStrategyError(); // 0x4581ba62

    /// STORAGE ///

    /// @notice Address of the owner
    address public owner;

    /// @notice An address that can be set by the owner of the Sickle contract
    /// in order to trigger specific functions.
    address public approved;

    /// MODIFIERS ///

    /// @dev Restricts a function call to the owner, however if the admin was
    /// not set yet,
    /// the modifier will not restrict the call, this allows the SickleFactory
    /// to perform
    /// some calls on the user's behalf before passing the admin rights to them
    modifier onlyOwner() {
        if (msg.sender != owner) revert NotOwnerError();
        _;
    }

    /// INITIALIZATION ///

    /// @param owner_ Address of the owner of this Sickle contract
    function _SickleStorage_initialize(
        address owner_,
        address approved_
    ) internal onlyInitializing {
        owner = owner_;
        approved = approved_;
    }

    /// WRITE FUNCTIONS ///

    /// @notice Sets the approved address of this Sickle
    /// @param newApproved Address meant to be approved by the owner
    function setApproved(address newApproved) external onlyOwner {
        approved = newApproved;
        emit SickleStorageEvents.ApprovedAddressChanged(newApproved);
    }

    /// @notice Checks if `caller` is either the owner of the Sickle contract
    /// or was approved by them
    /// @param caller Address to check
    /// @return True if `caller` is either the owner of the Sickle contract
    function isOwnerOrApproved(address caller) public view returns (bool) {
        return caller == owner || caller == approved;
    }
}
Multicall.sol 81 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { SickleStorage } from "contracts/base/SickleStorage.sol";
import { SickleRegistry } from "contracts/SickleRegistry.sol";

/// @title Multicall contract
/// @author vfat.tools
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall is SickleStorage {
    /// ERRORS ///

    error MulticallParamsMismatchError(); // 0xc1e637c9

    /// @notice Thrown when the target contract is not whitelisted
    /// @param target Address of the non-whitelisted target
    error TargetNotWhitelisted(address target); // 0x47ccabe7

    /// @notice Thrown when the caller is not whitelisted
    /// @param caller Address of the non-whitelisted caller
    error CallerNotWhitelisted(address caller); // 0x252c8273

    /// STORAGE ///

    /// @notice Address of the SickleRegistry contract
    /// @dev Needs to be immutable so that it's accessible for Sickle proxies
    SickleRegistry public immutable registry;

    /// INITIALIZATION ///

    /// @param registry_ Address of the SickleRegistry contract
    constructor(SickleRegistry registry_) initializer {
        registry = registry_;
    }

    /// WRITE FUNCTIONS ///

    /// @notice Batch multiple calls together (calls or delegatecalls)
    /// @param targets Array of targets to call
    /// @param data Array of data to pass with the calls
    function multicall(
        address[] calldata targets,
        bytes[] calldata data
    ) external payable {
        if (targets.length != data.length) {
            revert MulticallParamsMismatchError();
        }

        if (!registry.isWhitelistedCaller(msg.sender)) {
            revert CallerNotWhitelisted(msg.sender);
        }

        for (uint256 i = 0; i != data.length;) {
            if (targets[i] == address(0)) {
                unchecked {
                    ++i;
                }
                continue; // No-op
            }

            if (targets[i] != address(this)) {
                if (!registry.isWhitelistedTarget(targets[i])) {
                    revert TargetNotWhitelisted(targets[i]);
                }
            }

            (bool success, bytes memory result) =
                targets[i].delegatecall(data[i]);

            if (!success) {
                if (result.length == 0) revert();
                assembly {
                    revert(add(32, result), mload(result))
                }
            }
            unchecked {
                ++i;
            }
        }
    }
}
INonfungiblePositionManager.sol 85 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC721Enumerable } from
    "openzeppelin-contracts/contracts/interfaces/IERC721Enumerable.sol";

interface INonfungiblePositionManager is IERC721Enumerable {
    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    function increaseLiquidity(IncreaseLiquidityParams memory params)
        external
        payable
        returns (uint256 amount0, uint256 amount1, uint256 liquidity);

    function decreaseLiquidity(DecreaseLiquidityParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);

    function mint(MintParams memory params)
        external
        payable
        returns (uint256 tokenId, uint256 amount0, uint256 amount1);

    function collect(CollectParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);

    function burn(uint256 tokenId) external payable;

    function positions(uint256 tokenId)
        external
        view
        returns (
            uint96 nonce,
            address operator,
            address token0,
            address token1,
            uint24 fee,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );
}
NftZapStructs.sol 18 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { SwapParams } from "contracts/structs/LiquidityStructs.sol";
import {
    NftAddLiquidity,
    NftRemoveLiquidity
} from "contracts/structs/NftLiquidityStructs.sol";

struct NftZapIn {
    SwapParams[] swaps;
    NftAddLiquidity addLiquidityParams;
}

struct NftZapOut {
    NftRemoveLiquidity removeLiquidityParams;
    SwapParams[] swaps;
}
LiquidityStructs.sol 36 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

struct AddLiquidityParams {
    address router;
    address lpToken;
    address[] tokens;
    uint256[] desiredAmounts;
    uint256[] minAmounts;
    bytes extraData;
}

struct RemoveLiquidityParams {
    address router;
    address lpToken;
    address[] tokens;
    uint256 lpAmountIn;
    uint256[] minAmountsOut;
    bytes extraData;
}

struct SwapParams {
    address router;
    uint256 amountIn;
    uint256 minAmountOut;
    address tokenIn;
    bytes extraData;
}

struct GetAmountOutParams {
    address router;
    address lpToken;
    address tokenIn;
    address tokenOut;
    uint256 amountIn;
}
ZapLib.sol 133 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import {
    SwapParams,
    AddLiquidityParams
} from "contracts/structs/LiquidityStructs.sol";
import { ILiquidityConnector } from
    "contracts/interfaces/ILiquidityConnector.sol";
import { ConnectorRegistry } from "contracts/ConnectorRegistry.sol";
import { DelegateModule } from "contracts/modules/DelegateModule.sol";
import { ZapIn, ZapOut } from "contracts/structs/ZapStructs.sol";
import { IZapLib } from "contracts/interfaces/libraries/IZapLib.sol";
import { ISwapLib } from "contracts/interfaces/libraries/ISwapLib.sol";

contract ZapLib is DelegateModule, IZapLib {
    error LiquidityAmountError(); // 0x4d0ab6b4

    ISwapLib public immutable swapLib;
    ConnectorRegistry public immutable connectorRegistry;

    constructor(ConnectorRegistry connectorRegistry_, ISwapLib swapLib_) {
        connectorRegistry = connectorRegistry_;
        swapLib = swapLib_;
    }

    function zapIn(
        ZapIn memory zap
    ) external payable {
        uint256 swapDataLength = zap.swaps.length;
        for (uint256 i; i < swapDataLength;) {
            _delegateTo(
                address(swapLib), abi.encodeCall(ISwapLib.swap, (zap.swaps[i]))
            );
            unchecked {
                i++;
            }
        }

        if (zap.addLiquidityParams.lpToken == address(0)) {
            return;
        }

        bool atLeastOneNonZero = false;

        AddLiquidityParams memory addLiquidityParams = zap.addLiquidityParams;
        uint256 addLiquidityParamsTokensLength =
            addLiquidityParams.tokens.length;
        for (uint256 i; i < addLiquidityParamsTokensLength; i++) {
            if (addLiquidityParams.tokens[i] == address(0)) {
                continue;
            }
            if (addLiquidityParams.desiredAmounts[i] == 0) {
                addLiquidityParams.desiredAmounts[i] = IERC20(
                    addLiquidityParams.tokens[i]
                ).balanceOf(address(this));
            }
            if (addLiquidityParams.desiredAmounts[i] > 0) {
                atLeastOneNonZero = true;
                // In case there is USDT or similar dust approval, revoke it
                SafeTransferLib.safeApprove(
                    addLiquidityParams.tokens[i], addLiquidityParams.router, 0
                );
                SafeTransferLib.safeApprove(
                    addLiquidityParams.tokens[i],
                    addLiquidityParams.router,
                    addLiquidityParams.desiredAmounts[i]
                );
            }
        }

        if (!atLeastOneNonZero) {
            revert LiquidityAmountError();
        }

        address routerConnector =
            connectorRegistry.connectorOf(addLiquidityParams.router);

        _delegateTo(
            routerConnector,
            abi.encodeCall(
                ILiquidityConnector.addLiquidity, (addLiquidityParams)
            )
        );

        for (uint256 i; i < addLiquidityParamsTokensLength;) {
            if (addLiquidityParams.tokens[i] != address(0)) {
                // Revoke any dust approval in case the amount was estimated
                SafeTransferLib.safeApprove(
                    addLiquidityParams.tokens[i], addLiquidityParams.router, 0
                );
            }
            unchecked {
                i++;
            }
        }
    }

    function zapOut(
        ZapOut memory zap
    ) external {
        if (zap.removeLiquidityParams.lpToken != address(0)) {
            if (zap.removeLiquidityParams.lpAmountIn > 0) {
                SafeTransferLib.safeApprove(
                    zap.removeLiquidityParams.lpToken,
                    zap.removeLiquidityParams.router,
                    zap.removeLiquidityParams.lpAmountIn
                );
            }
            address routerConnector =
                connectorRegistry.connectorOf(zap.removeLiquidityParams.router);
            _delegateTo(
                address(routerConnector),
                abi.encodeCall(
                    ILiquidityConnector.removeLiquidity,
                    zap.removeLiquidityParams
                )
            );
        }

        uint256 swapDataLength = zap.swaps.length;
        for (uint256 i; i < swapDataLength;) {
            _delegateTo(
                address(swapLib), abi.encodeCall(ISwapLib.swap, (zap.swaps[i]))
            );
            unchecked {
                i++;
            }
        }
    }
}
Initializable.sol 165 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/Address.sol";

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

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

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

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

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

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

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

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

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}
IERC721Enumerable.sol 6 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/extensions/IERC721Enumerable.sol";
NftLiquidityStructs.sol 35 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { INonfungiblePositionManager } from
    "contracts/interfaces/external/uniswap/INonfungiblePositionManager.sol";

struct Pool {
    address token0;
    address token1;
    uint24 fee;
}

struct NftAddLiquidity {
    INonfungiblePositionManager nft;
    uint256 tokenId;
    Pool pool;
    int24 tickLower;
    int24 tickUpper;
    uint256 amount0Desired;
    uint256 amount1Desired;
    uint256 amount0Min;
    uint256 amount1Min;
    bytes extraData;
}

struct NftRemoveLiquidity {
    INonfungiblePositionManager nft;
    uint256 tokenId;
    uint128 liquidity;
    uint256 amount0Min; // For decreasing
    uint256 amount1Min;
    uint128 amount0Max; // For collecting
    uint128 amount1Max;
    bytes extraData;
}
IERC20.sol 82 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}
SafeTransferLib.sol 137 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                                ERRORS
    //////////////////////////////////////////////////////////////*/

    error ETHTransferFailed();
    error TransferFromFailed();
    error TransferFailed();
    error ApproveFailed();

    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        if (!success) revert ETHTransferFailed();
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        if (!success) revert TransferFromFailed();
    }

    function safeTransfer(
        address token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        if (!success) revert TransferFailed();
    }

    function safeApprove(
        address token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        if (!success) revert ApproveFailed();
    }
}
ILiquidityConnector.sol 27 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {
    AddLiquidityParams,
    RemoveLiquidityParams,
    SwapParams,
    GetAmountOutParams
} from "contracts/structs/LiquidityStructs.sol";

interface ILiquidityConnector {
    function addLiquidity(
        AddLiquidityParams memory addLiquidityParams
    ) external payable;

    function removeLiquidity(
        RemoveLiquidityParams memory removeLiquidityParams
    ) external;

    function swapExactTokensForTokens(
        SwapParams memory swap
    ) external payable;

    function getAmountOut(
        GetAmountOutParams memory getAmountOutParams
    ) external view returns (uint256);
}
ConnectorRegistry.sol 150 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { Admin } from "contracts/base/Admin.sol";
import { TimelockAdmin } from "contracts/base/TimelockAdmin.sol";

error ConnectorNotRegistered(address target);

interface ICustomConnectorRegistry {
    function connectorOf(address target) external view returns (address);
}

contract ConnectorRegistry is Admin, TimelockAdmin {
    event ConnectorChanged(address target, address connector);
    event CustomRegistryAdded(address registry);
    event CustomRegistryRemoved(address registry);

    error ConnectorAlreadySet(address target);
    error ConnectorNotSet(address target);

    ICustomConnectorRegistry[] public customRegistries;
    mapping(ICustomConnectorRegistry => bool) public isCustomRegistry;

    mapping(address target => address connector) private connectors_;

    constructor(
        address admin_,
        address timelockAdmin_
    ) Admin(admin_) TimelockAdmin(timelockAdmin_) { }

    /// @notice Update connector addresses for a batch of targets.
    /// @dev Controls which connector contracts are used for the specified
    /// targets.
    /// @custom:access Restricted to protocol admin.
    function setConnectors(
        address[] calldata targets,
        address[] calldata connectors
    ) external onlyAdmin {
        for (uint256 i; i != targets.length;) {
            if (connectors_[targets[i]] != address(0)) {
                revert ConnectorAlreadySet(targets[i]);
            }
            connectors_[targets[i]] = connectors[i];
            emit ConnectorChanged(targets[i], connectors[i]);

            unchecked {
                ++i;
            }
        }
    }

    function updateConnectors(
        address[] calldata targets,
        address[] calldata connectors
    ) external onlyTimelockAdmin {
        for (uint256 i; i != targets.length;) {
            if (connectors_[targets[i]] == address(0)) {
                revert ConnectorNotSet(targets[i]);
            }
            connectors_[targets[i]] = connectors[i];
            emit ConnectorChanged(targets[i], connectors[i]);

            unchecked {
                ++i;
            }
        }
    }

    /// @notice Append an address to the custom registries list.
    /// @custom:access Restricted to protocol admin.
    function addCustomRegistry(ICustomConnectorRegistry registry)
        external
        onlyAdmin
    {
        customRegistries.push(registry);
        isCustomRegistry[registry] = true;
        emit CustomRegistryAdded(address(registry));
    }

    /// @notice Replace an address in the custom registries list.
    /// @custom:access Restricted to protocol admin.
    function updateCustomRegistry(
        uint256 index,
        ICustomConnectorRegistry newRegistry
    ) external onlyTimelockAdmin {
        address oldRegistry = address(customRegistries[index]);
        isCustomRegistry[customRegistries[index]] = false;
        emit CustomRegistryRemoved(oldRegistry);
        customRegistries[index] = newRegistry;
        isCustomRegistry[newRegistry] = true;
        if (address(newRegistry) != address(0)) {
            emit CustomRegistryAdded(address(newRegistry));
        }
    }

    function connectorOf(address target) external view returns (address) {
        address connector = connectors_[target];
        if (connector != address(0)) {
            return connector;
        }

        uint256 length = customRegistries.length;
        for (uint256 i; i != length;) {
            if (address(customRegistries[i]) != address(0)) {
                try customRegistries[i].connectorOf(target) returns (
                    address _connector
                ) {
                    if (_connector != address(0)) {
                        return _connector;
                    }
                } catch {
                    // Ignore
                }
            }

            unchecked {
                ++i;
            }
        }

        revert ConnectorNotRegistered(target);
    }

    function hasConnector(address target) external view returns (bool) {
        if (connectors_[target] != address(0)) {
            return true;
        }

        uint256 length = customRegistries.length;
        for (uint256 i; i != length;) {
            if (address(customRegistries[i]) != address(0)) {
                try customRegistries[i].connectorOf(target) returns (
                    address _connector
                ) {
                    if (_connector != address(0)) {
                        return true;
                    }
                } catch {
                    // Ignore
                }

                unchecked {
                    ++i;
                }
            }
        }

        return false;
    }
}
DelegateModule.sol 20 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract DelegateModule {
    function _delegateTo(
        address to,
        bytes memory data
    ) internal returns (bytes memory) {
        (bool success, bytes memory result) = to.delegatecall(data);

        if (!success) {
            if (result.length == 0) revert();
            assembly {
                revert(add(32, result), mload(result))
            }
        }

        return result;
    }
}
ZapStructs.sol 18 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {
    SwapParams,
    AddLiquidityParams,
    RemoveLiquidityParams
} from "contracts/structs/LiquidityStructs.sol";

struct ZapIn {
    SwapParams[] swaps;
    AddLiquidityParams addLiquidityParams;
}

struct ZapOut {
    RemoveLiquidityParams removeLiquidityParams;
    SwapParams[] swaps;
}
IZapLib.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { ZapIn, ZapOut } from "contracts/structs/ZapStructs.sol";

interface IZapLib {
    function zapIn(
        ZapIn memory zap
    ) external payable;

    function zapOut(
        ZapOut memory zap
    ) external;
}
ISwapLib.sol 14 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { SwapParams } from "contracts/structs/LiquidityStructs.sol";

interface ISwapLib {
    function swap(
        SwapParams memory swap
    ) external payable;

    function swapMultiple(
        SwapParams[] memory swaps
    ) external;
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/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.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

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

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

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

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

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

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

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

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

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

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

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

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
IERC721Enumerable.sol 29 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/extensions/IERC721Enumerable.sol)

pragma solidity ^0.8.0;

import "../IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Enumerable is IERC721 {
    /**
     * @dev Returns the total amount of tokens stored by the contract.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns a token ID owned by `owner` at a given `index` of its token list.
     * Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
     */
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);

    /**
     * @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
     * Use along with {totalSupply} to enumerate all tokens.
     */
    function tokenByIndex(uint256 index) external view returns (uint256);
}
ERC20.sol 206 lines
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}
TimelockAdmin.sol 52 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/// @title TimelockAdmin contract
/// @author vfat.tools
/// @notice Provides an timelockAdministration mechanism allowing restricted
/// functions
abstract contract TimelockAdmin {
    /// ERRORS ///

    /// @notice Thrown when the caller is not the timelockAdmin
    error NotTimelockAdminError();

    /// EVENTS ///

    /// @notice Emitted when a new timelockAdmin is set
    /// @param oldTimelockAdmin Address of the old timelockAdmin
    /// @param newTimelockAdmin Address of the new timelockAdmin
    event TimelockAdminSet(address oldTimelockAdmin, address newTimelockAdmin);

    /// STORAGE ///

    /// @notice Address of the current timelockAdmin
    address public timelockAdmin;

    /// MODIFIERS ///

    /// @dev Restricts a function to the timelockAdmin
    modifier onlyTimelockAdmin() {
        if (msg.sender != timelockAdmin) revert NotTimelockAdminError();
        _;
    }

    /// WRITE FUNCTIONS ///

    /// @param timelockAdmin_ Address of the timelockAdmin
    constructor(address timelockAdmin_) {
        emit TimelockAdminSet(timelockAdmin, timelockAdmin_);
        timelockAdmin = timelockAdmin_;
    }

    /// @notice Sets a new timelockAdmin
    /// @dev Can only be called by the current timelockAdmin
    /// @param newTimelockAdmin Address of the new timelockAdmin
    function setTimelockAdmin(address newTimelockAdmin)
        external
        onlyTimelockAdmin
    {
        emit TimelockAdminSet(timelockAdmin, newTimelockAdmin);
        timelockAdmin = newTimelockAdmin;
    }
}
IERC721.sol 145 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "../../utils/introspection/IERC165.sol";

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

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

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

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

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

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

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

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

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

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

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

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

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

Read Contract

admin 0xf851a440 → address
approved 0x19d40b08 → address
approvedAutomators 0xd4b12003 → address
approvedAutomatorsLength 0x5be6d062 → uint256
isApprovedAutomator 0x8a345570 → bool
isOwnerOrApproved 0xf5b3f424 → bool
owner 0x8da5cb5b → address
registry 0x7b103999 → address

Write Contract 12 functions

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

compoundFor 0x40f5ec41
address[] strategies
address[] sickles
tuple[] positions
tuple[] params
bool[] inPlace
address[][] sweepTokens
compoundFor 0xfa7e92cb
address[] strategies
address[] sickles
tuple[] params
address[][] sweepTokens
exitFor 0x9823329a
address[] strategies
address[] sickles
tuple[] farms
tuple[] harvestParams
address[][] harvestSweepTokens
tuple[] withdrawParams
address[][] withdrawSweepTokens
exitFor 0x85e71046
address[] strategies
address[] sickles
tuple[] positions
tuple[] harvestParams
tuple[] withdrawParams
address[][] sweepTokens
harvestFor 0xb7edcf26
address[] strategies
address[] sickles
tuple[] farms
tuple[] params
address[][] sweepTokens
harvestFor 0x64644fab
address[] strategies
address[] sickles
tuple[] positions
tuple[] params
multicall 0x63fb0b96
address[] targets
bytes[] data
rebalanceFor 0xd5799819
address[] strategies
address[] sickles
tuple[] params
address[][] sweepTokens
revokeApprovedAutomator 0x1ccb65ff
address approvedAutomator_
setAdmin 0x704b6c02
address newAdmin
setApproved 0xfb0289ca
address newApproved
setApprovedAutomator 0x745a7b3b
address approvedAutomator_

Recent Transactions

No transactions found for this address