Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0x6C615481E96806edBd9987B6E522A4Ea85d13659
Balance 0 ETH
Nonce 1
Code Size 21473 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

21473 bytes
0x60806040526004361061021d5760003560e01c806362b850c71161011d578063bcbef206116100b0578063e3056a341161007f578063effca70511610064578063effca705146105e1578063f235757f14610601578063fa0c95551461062157600080fd5b8063e3056a3414610592578063e43581b8146105b257600080fd5b8063bcbef2061461052a578063d9a673ef1461053d578063db8266de14610550578063df08aed51461056357600080fd5b8063910cab11116100ec578063910cab11146104b7578063a669a7f6146104d7578063ac9650d8146104ea578063bb2871a51461050a57600080fd5b806362b850c71461044a57806362dd9af31461045d5780636afdd8501461047057806389352328146104a457600080fd5b806335ac2a50116101b057806340c5710c1161017f5780635684c275116101645780635684c275146103cd578063585cc6a5146103fa5780635f963dcf1461042257600080fd5b806340c5710c1461038d578063484b3577146103a057600080fd5b806335ac2a501461032757806337b0c09d146103475780633a79d6741461035a5780633ed242b41461036d57600080fd5b80631f66925c116101ec5780631f66925c146102c05780631f8b479d146102e15780632b3297f9146102f4578063340b532f1461031457600080fd5b806301ffc9a71461022957806305ce20d61461025e5780630c340a241461027357806313f6986d146102ab57600080fd5b3661022457005b600080fd5b34801561023557600080fd5b50610249610244366004613395565b610634565b60405190151581526020015b60405180910390f35b61027161026c36600461340e565b6106cd565b005b34801561027f57600080fd5b50600054610293906001600160a01b031681565b6040516001600160a01b039091168152602001610255565b3480156102b757600080fd5b50610271610801565b6102d36102ce366004613463565b6108ad565b604051908152602001610255565b6102716102ef366004613502565b610955565b34801561030057600080fd5b50600254610293906001600160a01b031681565b61027161032236600461358e565b6109a6565b61033a6103353660046135c7565b6109b4565b6040516102559190613624565b610271610355366004613463565b610b03565b610271610368366004613463565b610b1d565b61038061037b366004613668565b610b28565b604051610255919061372f565b61027161039b3660046137b1565b610c00565b3480156103ac57600080fd5b506103c06103bb36600461387d565b610d83565b6040516102559190613914565b3480156103d957600080fd5b506103ed6103e8366004613a45565b610e48565b6040516102559190613a9a565b34801561040657600080fd5b5061029373eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b610435610430366004613ba8565b610f06565b60408051928352602083019190915201610255565b610271610458366004613bf0565b610fbb565b61027161046b366004613c87565b610ffd565b34801561047c57600080fd5b506102937f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba381565b6102d36104b2366004613d0a565b611087565b3480156104c357600080fd5b50600354610293906001600160a01b031681565b6102d36104e5366004613deb565b611132565b6104fd6104f8366004613668565b61122d565b6040516102559190613e5f565b34801561051657600080fd5b5061033a610525366004613ed4565b611310565b610271610538366004613f31565b611326565b61027161054b366004613f67565b6113f5565b61027161055e366004613fde565b61142e565b34801561056f57600080fd5b5061024961057e36600461402f565b6001546001600160a01b0391821691161490565b34801561059e57600080fd5b50600154610293906001600160a01b031681565b3480156105be57600080fd5b506102496105cd36600461402f565b6000546001600160a01b0391821691161490565b3480156105ed57600080fd5b506102716105fc36600461358e565b61149e565b34801561060d57600080fd5b5061027161061c36600461402f565b611528565b6102d361062f36600461404c565b6115d8565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f3ed242b40000000000000000000000000000000000000000000000000000000014806106c757507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b838360006106dc83838361171f565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018790526000906001600160a01b03891690635b6fd01d9060240161010060405180830381865afa15801561073e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107629190614261565b519050610770818988611827565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b0389169063ded700a6906064015b600060405180830381600087803b1580156107df57600080fd5b505af11580156107f3573d6000803e3d6000fd5b505050505050505050505050565b6001546001600160a01b03163314610845576040517f9ba0305d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018054600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b0384161782559091169091556040517fdc57ca23c46d823853915ed5a090ca0ee9db5eb6a46f5c58e1c9158de861fd769190a1565b6000838360026108be83838361171f565b6040517f17621890000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0386811660248301528816906317621890906044016020604051808303816000875af1158015610926573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061094a919061431d565b979650505050505050565b6040517f4faa38830000000000000000000000000000000000000000000000000000000081526001600160a01b03891690634faa3883906107c5908a908a908a908a908a908a908a906004016144da565b6109b082826118da565b5050565b606060005b83811015610a695760008585838181106109d5576109d561451b565b90506020028101906109e7919061454a565b6109f5906020810190614588565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201829052509394505050505b8151811015610a5f57610a5788838381518110610a4857610a4861451b565b6020026020010151600261171f565b600101610a29565b50506001016109b9565b506040517f480b37960000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063480b379690610ab3908790879087906004016145f0565b6000604051808303816000875af1158015610ad2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610afa9190810190614708565b95945050505050565b610b186001600160a01b03841633838561199b565b505050565b610b18838383611a17565b60608167ffffffffffffffff811115610b4357610b4361413a565b604051908082528060200260200182016040528015610b9a57816020015b610b87604051806060016040528060001515815260200160608152602001600081525090565b815260200190600190039081610b615790505b50905060005b82811015610bf957610bd4848483818110610bbd57610bbd61451b565b9050602002810190610bcf9190614793565b611a7f565b828281518110610be657610be661451b565b6020908102919091010152600101610ba0565b5092915050565b82826000610c0f83838361171f565b6040517f5b6fd01d000000000000000000000000000000000000000000000000000000008152600481018690526000906001600160a01b03881690635b6fd01d9060240161010060405180830381865afa158015610c71573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c959190614261565b516040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa158015610cf9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d1d919061431d565b9050610d2a828983611827565b6040517fded700a6000000000000000000000000000000000000000000000000000000008152600481018890526024810182905263ffffffff871660448201526001600160a01b0389169063ded700a6906064016107c5565b6040805180820190915260608082526020820152600080610da48888611bff565b6040517f4997cdc300000000000000000000000000000000000000000000000000000000815291935091506001600160a01b038a1690634997cdc390610df690859085908b908b908b906004016148ab565b600060405180830381865afa158015610e13573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e3b91908101906149db565b9998505050505050505050565b6040805180820190915260608082526020820152600080610e698585611bff565b6040517fd2d95b2d00000000000000000000000000000000000000000000000000000000815291935091506001600160a01b0387169063d2d95b2d90610eb59085908590600401614b46565b600060405180830381865afa158015610ed2573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610efa9190810190614b6b565b925050505b9392505050565b60008085856003610f1883838361171f565b6040517f72ada4c5000000000000000000000000000000000000000000000000000000008152600481018990526001600160a01b03888116602483015287811660448301528a16906372ada4c59060640160408051808303816000875af1158015610f87573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fab9190614caf565b909a909950975050505050505050565b610ff46001600160a01b037f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31688888888888888611c60565b50505050505050565b6040517f3dff05870000000000000000000000000000000000000000000000000000000081526001600160a01b03881690633dff05879061104c90899089908990899089908990600401614cd3565b600060405180830381600087803b15801561106657600080fd5b505af115801561107a573d6000803e3d6000fd5b5050505050505050505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009081906001600160a01b038c16906370a0823190602401602060405180830381865afa1580156110e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061110d919061431d565b90506111228c8c8c848d8d8d8d8d8d8d6115d8565b9c9b505050505050505050505050565b60006001600160a01b0386161561115f5760035461115f906001600160a01b038881169116600019611d1a565b600254611177906001600160a01b0316858588611dd8565b6001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611221576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa1580156111f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061121c919061431d565b611223565b475b9695505050505050565b60608167ffffffffffffffff8111156112485761124861413a565b60405190808252806020026020018201604052801561127b57816020015b60608152602001906001900390816112665790505b50905060005b82811015610bf9576112eb3085858481811061129f5761129f61451b565b90506020028101906112b19190614793565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250611e2a92505050565b8282815181106112fd576112fd61451b565b6020908102919091010152600101611281565b6060610afa85858585611e97565b949350505050565b60005a9050600080306001600160a01b03168585604051611348929190614d90565b600060405180830381855af49150503d8060008114611383576040519150601f19603f3d011682016040523d82523d6000602084013e611388565b606091505b509150915060005a61139a9085614dcf565b905060405180606001604052808415158152602001838152602001828152506040517f493703af0000000000000000000000000000000000000000000000000000000081526004016113ec9190614de2565b60405180910390fd5b610ff46001600160a01b037f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba31688888888888888611f73565b8484600161143d83838361171f565b6040517ff1accf39000000000000000000000000000000000000000000000000000000008152600481018890526024810187905263ffffffff861660448201526001600160a01b03858116606483015289169063f1accf39906084016107c5565b6000546001600160a01b031633146114e2576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280546001600160a01b039384167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560038054929093169116179055565b6000546001600160a01b0316331461156c576040517fe0a8b92000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f56bddfa0cee9697cebddf9acd7f23dc6583663b05e007b877056d05017994def9060200160405180910390a150565b60006115e58b8d8b611827565b81611685576040517f6b29e1bd0000000000000000000000000000000000000000000000000000000081526001600160a01b038d1690636b29e1bd9061163d908e908e908e908e908e908e908e908e90600401614df5565b6020604051808303816000875af115801561165c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611680919061431d565b611122565b6040517fb2b513c10000000000000000000000000000000000000000000000000000000081526001600160a01b038d169063b2b513c1906116dc908e908e908e908e908e908e908e908e908e908e90600401614e54565b6020604051808303816000875af11580156116fb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611122919061431d565b826001600160a01b031663cc7a20496040518163ffffffff1660e01b8152600401602060405180830381865afa15801561175d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117819190614ecb565b6001600160a01b031663823abfd98333846040518463ffffffff1660e01b81526004016117b093929190614ee8565b602060405180830381865afa1580156117cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117f19190614f0b565b610b18576040517f5c427cd900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b0383811660248301526000919085169063dd62ed3e90604401602060405180830381865afa158015611890573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118b4919061431d565b9050818110156118d4576118d46001600160a01b03851684600019611d1a565b50505050565b60006001600160a01b03831673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611986576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa15801561195d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611981919061431d565b611988565b475b90508015610b1857610b18838284611a17565b6040516001600160a01b0384811660248301528381166044830152606482018390526118d49186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611ffc565b6001600160a01b038116611a285750335b7fffffffffffffffffffffffff11111111111111111111111111111111111111126001600160a01b03841601611a6b57610b186001600160a01b03821683612078565b610b186001600160a01b0384168284612141565b611aa5604051806060016040528060001515815260200160608152602001600081525090565b600080306001600160a01b031663bcbef20660e01b8686604051602401611acd929190614f28565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051611b389190614f3c565b600060405180830381855af49150503d8060008114611b73576040519150601f19603f3d011682016040523d82523d6000602084013e611b78565b606091505b50915091508115611be5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f5754463f2053686f756c642068617665206661696c656421000000000000000060448201526064016113ec565b60048101905080806020019051810190610afa9190614f4e565b606080611c5484846000604051908082528060200260200182016040528015611c4e57816020015b6040805180820190915260008082526020820152815260200190600190039081611c275790505b50612172565b50909590945092505050565b8515611d1057876001600160a01b031663edd9444b60405180606001604052808a8a808060200260200160405190810160405280939291908181526020016000905b82821015611cce57611cbf60408302860136819003810190615024565b81526020019060010190611ca2565b5050505050815260200188815260200187815250611ced8a8a866121f7565b3387876040518663ffffffff1660e01b81526004016107c59594939291906150ac565b5050505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052611d9984826122cc565b6118d4576040516001600160a01b03848116602483015260006044830152611dce91869182169063095ea7b3906064016119d0565b6118d48482611ffc565b611e2383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050506001600160a01b0387169190508361236f565b5050505050565b6060600080846001600160a01b031684604051611e479190614f3c565b600060405180830381855af49150503d8060008114611e82576040519150601f19603f3d011682016040523d82523d6000602084013e611e87565b606091505b5091509150610afa858383612417565b60608267ffffffffffffffff811115611eb257611eb261413a565b604051908082528060200260200182016040528015611edb578160200160208202803683370190505b50905060005b83811015611f6a57611f4586868684818110611eff57611eff61451b565b611f15926020604090920201908101915061402f565b878785818110611f2757611f2761451b565b9050604002016020016020810190611f3f919061402f565b8661248c565b828281518110611f5757611f5761451b565b6020908102919091010152600101611ee1565b50949350505050565b6040805160a0810182526001600160a01b0389811660608301908152608083018a9052825260208083018990528284018890528351808501855285831681529081018a905292517f30f28b7a000000000000000000000000000000000000000000000000000000008152908b16926330f28b7a926107c592909190339089908990600401615166565b60006120116001600160a01b03841683612803565b905080516000141580156120365750808060200190518101906120349190614f0b565b155b15610b18576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b03841660048201526024016113ec565b804710156120b4576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016113ec565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612101576040519150601f19603f3d011682016040523d82523d6000602084013e612106565b606091505b5050905080610b18576040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b03838116602483015260448201839052610b1891859182169063a9059cbb906064016119d0565b60608060606121d38686808060200260200160405190810160405280939291908181526020016000905b828210156121c8576121b9604083028601368190038101906151df565b8152602001906001019061219c565b505050505085612811565b92506121e0868685612ccd565b91506121ec8484612f5e565b905093509350939050565b60608267ffffffffffffffff8111156122125761221261413a565b60405190808252806020026020018201604052801561225757816020015b60408051808201909152600080825260208201528152602001906001900390816122305790505b50905060005b81518110156122c4576040518060400160405280846001600160a01b031681526020018686848181106122925761229261451b565b905060400201602001358152508282815181106122b1576122b161451b565b602090810291909101015260010161225d565b509392505050565b6000806000846001600160a01b0316846040516122e99190614f3c565b6000604051808303816000865af19150503d8060008114612326576040519150601f19603f3d011682016040523d82523d6000602084013e61232b565b606091505b50915091508180156123555750805115806123555750808060200190518101906123559190614f0b565b8015610afa5750505050506001600160a01b03163b151590565b6060814710156123ad576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016113ec565b600080856001600160a01b031684866040516123c99190614f3c565b60006040518083038185875af1925050503d8060008114612406576040519150601f19603f3d011682016040523d82523d6000602084013e61240b565b606091505b5091509150610efa8683835b60608261242c576124278261305a565b610eff565b815115801561244357506001600160a01b0384163b155b15612485576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024016113ec565b5080610eff565b600080600061249b868661309f565b6040517f582cf84b0000000000000000000000000000000000000000000000000000000081526001600160a01b038084166004830152808316602483015292945090925060009189169063582cf84b90604401602060405180830381865afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190615220565b90507f010000000000000000000000000000000000000000000000000000000000000060005b7fff00000000000000000000000000000000000000000000000000000000000000808316908416108015906125ab57507fff00000000000000000000000000000000000000000000000000000000000000821615155b156127f257817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168284167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916036127c4576040517f808ba8e00000000000000000000000000000000000000000000000000000000081526001600160a01b038a8116600483015289811660248301527fff000000000000000000000000000000000000000000000000000000000000008416604483015260009182918291908e169063808ba8e090606401608060405180830381865afa158015612694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126b89190615267565b9350935093505060006126ca866130d0565b90506000816126d981866152bf565b6126e4906001615309565b6126ee9190615326565b63ffffffff1690508b612719576127066003836152bf565b6127169063ffffffff168261534e565b90505b6000857bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16118061276457506000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff16115b1561279d5742811161278257859a505050505050505050505061131e565b61278c4282614dcf565b9a505050505050505050505061131e565b428111156127be5785156127b157856127bb565b6127bb4282614dcf565b95505b50505050505b6001827effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916901b9150612555565b506000199998505050505050505050565b6060610eff8383600061236f565b60606000808351855160026128269190615361565b612830919061534e565b67ffffffffffffffff8111156128485761284861413a565b604051908082528060200260200182016040528015612871578160200160208202803683370190505b50905060005b8551811015612a405760008060005b858110801561289c575082801561289a5750815b155b1561297157821580156128f657508884815181106128bc576128bc61451b565b6020026020010151600001516001600160a01b03168582815181106128e3576128e361451b565b60200260200101516001600160a01b0316145b1561290057600192505b81158015612955575088848151811061291b5761291b61451b565b6020026020010151602001516001600160a01b03168582815181106129425761294261451b565b60200260200101516001600160a01b0316145b1561295f57600191505b8061296981615378565b915050612886565b50816129d4578783815181106129895761298961451b565b6020026020010151600001518486806129a190615378565b9750815181106129b3576129b361451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b80612a36578783815181106129eb576129eb61451b565b602002602001015160200151848680612a0390615378565b975081518110612a1557612a1561451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b5050600101612877565b5060005b8451811015612b35576000805b8481108015612a5e575081155b15612ac957868381518110612a7557612a7561451b565b6020026020010151600001516001600160a01b0316848281518110612a9c57612a9c61451b565b60200260200101516001600160a01b031603612ab757600191505b80612ac181615378565b915050612a51565b5080612b2c57858281518110612ae157612ae161451b565b602002602001015160000151838580612af990615378565b965081518110612b0b57612b0b61451b565b60200260200101906001600160a01b031690816001600160a01b0316815250505b50600101612a44565b508167ffffffffffffffff811115612b4f57612b4f61413a565b604051908082528060200260200182016040528015612b78578160200160208202803683370190505b50925060005b82811015612cc4576000828281518110612b9a57612b9a61451b565b6020026020010151905060005b816001600160a01b0316868281518110612bc357612bc361451b565b60200260200101516001600160a01b0316108015612c0d575060006001600160a01b0316868281518110612bf957612bf961451b565b60200260200101516001600160a01b031614155b15612c245780612c1c81615378565b915050612ba7565b825b81811115612c8e5786612c3a600183614dcf565b81518110612c4a57612c4a61451b565b6020026020010151878281518110612c6457612c6461451b565b6001600160a01b039092166020928302919091019091015280612c8681615392565b915050612c26565b5081868281518110612ca257612ca261451b565b6001600160a01b03909216602092830291909101909101525050600101612b7e565b50505092915050565b60608267ffffffffffffffff811115612ce857612ce861413a565b604051908082528060200260200182016040528015612d2d57816020015b6040805180820190915260008082526020820152815260200190600190039081612d065790505b5090506000805b83518160ff161015612f55576000612d4d8260016153a9565b90505b84518160ff161015612f425760005b86811015612f2f57878782818110612d7957612d7961451b565b612d8f926020604090920201908101915061402f565b6001600160a01b0316868460ff1681518110612dad57612dad61451b565b60200260200101516001600160a01b0316148015612e215750878782818110612dd857612dd861451b565b9050604002016020016020810190612df0919061402f565b6001600160a01b0316868360ff1681518110612e0e57612e0e61451b565b60200260200101516001600160a01b0316145b80612ee15750878782818110612e3957612e3961451b565b9050604002016020016020810190612e51919061402f565b6001600160a01b0316868460ff1681518110612e6f57612e6f61451b565b60200260200101516001600160a01b0316148015612ee15750878782818110612e9a57612e9a61451b565b612eb0926020604090920201908101915061402f565b6001600160a01b0316868360ff1681518110612ece57612ece61451b565b60200260200101516001600160a01b0316145b15612f27576040805180820190915260ff8085168252831660208201528585612f0981615378565b965081518110612f1b57612f1b61451b565b60200260200101819052505b600101612d5f565b5080612f3a816153c2565b915050612d50565b5080612f4d816153c2565b915050612d34565b50509392505050565b6060815167ffffffffffffffff811115612f7a57612f7a61413a565b604051908082528060200260200182016040528015612fa3578160200160208202803683370190505b50905060005b8351811015610bf95760005b848281518110612fc757612fc761451b565b6020026020010151600001516001600160a01b0316848281518110612fee57612fee61451b565b60200260200101516001600160a01b031614613016578061300e81615378565b915050612fb5565b8482815181106130285761302861451b565b6020026020010151602001518382815181106130465761304661451b565b602090810291909101015250600101612fa9565b80511561306a5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b600080826001600160a01b0316846001600160a01b0316106130c25782846130c5565b83835b909590945092505050565b60007fff0000000000000000000000000000000000000000000000000000000000000082167f0100000000000000000000000000000000000000000000000000000000000000036131235750603c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f020000000000000000000000000000000000000000000000000000000000000003613175575061012c919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0400000000000000000000000000000000000000000000000000000000000000036131c75750610384919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f0800000000000000000000000000000000000000000000000000000000000000036132195750610708919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f10000000000000000000000000000000000000000000000000000000000000000361326b5750610e10919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f2000000000000000000000000000000000000000000000000000000000000000036132bd5750613840919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f400000000000000000000000000000000000000000000000000000000000000003613310575062015180919050565b7fff0000000000000000000000000000000000000000000000000000000000000082167f800000000000000000000000000000000000000000000000000000000000000003613363575062093a80919050565b6040517fbf3cad0b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602082840312156133a757600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610eff57600080fd5b6001600160a01b038116811461309c57600080fd5b63ffffffff8116811461309c57600080fd5b8035613409816133ec565b919050565b6000806000806080858703121561342457600080fd5b843561342f816133d7565b93506020850135925060408501359150606085013561344d816133ec565b939692955090935050565b8035613409816133d7565b60008060006060848603121561347857600080fd5b8335613483816133d7565b925060208401359150604084013561349a816133d7565b809150509250925092565b60008083601f8401126134b757600080fd5b50813567ffffffffffffffff8111156134cf57600080fd5b6020830191508360208260051b85010111156134ea57600080fd5b9250929050565b803560ff8116811461340957600080fd5b60008060008060008060008060e0898b03121561351e57600080fd5b8835613529816133d7565b9750602089013567ffffffffffffffff81111561354557600080fd5b6135518b828c016134a5565b909850965050604089013594506060890135935061357160808a016134f1565b925060a0890135915060c089013590509295985092959890939650565b600080604083850312156135a157600080fd5b82356135ac816133d7565b915060208301356135bc816133d7565b809150509250929050565b600080600080606085870312156135dd57600080fd5b84356135e8816133d7565b9350602085013567ffffffffffffffff81111561360457600080fd5b613610878288016134a5565b909450925050604085013561344d816133d7565b6020808252825182820181905260009190848201906040850190845b8181101561365c57835183529284019291840191600101613640565b50909695505050505050565b6000806020838503121561367b57600080fd5b823567ffffffffffffffff81111561369257600080fd5b61369e858286016134a5565b90969095509350505050565b60005b838110156136c55781810151838201526020016136ad565b50506000910152565b600081518084526136e68160208601602086016136aa565b601f01601f19169290920160200192915050565b805115158252600060208201516060602085015261371b60608501826136ce565b604093840151949093019390935250919050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156137a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08886030184526137928583516136fa565b94509285019290850190600101613758565b5092979650505050505050565b6000806000606084860312156137c657600080fd5b83356137d1816133d7565b925060208401359150604084013561349a816133ec565b60008083601f8401126137fa57600080fd5b50813567ffffffffffffffff81111561381257600080fd5b6020830191508360208260061b85010111156134ea57600080fd5b801515811461309c57600080fd5b60008083601f84011261384d57600080fd5b50813567ffffffffffffffff81111561386557600080fd5b6020830191508360208285010111156134ea57600080fd5b6000806000806000806080878903121561389657600080fd5b86356138a1816133d7565b9550602087013567ffffffffffffffff808211156138be57600080fd5b6138ca8a838b016137e8565b9097509550604089013591506138df8261382d565b909350606088013590808211156138f557600080fd5b5061390289828a0161383b565b979a9699509497509295939492505050565b60006020808352606080840185516040808588015282825180855260809450608089019150868401935060005b8181101561398e5761397e8386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613941565b505088860151888203601f190160408a0152805180835290870193506000918701905b80831015613a3757845180516001600160a01b03908116845289820151168984015284810151858401528781015188840152868101518784015260a0808201519084015260c0908101517fff000000000000000000000000000000000000000000000000000000000000001690830152938701936001929092019160e0909101906139b1565b509998505050505050505050565b600080600060408486031215613a5a57600080fd5b8335613a65816133d7565b9250602084013567ffffffffffffffff811115613a8157600080fd5b613a8d868287016137e8565b9497909650939450505050565b60006020808352606080840185516040808588015282825180855260809450608089019150868401935060005b81811015613b1457613b048386516001600160a01b0381511682526020810151602083015260408101516040830152606081015160608301525050565b9387019391850191600101613ac7565b505088860151888203601f190160408a0152805180835290870193506000918701905b80831015613a3757845180516001600160a01b039081168452898201511689840152848101518584015287810151888401528601517fff000000000000000000000000000000000000000000000000000000000000001686830152938701936001929092019160a090910190613b37565b60008060008060808587031215613bbe57600080fd5b8435613bc9816133d7565b9350602085013592506040850135613be0816133d7565b9150606085013561344d816133d7565b600080600080600080600060a0888a031215613c0b57600080fd5b873567ffffffffffffffff80821115613c2357600080fd5b613c2f8b838c016137e8565b909950975060208a0135965060408a0135955060608a0135915080821115613c5657600080fd5b50613c638a828b0161383b565b9094509250506080880135613c77816133d7565b8091505092959891949750929550565b600080600080600080600060c0888a031215613ca257600080fd5b8735613cad816133d7565b9650602088013567ffffffffffffffff811115613cc957600080fd5b613cd58a828b016134a5565b90975095505060408801359350613cee606089016134f1565b92506080880135915060a0880135905092959891949750929550565b6000806000806000806000806000806101008b8d031215613d2a57600080fd5b8a35613d35816133d7565b995060208b0135613d45816133d7565b985060408b0135613d55816133d7565b975060608b0135613d65816133ec565b965060808b0135613d75816133ec565b9550613d8360a08c01613458565b945060c08b013567ffffffffffffffff80821115613da057600080fd5b613dac8e838f016134a5565b909650945060e08d0135915080821115613dc557600080fd5b50613dd28d828e0161383b565b915080935050809150509295989b9194979a5092959850565b600080600080600060808688031215613e0357600080fd5b8535613e0e816133d7565b945060208601359350604086013567ffffffffffffffff811115613e3157600080fd5b613e3d8882890161383b565b9094509250506060860135613e51816133d7565b809150509295509295909350565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b828110156137a4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0888603018452613ec28583516136ce565b94509285019290850190600101613e88565b60008060008060608587031215613eea57600080fd5b8435613ef5816133d7565b9350602085013567ffffffffffffffff811115613f1157600080fd5b613f1d878288016137e8565b909450925050604085013561344d8161382d565b60008060208385031215613f4457600080fd5b823567ffffffffffffffff811115613f5b57600080fd5b61369e8582860161383b565b600080600080600080600060c0888a031215613f8257600080fd5b8735613f8d816133d7565b9650602088013595506040880135945060608801359350608088013567ffffffffffffffff811115613fbe57600080fd5b613fca8a828b0161383b565b90945092505060a0880135613c77816133d7565b600080600080600060a08688031215613ff657600080fd5b8535614001816133d7565b94506020860135935060408601359250606086013561401f816133ec565b91506080860135613e51816133d7565b60006020828403121561404157600080fd5b8135610eff816133d7565b60008060008060008060008060008060006101208c8e03121561406e57600080fd5b6140788c356133d7565b8b359a5061408960208d01356133d7565b60208c0135995061409c60408d01613458565b985060608c013597506140b160808d016133fe565b96506140bf60a08d016133fe565b95506140cd60c08d01613458565b945067ffffffffffffffff8060e08e013511156140e957600080fd5b6140f98e60e08f01358f016134a5565b90955093506101008d013581101561411057600080fd5b506141228d6101008e01358e0161383b565b81935080925050509295989b509295989b9093969950565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561418c5761418c61413a565b60405290565b60405160e0810167ffffffffffffffff8111828210171561418c5761418c61413a565b60405160a0810167ffffffffffffffff8111828210171561418c5761418c61413a565b6040516060810167ffffffffffffffff8111828210171561418c5761418c61413a565b604051601f8201601f1916810167ffffffffffffffff811182821017156142245761422461413a565b604052919050565b8051613409816133d7565b8051613409816133ec565b80516effffffffffffffffffffffffffffff8116811461340957600080fd5b600061010080838503121561427557600080fd5b6040519081019067ffffffffffffffff821181831017156142985761429861413a565b81604052835191506142a9826133d7565b8181526142b86020850161422c565b60208201526142c960408501614237565b60408201526142da60608501614237565b6060820152608084015160808201526142f560a08501614237565b60a082015260c084015160c082015261431060e08501614242565b60e0820152949350505050565b60006020828403121561432f57600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261436b57600080fd5b830160208101925035905067ffffffffffffffff81111561438b57600080fd5b8060051b36038213156134ea57600080fd5b600481106143d4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261440c57600080fd5b90910192915050565b60008383855260208086019550808560051b8301018460005b878110156144cd57601f1985840301895261444982886143d8565b60408085018235614459816133d7565b6001600160a01b0316865261447083880184614336565b8789019390935290829052909150606085019060005b838110156144b85781356004811061449d57600080fd5b6144a7848261439d565b509187019190870190600101614486565b5050998501999350509083019060010161442e565b5090979650505050505050565b60c0815260006144ee60c08301898b614415565b602083019790975250604081019490945260ff929092166060840152608083015260a09091015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261457e57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126145bd57600080fd5b83018035915067ffffffffffffffff8211156145d857600080fd5b6020019150600581901b36038213156134ea57600080fd5b60408082528181018490526000906060808401600587811b860183018986805b8b8110156146c6577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08a8503018652614649838e6143d8565b8035614654816133d7565b6001600160a01b03168552602061466d82820183614336565b92508a82880152828b8801527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156146a5578485fd5b91871b918281888c0137509687019694018701939290920191600101614610565b5050506001600160a01b0388166020880152945061131e9350505050565b600067ffffffffffffffff8211156146fe576146fe61413a565b5060051b60200190565b6000602080838503121561471b57600080fd5b825167ffffffffffffffff81111561473257600080fd5b8301601f8101851361474357600080fd5b8051614756614751826146e4565b6141fb565b81815260059190911b8201830190838101908783111561477557600080fd5b928401925b8284101561094a5783518252928401929084019061477a565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126147c857600080fd5b83018035915067ffffffffffffffff8211156147e357600080fd5b6020019150368190038213156134ea57600080fd5b60008151808452602080850194506020840160005b838110156148325781516001600160a01b03168752958201959082019060010161480d565b509495945050505050565b60008151808452602080850194506020840160005b83811015614832578151805160ff908116895290840151168388015260409096019590820190600101614852565b818352818160208501375060006020828401015260006020601f19601f840116840101905092915050565b6080815260006148be60808301886147f8565b82810360208401526148d0818861483d565b9050851515604084015282810360608401526148ed818587614880565b98975050505050505050565b6000614907614751846146e4565b8381529050602080820190600785901b84018681111561492657600080fd5b845b818110156149a057608080828a0312156149425760008081fd5b604080519182019167ffffffffffffffff831181841017156149665761496661413a565b918152825191614975836133d7565b9182528285015185830152808301519082015260608083015190820152845292820192608001614928565b505050509392505050565b80517fff000000000000000000000000000000000000000000000000000000000000008116811461340957600080fd5b600060208083850312156149ee57600080fd5b825167ffffffffffffffff80821115614a0657600080fd5b81850191506040808388031215614a1c57600080fd5b614a24614169565b835183811115614a3357600080fd5b8401601f81018913614a4457600080fd5b614a528982518884016148f9565b8252508484015183811115614a6657600080fd5b80850194505087601f850112614a7b57600080fd5b83519250614a8b614751846146e4565b83815260e0938402850186019386820191908a861115614aaa57600080fd5b958701955b85871015614b335780878c031215614ac75760008081fd5b614acf614192565b8751614ada816133d7565b815287890151614ae9816133d7565b818a01528786015186820152606080890151908201526080808901519082015260a0808901519082015260c0614b20818a016149ab565b9082015283529586019591870191614aaf565b5095820195909552979650505050505050565b604081526000614b5960408301856147f8565b8281036020840152610afa818561483d565b60006020808385031215614b7e57600080fd5b825167ffffffffffffffff80821115614b9657600080fd5b81850191506040808388031215614bac57600080fd5b614bb4614169565b835183811115614bc357600080fd5b8401601f81018913614bd457600080fd5b614be28982518884016148f9565b8252508484015183811115614bf657600080fd5b80850194505087601f850112614c0b57600080fd5b83519250614c1b614751846146e4565b83815260a0938402850186019386820191908a861115614c3a57600080fd5b958701955b85871015614b335780878c031215614c575760008081fd5b614c5f6141b5565b8751614c6a816133d7565b815287890151614c79816133d7565b818a01528786015186820152606080890151908201526080614c9c818a016149ab565b9082015283529586019591870191614c3f565b60008060408385031215614cc257600080fd5b505080516020909101519092909150565b60a08082528101869052600060c0600588901b8301810190830189835b8a811015614d67577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40868503018352614d29828d6143d8565b6040813586526020614d3d81840184614336565b93508282890152614d518389018583614415565b9750509485019493909301925050600101614cf0565b5050506020830187905260ff861660408401529050606082019390935260800152949350505050565b8183823760009101908152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106c7576106c7614da0565b602081526000610eff60208301846136fa565b60006001600160a01b03808b168352808a16602084015288604084015263ffffffff808916606085015280881660808501525080861660a08401525060e060c0830152614e4660e083018486614415565b9a9950505050505050505050565b6001600160a01b038b811682528a81166020830152604082018a905263ffffffff898116606084015288166080830152861660a082015261010060c08201819052600090614ea58382018789614415565b905082810360e0840152614eba818587614880565b9d9c50505050505050505050505050565b600060208284031215614edd57600080fd5b8151610eff816133d7565b8381526001600160a01b03831660208201526060810161131e604083018461439d565b600060208284031215614f1d57600080fd5b8151610eff8161382d565b60208152600061131e602083018486614880565b6000825161457e8184602087016136aa565b60006020808385031215614f6157600080fd5b825167ffffffffffffffff80821115614f7957600080fd5b9084019060608287031215614f8d57600080fd5b614f956141d8565b8251614fa08161382d565b81528284015182811115614fb357600080fd5b8301601f81018813614fc457600080fd5b805183811115614fd657614fd661413a565b614fe886601f19601f840116016141fb565b93508084528886828401011115614ffe57600080fd5b61500d818786018885016136aa565b505092830152604090810151908201529392505050565b60006040828403121561503657600080fd5b61503e614169565b8235615049816133d7565b81526020928301359281019290925250919050565b60008151808452602080850194506020840160005b838110156148325761509987835180516001600160a01b03168252602090810151910152565b6040969096019590820190600101615073565b60808152600060e08201875160606080850152818151808452610100860191506020935060208301925060005b81811015615112576150ff83855180516001600160a01b03168252602090810151910152565b92840192604092909201916001016150d9565b505060208a015160a086015260408a015160c0860152848103602086015261513a818a61505e565b9250505061515360408401876001600160a01b03169052565b82810360608401526148ed818587614880565b600061010061518983895180516001600160a01b03168252602090810151910152565b60208801516040840152604088015160608401526151bd608084018880516001600160a01b03168252602090810151910152565b6001600160a01b03861660c08401528060e08401526148ed8184018587614880565b6000604082840312156151f157600080fd5b6151f9614169565b8235615204816133d7565b81526020830135615214816133d7565b60208201529392505050565b60006020828403121561523257600080fd5b610eff826149ab565b80517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8116811461340957600080fd5b6000806000806080858703121561527d57600080fd5b8451615288816133ec565b93506152966020860161523b565b925060408501516152a6816133ec565b91506152b46060860161523b565b905092959194509250565b600063ffffffff808416806152fd577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b92169190910492915050565b63ffffffff818116838216019080821115610bf957610bf9614da0565b63ffffffff81811683821602808216919082811461534657615346614da0565b505092915050565b808201808211156106c7576106c7614da0565b80820281158282048414176106c7576106c7614da0565b6000600019820361538b5761538b614da0565b5060010190565b6000816153a1576153a1614da0565b506000190190565b60ff81811683821601908111156106c7576106c7614da0565b600060ff821660ff81036153d8576153d8614da0565b6001019291505056

Verified Source Code Partial Match

Compiler: v0.8.22+commit.4fc1097e EVM: paris Optimization: Yes (9999 runs)
SimulationAdapter.sol 61 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

import { IERC165 } from "./interfaces/external/IERC165.sol";
import { ISimulationAdapter } from "./interfaces/ISimulationAdapter.sol";

/**
 * @title Simulation Adapter
 * @author Sam Bugs
 * @notice This contracts adds off-chain simulation capabilities to existing contracts. It works similarly to a
 *         multicall, but the state is not modified in each subcall.
 */
abstract contract SimulationAdapter is IERC165, ISimulationAdapter {
  /// @notice An error that contains a simulation's result
  error SimulatedCall(SimulationResult result);

  /// @inheritdoc IERC165
  function supportsInterface(bytes4 _interfaceId) public view virtual override returns (bool) {
    return _interfaceId == type(ISimulationAdapter).interfaceId || _interfaceId == type(IERC165).interfaceId;
  }

  /// @inheritdoc ISimulationAdapter
  function simulate(bytes[] calldata _calls) external payable returns (SimulationResult[] memory _results) {
    _results = new SimulationResult[](_calls.length);
    for (uint256 i = 0; i < _calls.length; i++) {
      _results[i] = _simulate(_calls[i]);
    }
    return _results;
  }

  /**
   * @notice Executes a simulation and returns the result
   * @param _call The call to simulate
   * @return _simulationResult The simulation's result
   */
  function _simulate(bytes calldata _call) internal returns (SimulationResult memory _simulationResult) {
    (bool _success, bytes memory _result) =
    // solhint-disable-next-line avoid-low-level-calls
     address(this).delegatecall(abi.encodeWithSelector(this.simulateAndRevert.selector, _call));
    require(!_success, "WTF? Should have failed!");
    // Move pointer to ignore selector
    // solhint-disable-next-line no-inline-assembly
    assembly {
      _result := add(_result, 0x04)
    }
    (_simulationResult) = abi.decode(_result, (SimulationResult));
  }

  /**
   * @notice Executes a call agains this contract and reverts with the result
   * @dev This is meant to be used internally, do not call!
   * @param _call The call to simulate
   */
  function simulateAndRevert(bytes calldata _call) external payable {
    uint256 _gasAtStart = gasleft();
    // solhint-disable-next-line avoid-low-level-calls
    (bool _success, bytes memory _result) = address(this).delegatecall(_call);
    uint256 _gasSpent = _gasAtStart - gasleft();
    revert SimulatedCall(SimulationResult({ success: _success, result: _result, gasSpent: _gasSpent }));
  }
}
IDCAHub.sol 699 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import '@mean-finance/oracles/solidity/interfaces/ITokenPriceOracle.sol';
import './IDCAPermissionManager.sol';

/**
 * @title The interface for all state related queries
 * @notice These methods allow users to read the hubs's current values
 */
interface IDCAHubParameters {
  /**
   * @notice Returns how much will the amount to swap differ from the previous swap. f.e. if the returned value is -100, then the amount to swap will be 100 less than the swap just before it
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @param swapNumber The swap number to check
   * @return swapDeltaAToB How much less of token A will the following swap require
   * @return swapDeltaBToA How much less of token B will the following swap require
   */
  function swapAmountDelta(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask,
    uint32 swapNumber
  ) external view returns (uint128 swapDeltaAToB, uint128 swapDeltaBToA);

  /**
   * @notice Returns the sum of the ratios reported in all swaps executed until the given swap number
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @param swapNumber The swap number to check
   * @return accumRatioAToB The sum of all ratios from A to B
   * @return accumRatioBToA The sum of all ratios from B to A
   */
  function accumRatio(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask,
    uint32 swapNumber
  ) external view returns (uint256 accumRatioAToB, uint256 accumRatioBToA);

  /**
   * @notice Returns swapping information about a specific pair
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA One of the pair's token
   * @param tokenB The other of the pair's token
   * @param swapIntervalMask The byte representation of the swap interval to check
   * @return performedSwaps How many swaps have been executed
   * @return nextAmountToSwapAToB How much of token A will be swapped on the next swap
   * @return lastSwappedAt Timestamp of the last swap
   * @return nextAmountToSwapBToA How much of token B will be swapped on the next swap
   */
  function swapData(
    address tokenA,
    address tokenB,
    bytes1 swapIntervalMask
  )
    external
    view
    returns (
      uint32 performedSwaps,
      uint224 nextAmountToSwapAToB,
      uint32 lastSwappedAt,
      uint224 nextAmountToSwapBToA
    );

  /**
   * @notice Returns the byte representation of the set of actice swap intervals for the given pair
   * @dev `tokenA` must be smaller than `tokenB` (tokenA < tokenB)
   * @param tokenA The smaller of the pair's token
   * @param tokenB The other of the pair's token
   * @return The byte representation of the set of actice swap intervals
   */
  function activeSwapIntervals(address tokenA, address tokenB) external view returns (bytes1);

  /**
   * @notice Returns how much of the hub's token balance belongs to the platform
   * @param token The token to check
   * @return The amount that belongs to the platform
   */
  function platformBalance(address token) external view returns (uint256);
}

/**
 * @title The interface for all position related matters
 * @notice These methods allow users to create, modify and terminate their positions
 */
interface IDCAHubPositionHandler {
  /// @notice The position of a certain user
  struct UserPosition {
    // The token that the user deposited and will be swapped in exchange for "to"
    IERC20Metadata from;
    // The token that the user will get in exchange for their "from" tokens in each swap
    IERC20Metadata to;
    // How frequently the position's swaps should be executed
    uint32 swapInterval;
    // How many swaps were executed since deposit, last modification, or last withdraw
    uint32 swapsExecuted;
    // How many "to" tokens can currently be withdrawn
    uint256 swapped;
    // How many swaps left the position has to execute
    uint32 swapsLeft;
    // How many "from" tokens there are left to swap
    uint256 remaining;
    // How many "from" tokens need to be traded in each swap
    uint120 rate;
  }

  /// @notice A list of positions that all have the same `to` token
  struct PositionSet {
    // The `to` token
    address token;
    // The position ids
    uint256[] positionIds;
  }

  /**
   * @notice Emitted when a position is terminated
   * @param user The address of the user that terminated the position
   * @param recipientUnswapped The address of the user that will receive the unswapped tokens
   * @param recipientSwapped The address of the user that will receive the swapped tokens
   * @param positionId The id of the position that was terminated
   * @param returnedUnswapped How many "from" tokens were returned to the caller
   * @param returnedSwapped How many "to" tokens were returned to the caller
   */
  event Terminated(
    address indexed user,
    address indexed recipientUnswapped,
    address indexed recipientSwapped,
    uint256 positionId,
    uint256 returnedUnswapped,
    uint256 returnedSwapped
  );

  /**
   * @notice Emitted when a position is created
   * @param depositor The address of the user that creates the position
   * @param owner The address of the user that will own the position
   * @param positionId The id of the position that was created
   * @param fromToken The address of the "from" token
   * @param toToken The address of the "to" token
   * @param swapInterval How frequently the position's swaps should be executed
   * @param rate How many "from" tokens need to be traded in each swap
   * @param startingSwap The number of the swap when the position will be executed for the first time
   * @param lastSwap The number of the swap when the position will be executed for the last time
   * @param permissions The permissions defined for the position
   */
  event Deposited(
    address indexed depositor,
    address indexed owner,
    uint256 positionId,
    address fromToken,
    address toToken,
    uint32 swapInterval,
    uint120 rate,
    uint32 startingSwap,
    uint32 lastSwap,
    IDCAPermissionManager.PermissionSet[] permissions
  );

  /**
   * @notice Emitted when a position is created and extra data is provided
   * @param positionId The id of the position that was created
   * @param data The extra data that was provided
   */
  event Miscellaneous(uint256 positionId, bytes data);

  /**
   * @notice Emitted when a user withdraws all swapped tokens from a position
   * @param withdrawer The address of the user that executed the withdraw
   * @param recipient The address of the user that will receive the withdrawn tokens
   * @param positionId The id of the position that was affected
   * @param token The address of the withdrawn tokens. It's the same as the position's "to" token
   * @param amount The amount that was withdrawn
   */
  event Withdrew(address indexed withdrawer, address indexed recipient, uint256 positionId, address token, uint256 amount);

  /**
   * @notice Emitted when a user withdraws all swapped tokens from many positions
   * @param withdrawer The address of the user that executed the withdraws
   * @param recipient The address of the user that will receive the withdrawn tokens
   * @param positions The positions to withdraw from
   * @param withdrew The total amount that was withdrawn from each token
   */
  event WithdrewMany(address indexed withdrawer, address indexed recipient, PositionSet[] positions, uint256[] withdrew);

  /**
   * @notice Emitted when a position is modified
   * @param user The address of the user that modified the position
   * @param positionId The id of the position that was modified
   * @param rate How many "from" tokens need to be traded in each swap
   * @param startingSwap The number of the swap when the position will be executed for the first time
   * @param lastSwap The number of the swap when the position will be executed for the last time
   */
  event Modified(address indexed user, uint256 positionId, uint120 rate, uint32 startingSwap, uint32 lastSwap);

  /// @notice Thrown when a user tries to create a position with the same `from` & `to`
  error InvalidToken();

  /// @notice Thrown when a user tries to create a position with a swap interval that is not allowed
  error IntervalNotAllowed();

  /// @notice Thrown when a user tries operate on a position that doesn't exist (it might have been already terminated)
  error InvalidPosition();

  /// @notice Thrown when a user tries operate on a position that they don't have access to
  error UnauthorizedCaller();

  /// @notice Thrown when a user tries to create a position with zero swaps
  error ZeroSwaps();

  /// @notice Thrown when a user tries to create a position with zero funds
  error ZeroAmount();

  /// @notice Thrown when a user tries to withdraw a position whose `to` token doesn't match the specified one
  error PositionDoesNotMatchToken();

  /// @notice Thrown when a user tries create or modify a position with an amount too big
  error AmountTooBig();

  /**
   * @notice Returns the permission manager contract
   * @return The contract itself
   */
  function permissionManager() external view returns (IDCAPermissionManager);

  /**
   * @notice Returns total created positions
   * @return The total created positions
   */
  function totalCreatedPositions() external view returns (uint256);

  /**
   * @notice Returns a user position
   * @param positionId The id of the position
   * @return position The position itself
   */
  function userPosition(uint256 positionId) external view returns (UserPosition memory position);

  /**
   * @notice Creates a new position
   * @dev Will revert:
   *      - With ZeroAddress if from, to or owner are zero
   *      - With InvalidToken if from == to
   *      - With ZeroAmount if amount is zero
   *      - With AmountTooBig if amount is too big
   *      - With ZeroSwaps if amountOfSwaps is zero
   *      - With IntervalNotAllowed if swapInterval is not allowed
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param permissions Extra permissions to add to the position. Can be empty
   * @return positionId The id of the created position
   */
  function deposit(
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions
  ) external returns (uint256 positionId);

  /**
   * @notice Creates a new position
   * @dev Will revert:
   *      - With ZeroAddress if from, to or owner are zero
   *      - With InvalidToken if from == to
   *      - With ZeroAmount if amount is zero
   *      - With AmountTooBig if amount is too big
   *      - With ZeroSwaps if amountOfSwaps is zero
   *      - With IntervalNotAllowed if swapInterval is not allowed
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param permissions Extra permissions to add to the position. Can be empty
   * @param miscellaneous Bytes that will be emitted, and associated with the position
   * @return positionId The id of the created position
   */
  function deposit(
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external returns (uint256 positionId);

  /**
   * @notice Withdraws all swapped tokens from a position to a recipient
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroAddress if recipient is zero
   * @param positionId The position's id
   * @param recipient The address to withdraw swapped tokens to
   * @return swapped How much was withdrawn
   */
  function withdrawSwapped(uint256 positionId, address recipient) external returns (uint256 swapped);

  /**
   * @notice Withdraws all swapped tokens from multiple positions
   * @dev Will revert:
   *      - With InvalidPosition if any of the position ids are invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position to any of the given positions
   *      - With ZeroAddress if recipient is zero
   *      - With PositionDoesNotMatchToken if any of the positions do not match the token in their position set
   * @param positions A list positions, grouped by `to` token
   * @param recipient The address to withdraw swapped tokens to
   * @return withdrawn How much was withdrawn for each token
   */
  function withdrawSwappedMany(PositionSet[] calldata positions, address recipient) external returns (uint256[] memory withdrawn);

  /**
   * @notice Takes the unswapped balance, adds the new deposited funds and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With AmountTooBig if amount is too big
   * @param positionId The position's id
   * @param amount Amount of funds to add to the position
   * @param newSwaps The new amount of swaps
   */
  function increasePosition(
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps
  ) external;

  /**
   * @notice Withdraws the specified amount from the unswapped balance and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroSwaps if newSwaps is zero and amount is not the total unswapped balance
   * @param positionId The position's id
   * @param amount Amount of funds to withdraw from the position
   * @param newSwaps The new amount of swaps
   * @param recipient The address to send tokens to
   */
  function reducePosition(
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps,
    address recipient
  ) external;

  /**
   * @notice Terminates the position and sends all unswapped and swapped balance to the specified recipients
   * @dev Will revert:
   *      - With InvalidPosition if positionId is invalid
   *      - With UnauthorizedCaller if the caller doesn't have access to the position
   *      - With ZeroAddress if recipientUnswapped or recipientSwapped is zero
   * @param positionId The position's id
   * @param recipientUnswapped The address to withdraw unswapped tokens to
   * @param recipientSwapped The address to withdraw swapped tokens to
   * @return unswapped The unswapped balance sent to `recipientUnswapped`
   * @return swapped The swapped balance sent to `recipientSwapped`
   */
  function terminate(
    uint256 positionId,
    address recipientUnswapped,
    address recipientSwapped
  ) external returns (uint256 unswapped, uint256 swapped);
}

/**
 * @title The interface for all swap related matters
 * @notice These methods allow users to get information about the next swap, and how to execute it
 */
interface IDCAHubSwapHandler {
  /// @notice Information about a swap
  struct SwapInfo {
    // The tokens involved in the swap
    TokenInSwap[] tokens;
    // The pairs involved in the swap
    PairInSwap[] pairs;
  }

  /// @notice Information about a token's role in a swap
  struct TokenInSwap {
    // The token's address
    address token;
    // How much will be given of this token as a reward
    uint256 reward;
    // How much of this token needs to be provided by swapper
    uint256 toProvide;
    // How much of this token will be paid to the platform
    uint256 platformFee;
  }

  /// @notice Information about a pair in a swap
  struct PairInSwap {
    // The address of one of the tokens
    address tokenA;
    // The address of the other token
    address tokenB;
    // The total amount of token A swapped in this pair
    uint256 totalAmountToSwapTokenA;
    // The total amount of token B swapped in this pair
    uint256 totalAmountToSwapTokenB;
    // How much is 1 unit of token A when converted to B
    uint256 ratioAToB;
    // How much is 1 unit of token B when converted to A
    uint256 ratioBToA;
    // The swap intervals involved in the swap, represented as a byte
    bytes1 intervalsInSwap;
  }

  /// @notice A pair of tokens, represented by their indexes in an array
  struct PairIndexes {
    // The index of the token A
    uint8 indexTokenA;
    // The index of the token B
    uint8 indexTokenB;
  }

  /**
   * @notice Emitted when a swap is executed
   * @param sender The address of the user that initiated the swap
   * @param rewardRecipient The address that received the reward
   * @param callbackHandler The address that executed the callback
   * @param swapInformation All information related to the swap
   * @param borrowed How much was borrowed
   * @param fee The swap fee at the moment of the swap
   */
  event Swapped(
    address indexed sender,
    address indexed rewardRecipient,
    address indexed callbackHandler,
    SwapInfo swapInformation,
    uint256[] borrowed,
    uint32 fee
  );

  /// @notice Thrown when pairs indexes are not sorted correctly
  error InvalidPairs();

  /// @notice Thrown when trying to execute a swap, but there is nothing to swap
  error NoSwapsToExecute();

  /**
   * @notice Returns all information related to the next swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   * @param tokens The tokens involved in the next swap
   * @param pairs The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the next swap information for privileged and non-privileged accounts
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return swapInformation The information about the next swap
   */
  function getNextSwapInfo(
    address[] calldata tokens,
    PairIndexes[] calldata pairs,
    bool calculatePrivilegedAvailability,
    bytes calldata oracleData
  ) external view returns (SwapInfo memory swapInformation);

  /**
   * @notice Executes a flash swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   *      - With Paused if swaps are paused by protocol
   *      - With NoSwapsToExecute if there are no swaps to execute for the given pairs
   *      - With LiquidityNotReturned if the required tokens were not back during the callback
   * @param tokens The tokens involved in the next swap
   * @param pairsToSwap The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param rewardRecipient The address to send the reward to
   * @param callbackHandler Address to call for callback (and send the borrowed tokens to)
   * @param borrow How much to borrow of each of the tokens in tokens. The amount must match the position of the token in the tokens array
   * @param callbackData Bytes to send to the caller during the callback
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return Information about the executed swap
   */
  function swap(
    address[] calldata tokens,
    PairIndexes[] calldata pairsToSwap,
    address rewardRecipient,
    address callbackHandler,
    uint256[] calldata borrow,
    bytes calldata callbackData,
    bytes calldata oracleData
  ) external returns (SwapInfo memory);
}

/**
 * @title The interface for handling all configuration
 * @notice This contract will manage configuration that affects all pairs, swappers, etc
 */
interface IDCAHubConfigHandler {
  /**
   * @notice Emitted when a new oracle is set
   * @param oracle The new oracle contract
   */
  event OracleSet(ITokenPriceOracle oracle);

  /**
   * @notice Emitted when a new swap fee is set
   * @param feeSet The new swap fee
   */
  event SwapFeeSet(uint32 feeSet);

  /**
   * @notice Emitted when new swap intervals are allowed
   * @param swapIntervals The new swap intervals
   */
  event SwapIntervalsAllowed(uint32[] swapIntervals);

  /**
   * @notice Emitted when some swap intervals are no longer allowed
   * @param swapIntervals The swap intervals that are no longer allowed
   */
  event SwapIntervalsForbidden(uint32[] swapIntervals);

  /**
   * @notice Emitted when a new platform fee ratio is set
   * @param platformFeeRatio The new platform fee ratio
   */
  event PlatformFeeRatioSet(uint16 platformFeeRatio);

  /**
   * @notice Emitted when allowed states of tokens are updated
   * @param tokens Array of updated tokens
   * @param allowed Array of new allow state per token were allowed[i] is the updated state of tokens[i]
   */
  event TokensAllowedUpdated(address[] tokens, bool[] allowed);

  /// @notice Thrown when trying to interact with an unallowed token
  error UnallowedToken();

  /// @notice Thrown when set allowed tokens input is not valid
  error InvalidAllowedTokensInput();

  /// @notice Thrown when trying to set a fee higher than the maximum allowed
  error HighFee();

  /// @notice Thrown when trying to set a fee that is not multiple of 100
  error InvalidFee();

  /// @notice Thrown when trying to set a fee ratio that is higher that the maximum allowed
  error HighPlatformFeeRatio();

  /**
   * @notice Returns the max fee ratio that can be set
   * @dev Cannot be modified
   * @return The maximum possible value
   */
  // solhint-disable-next-line func-name-mixedcase
  function MAX_PLATFORM_FEE_RATIO() external view returns (uint16);

  /**
   * @notice Returns the fee charged on swaps
   * @return swapFee The fee itself
   */
  function swapFee() external view returns (uint32 swapFee);

  /**
   * @notice Returns the price oracle contract
   * @return oracle The contract itself
   */
  function oracle() external view returns (ITokenPriceOracle oracle);

  /**
   * @notice Returns how much will the platform take from the fees collected in swaps
   * @return The current ratio
   */
  function platformFeeRatio() external view returns (uint16);

  /**
   * @notice Returns the max fee that can be set for swaps
   * @dev Cannot be modified
   * @return maxFee The maximum possible fee
   */
  // solhint-disable-next-line func-name-mixedcase
  function MAX_FEE() external view returns (uint32 maxFee);

  /**
   * @notice Returns a byte that represents allowed swap intervals
   * @return allowedSwapIntervals The allowed swap intervals
   */
  function allowedSwapIntervals() external view returns (bytes1 allowedSwapIntervals);

  /**
   * @notice Returns if a token is currently allowed or not
   * @return Allowed state of token
   */
  function allowedTokens(address token) external view returns (bool);

  /**
   * @notice Returns token's magnitude (10**decimals)
   * @return Stored magnitude for token
   */
  function tokenMagnitude(address token) external view returns (uint120);

  /**
   * @notice Returns whether swaps and deposits are currently paused
   * @return isPaused Whether swaps and deposits are currently paused
   */
  function paused() external view returns (bool isPaused);

  /**
   * @notice Sets a new swap fee
   * @dev Will revert with HighFee if the fee is higher than the maximum
   * @dev Will revert with InvalidFee if the fee is not multiple of 100
   * @param fee The new swap fee
   */
  function setSwapFee(uint32 fee) external;

  /**
   * @notice Sets a new price oracle
   * @dev Will revert with ZeroAddress if the zero address is passed
   * @param oracle The new oracle contract
   */
  function setOracle(ITokenPriceOracle oracle) external;

  /**
   * @notice Sets a new platform fee ratio
   * @dev Will revert with HighPlatformFeeRatio if given ratio is too high
   * @param platformFeeRatio The new ratio
   */
  function setPlatformFeeRatio(uint16 platformFeeRatio) external;

  /**
   * @notice Adds new swap intervals to the allowed list
   * @param swapIntervals The new swap intervals
   */
  function addSwapIntervalsToAllowedList(uint32[] calldata swapIntervals) external;

  /**
   * @notice Removes some swap intervals from the allowed list
   * @param swapIntervals The swap intervals to remove
   */
  function removeSwapIntervalsFromAllowedList(uint32[] calldata swapIntervals) external;

  /// @notice Pauses all swaps and deposits
  function pause() external;

  /// @notice Unpauses all swaps and deposits
  function unpause() external;
}

/**
 * @title The interface for handling platform related actions
 * @notice This contract will handle all actions that affect the platform in some way
 */
interface IDCAHubPlatformHandler {
  /**
   * @notice Emitted when someone withdraws from the paltform balance
   * @param sender The address of the user that initiated the withdraw
   * @param recipient The address that received the withdraw
   * @param amounts The tokens (and the amount) that were withdrawn
   */
  event WithdrewFromPlatform(address indexed sender, address indexed recipient, IDCAHub.AmountOfToken[] amounts);

  /**
   * @notice Withdraws tokens from the platform balance
   * @param amounts The amounts to withdraw
   * @param recipient The address that will receive the tokens
   */
  function withdrawFromPlatformBalance(IDCAHub.AmountOfToken[] calldata amounts, address recipient) external;
}

interface IDCAHub is IDCAHubParameters, IDCAHubConfigHandler, IDCAHubSwapHandler, IDCAHubPositionHandler, IDCAHubPlatformHandler {
  /// @notice Specifies an amount of a token. For example to determine how much to borrow from certain tokens
  struct AmountOfToken {
    // The tokens' address
    address token;
    // How much to borrow or withdraw of the specified token
    uint256 amount;
  }

  /// @notice Thrown when one of the parameters is a zero address
  error ZeroAddress();

  /// @notice Thrown when the expected liquidity is not returned in flash swaps
  error LiquidityNotReturned();

  /// @notice Thrown when a list of token pairs is not sorted, or if there are duplicates
  error InvalidTokens();
}
Intervals.sol 60 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/// @title Intervals library
/// @notice Provides functions to easily convert from swap intervals to their byte representation and viceversa
library Intervals {
  /// @notice Thrown when a user tries convert and invalid interval to a byte representation
  error InvalidInterval();

  /// @notice Thrown when a user tries convert and invalid byte representation to an interval
  error InvalidMask();

  /// @notice Takes a swap interval and returns its byte representation
  /// @dev Will revert with InvalidInterval if the swap interval is not valid
  /// @param _swapInterval The swap interval
  /// @return The interval's byte representation
  function intervalToMask(uint32 _swapInterval) internal pure returns (bytes1) {
    if (_swapInterval == 1 minutes) return 0x01;
    if (_swapInterval == 5 minutes) return 0x02;
    if (_swapInterval == 15 minutes) return 0x04;
    if (_swapInterval == 30 minutes) return 0x08;
    if (_swapInterval == 1 hours) return 0x10;
    if (_swapInterval == 4 hours) return 0x20;
    if (_swapInterval == 1 days) return 0x40;
    if (_swapInterval == 1 weeks) return 0x80;
    revert InvalidInterval();
  }

  /// @notice Takes a byte representation of a swap interval and returns the swap interval
  /// @dev Will revert with InvalidMask if the byte representation is not valid
  /// @param _mask The byte representation
  /// @return The swap interval
  function maskToInterval(bytes1 _mask) internal pure returns (uint32) {
    if (_mask == 0x01) return 1 minutes;
    if (_mask == 0x02) return 5 minutes;
    if (_mask == 0x04) return 15 minutes;
    if (_mask == 0x08) return 30 minutes;
    if (_mask == 0x10) return 1 hours;
    if (_mask == 0x20) return 4 hours;
    if (_mask == 0x40) return 1 days;
    if (_mask == 0x80) return 1 weeks;
    revert InvalidMask();
  }

  /// @notice Takes a byte representation of a set of swap intervals and returns which ones are in the set
  /// @dev Will always return an array of length 8, with zeros at the end if there are less than 8 intervals
  /// @param _byte The byte representation
  /// @return _intervals The swap intervals in the set
  function intervalsInByte(bytes1 _byte) internal pure returns (uint32[] memory _intervals) {
    _intervals = new uint32[](8);
    uint8 _index;
    bytes1 _mask = 0x01;
    while (_byte >= _mask && _mask > 0) {
      if (_byte & _mask != 0) {
        _intervals[_index++] = maskToInterval(_mask);
      }
      _mask <<= 1;
    }
  }
}
Governable.sol 57 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import './interfaces/IGovernable.sol';

/**
 * @notice This contract is meant to be used in other contracts. By using this contract,
 *         a specific address will be given a "governor" role, which basically will be able to
 *         control certains aspects of the contract. There are other contracts that do the same,
 *         but this contract forces a new governor to accept the role before it's transferred.
 *         This is a basically a safety measure to prevent losing access to the contract.
 */
abstract contract Governable is IGovernable {
  /// @inheritdoc IGovernable
  address public governor;

  /// @inheritdoc IGovernable
  address public pendingGovernor;

  constructor(address _governor) {
    if (_governor == address(0)) revert GovernorIsZeroAddress();
    governor = _governor;
  }

  /// @inheritdoc IGovernable
  function isGovernor(address _account) public view returns (bool) {
    return _account == governor;
  }

  /// @inheritdoc IGovernable
  function isPendingGovernor(address _account) public view returns (bool) {
    return _account == pendingGovernor;
  }

  /// @inheritdoc IGovernable
  function setPendingGovernor(address _pendingGovernor) external onlyGovernor {
    pendingGovernor = _pendingGovernor;
    emit PendingGovernorSet(_pendingGovernor);
  }

  /// @inheritdoc IGovernable
  function acceptPendingGovernor() external onlyPendingGovernor {
    governor = pendingGovernor;
    pendingGovernor = address(0);
    emit PendingGovernorAccepted();
  }

  modifier onlyGovernor() {
    if (!isGovernor(msg.sender)) revert OnlyGovernor();
    _;
  }

  modifier onlyPendingGovernor() {
    if (!isPendingGovernor(msg.sender)) revert OnlyPendingGovernor();
    _;
  }
}
TokenSorting.sol 15 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >0.6;

/// @title TokenSorting library
/// @notice Provides functions to sort tokens easily
library TokenSorting {
  /// @notice Takes two tokens, and returns them sorted
  /// @param _tokenA One of the tokens
  /// @param _tokenB The other token
  /// @return __tokenA The first of the tokens
  /// @return __tokenB The second of the tokens
  function sortTokens(address _tokenA, address _tokenB) internal pure returns (address __tokenA, address __tokenB) {
    (__tokenA, __tokenB) = _tokenA < _tokenB ? (_tokenA, _tokenB) : (_tokenB, _tokenA);
  }
}
SwapAdapter.sol 93 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import '@openzeppelin/contracts/utils/Address.sol';

abstract contract SwapAdapter {
  using SafeERC20 for IERC20;
  using Address for address;
  using Address for address payable;

  /// @notice Describes how the allowance should be revoked for the given spender
  struct RevokeAction {
    address spender;
    IERC20[] tokens;
  }

  address public constant PROTOCOL_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

  /**
   * @notice Takes the given amount of tokens from the caller
   * @param _token The token to check
   * @param _amount The amount to take
   */
  function _takeFromMsgSender(IERC20 _token, uint256 _amount) internal virtual {
    _token.safeTransferFrom(msg.sender, address(this), _amount);
  }

  /**
   * @notice Executes a swap for the given swapper
   * @param _swapper The actual swapper
   * @param _swapData The swap execution data
   */
  function _executeSwap(
    address _swapper,
    bytes calldata _swapData,
    uint256 _value
  ) internal virtual {
    _swapper.functionCallWithValue(_swapData, _value);
  }

  /**
   * @notice Transfers the given amount of tokens from the contract to the recipient
   * @param _token The token to check
   * @param _amount The amount to send
   * @param _recipient The recipient
   */
  function _sendToRecipient(
    address _token,
    uint256 _amount,
    address _recipient
  ) internal virtual {
    if (_recipient == address(0)) _recipient = msg.sender;
    if (_token == PROTOCOL_TOKEN) {
      payable(_recipient).sendValue(_amount);
    } else {
      IERC20(_token).safeTransfer(_recipient, _amount);
    }
  }

  /**
   * @notice Checks if the contract has any balance of the given token, and if it does,
   *         it sends it to the given recipient
   * @param _token The token to check
   * @param _recipient The recipient of the token balance
   */
  function _sendBalanceOnContractToRecipient(address _token, address _recipient) internal virtual {
    uint256 _balance = _token == PROTOCOL_TOKEN ? address(this).balance : IERC20(_token).balanceOf(address(this));
    if (_balance > 0) {
      _sendToRecipient(_token, _balance, _recipient);
    }
  }

  /**
   * @notice Revokes ERC20 allowances for the given spenders
   * @dev If exposed, then it should be permissioned
   * @param _revokeActions The spenders and tokens to revoke
   */
  function _revokeAllowances(RevokeAction[] calldata _revokeActions) internal virtual {
    for (uint256 i = 0; i < _revokeActions.length; ) {
      RevokeAction memory _action = _revokeActions[i];
      for (uint256 j = 0; j < _action.tokens.length; ) {
        _action.tokens[j].forceApprove(_action.spender, 0);
        unchecked {
          j++;
        }
      }
      unchecked {
        i++;
      }
    }
  }
}
ITokenPriceOracle.sol 78 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/**
 * @title The interface for an oracle that provides price quotes
 * @notice These methods allow users to add support for pairs, and then ask for quotes
 */
interface ITokenPriceOracle {
  /// @notice Thrown when trying to add support for a pair that cannot be supported
  error PairCannotBeSupported(address tokenA, address tokenB);

  /// @notice Thrown when trying to execute a quote with a pair that isn't supported yet
  error PairNotSupportedYet(address tokenA, address tokenB);

  /**
   * @notice Returns whether this oracle can support the given pair of tokens
   * @dev tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @return Whether the given pair of tokens can be supported by the oracle
   */
  function canSupportPair(address tokenA, address tokenB) external view returns (bool);

  /**
   * @notice Returns whether this oracle is already supporting the given pair of tokens
   * @dev tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @return Whether the given pair of tokens is already being supported by the oracle
   */
  function isPairAlreadySupported(address tokenA, address tokenB) external view returns (bool);

  /**
   * @notice Returns a quote, based on the given tokens and amount
   * @dev Will revert if pair isn't supported
   * @param tokenIn The token that will be provided
   * @param amountIn The amount that will be provided
   * @param tokenOut The token we would like to quote
   * @param data Custom data that the oracle might need to operate
   * @return amountOut How much `tokenOut` will be returned in exchange for `amountIn` amount of `tokenIn`
   */
  function quote(
    address tokenIn,
    uint256 amountIn,
    address tokenOut,
    bytes calldata data
  ) external view returns (uint256 amountOut);

  /**
   * @notice Add or reconfigures the support for a given pair. This function will let the oracle take some actions
   *         to configure the pair, in preparation for future quotes. Can be called many times in order to let the oracle
   *         re-configure for a new context
   * @dev Will revert if pair cannot be supported. tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @param data Custom data that the oracle might need to operate
   */
  function addOrModifySupportForPair(
    address tokenA,
    address tokenB,
    bytes calldata data
  ) external;

  /**
   * @notice Adds support for a given pair if the oracle didn't support it already. If called for a pair that is already supported,
   *         then nothing will happen. This function will let the oracle take some actions to configure the pair, in preparation
   *         for future quotes
   * @dev Will revert if pair cannot be supported. tokenA and tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param tokenA One of the pair's tokens
   * @param tokenB The other of the pair's tokens
   * @param data Custom data that the oracle might need to operate
   */
  function addSupportForPairIfNeeded(
    address tokenA,
    address tokenB,
    bytes calldata data
  ) external;
}
BaseCompanion.sol 153 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import './SwapAdapter.sol';
import './PayableMulticall.sol';
import {SimulationAdapter} from '@mean-finance/call-simulation/contracts/SimulationAdapter.sol';
import {IPermit2} from '../interfaces/external/IPermit2.sol';
import {Permit2Transfers} from '../libraries/Permit2Transfers.sol';
import './Governable.sol';

/**
 * @notice This contract will work as base companion for all our contracts. It will extend the capabilities of our companion
 *         contracts so that they can execute multicalls, swaps, revokes and more
 * @dev All public functions are payable, so that they can be multicalled together with other payable functions when msg.value > 0
 */
abstract contract BaseCompanion is SimulationAdapter, Governable, SwapAdapter, PayableMulticall {
  using Permit2Transfers for IPermit2;
  using SafeERC20 for IERC20;

  /**
   * @notice Returns the address of the Permit2 contract
   * @dev This value is constant and cannot change
   * @return The address of the Permit2 contract
   */
  // solhint-disable-next-line var-name-mixedcase
  IPermit2 public immutable PERMIT2;

  /// @notice The address of the swapper
  address public swapper;

  /// @notice The address of the allowance target
  address public allowanceTarget;

  constructor(
    address _swapper,
    address _allowanceTarget,
    address _governor,
    IPermit2 _permit2
  ) SwapAdapter() Governable(_governor) {
    swapper = _swapper;
    allowanceTarget = _allowanceTarget;
    PERMIT2 = _permit2;
  }

  receive() external payable {}

  /**
   * @notice Sends the specified amount of the given token to the recipient
   * @param _token The token to transfer
   * @param _amount The amount to transfer
   * @param _recipient The recipient of the token balance
   */
  function sendToRecipient(
    address _token,
    uint256 _amount,
    address _recipient
  ) external payable {
    _sendToRecipient(_token, _amount, _recipient);
  }

  /**
   * @notice Takes the given amount of tokens from the caller and transfers it to this contract
   * @param _token The token to take
   * @param _amount The amount to take
   */
  function takeFromCaller(
    IERC20 _token,
    uint256 _amount,
    address _recipient
  ) external payable {
    _token.safeTransferFrom(msg.sender, _recipient, _amount);
  }

  /**
   * @notice Executes a swap against the swapper
   * @param _allowanceToken The token to set allowance for (can be set to zero address to ignore)
   * @param _value The value to send to the swapper as part of the swap
   * @param _swapData The swap data
   * @param _tokenOut The token that will be bought as part of the swap
   */
  function runSwap(
    address _allowanceToken,
    uint256 _value,
    bytes calldata _swapData,
    address _tokenOut
  ) external payable returns (uint256 _amountOut) {
    if (_allowanceToken != address(0)) {
      IERC20(_allowanceToken).forceApprove(allowanceTarget, type(uint256).max);
    }

    _executeSwap(swapper, _swapData, _value);

    _amountOut = _tokenOut == PROTOCOL_TOKEN ? address(this).balance : IERC20(_tokenOut).balanceOf(address(this));
  }

  /**
   * @notice Takes the given amount of tokens from the caller with Permit2 and transfers it to this contract
   * @param _token The token to take
   * @param _amount The amount to take
   * @param _nonce The signed nonce
   * @param _deadline The signature's deadline
   * @param _signature The owner's signature
   * @param _recipient The address that will receive the funds
   */
  function permitTakeFromCaller(
    address _token,
    uint256 _amount,
    uint256 _nonce,
    uint256 _deadline,
    bytes calldata _signature,
    address _recipient
  ) external payable {
    PERMIT2.takeFromCaller(_token, _amount, _nonce, _deadline, _signature, _recipient);
  }

  /**
   * @notice Takes the a batch of tokens from the caller with Permit2 and transfers it to this contract
   * @param _tokens The tokens to take
   * @param _nonce The signed nonce
   * @param _deadline The signature's deadline
   * @param _signature The owner's signature
   * @param _recipient The address that will receive the funds
   */
  function batchPermitTakeFromCaller(
    IPermit2.TokenPermissions[] calldata _tokens,
    uint256 _nonce,
    uint256 _deadline,
    bytes calldata _signature,
    address _recipient
  ) external payable {
    PERMIT2.batchTakeFromCaller(_tokens, _nonce, _deadline, _signature, _recipient);
  }

  /**
   * @notice Checks if the contract has any balance of the given token, and if it does,
   *         it sends it to the given recipient
   * @param _token The token to check
   * @param _recipient The recipient of the token balance
   */
  function sendBalanceOnContractToRecipient(address _token, address _recipient) external payable {
    _sendBalanceOnContractToRecipient(_token, _recipient);
  }

  /**
   * @notice Sets a new swapper and allowance target
   * @param _newSwapper The address of the new swapper
   * @param _newAllowanceTarget The address of the new allowance target
   */
  function setSwapper(address _newSwapper, address _newAllowanceTarget) external onlyGovernor {
    swapper = _newSwapper;
    allowanceTarget = _newAllowanceTarget;
  }
}
IERC165.sol 14 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

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);
}
ISimulationAdapter.sol 19 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

interface ISimulationAdapter {
  /// @notice A simulation's result
  struct SimulationResult {
    bool success;
    bytes result;
    uint256 gasSpent;
  }

  /**
   * @notice Executes individual simulations against this contract but doesn't modify the state when doing so
   * @dev This function is meant to be used for off-chain simulation and should not be called on-chain
   * @param calls The calls to simulate
   * @return results Each simulation result
   */
  function simulate(bytes[] calldata calls) external payable returns (SimulationResult[] memory results);
}
PayableMulticall.sol 30 lines
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/utils/Address.sol';

/**
 * @dev Adding this contract will enable batching calls. This is basically the same as Open Zeppelin's
 *      Multicall contract, but we have made it payable. It supports both payable and non payable
 *      functions. However, if `msg.value` is not zero, then non payable functions cannot be called.
 *      Any contract that uses this Multicall version should be very careful when using msg.value.
 *      For more context, read: https://github.com/Uniswap/v3-periphery/issues/52
 */
abstract contract PayableMulticall {
  /**
   * @notice Receives and executes a batch of function calls on this contract.
   * @param _data A list of different function calls to execute
   * @return _results The result of executing each of those calls
   */
  function multicall(bytes[] calldata _data) external payable returns (bytes[] memory _results) {
    _results = new bytes[](_data.length);
    for (uint256 i = 0; i < _data.length; ) {
      _results[i] = Address.functionDelegateCall(address(this), _data[i]);
      unchecked {
        i++;
      }
    }
    return _results;
  }
}
ISharedTypes.sol 8 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

/// @notice A pair of tokens
struct Pair {
  address tokenA;
  address tokenB;
}
InputBuilding.sol 133 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '../interfaces/ISharedTypes.sol';

/// @title Input Building Library
/// @notice Provides functions to build input for swap related actions
/// @dev Please note that these functions are very expensive. Ideally, these would be used for off-chain purposes
library InputBuilding {
  /// @notice Takes a list of pairs and returns the input necessary to check the next swap
  /// @dev Even though this function allows it, the DCAHub will fail if duplicated pairs are used
  /// @return _tokens A sorted list of all the tokens involved in the swap
  /// @return _pairsToSwap A sorted list of indexes that represent the pairs involved in the swap
  function buildGetNextSwapInfoInput(Pair[] calldata _pairs)
    internal
    pure
    returns (address[] memory _tokens, IDCAHub.PairIndexes[] memory _pairsToSwap)
  {
    (_tokens, _pairsToSwap, ) = buildSwapInput(_pairs, new IDCAHub.AmountOfToken[](0));
  }

  /// @notice Takes a list of pairs and a list of tokens to borrow and returns the input necessary to execute a swap
  /// @dev Even though this function allows it, the DCAHub will fail if duplicated pairs are used
  /// @return _tokens A sorted list of all the tokens involved in the swap
  /// @return _pairsToSwap A sorted list of indexes that represent the pairs involved in the swap
  /// @return _borrow A list of amounts to borrow, based on the sorted token list
  function buildSwapInput(Pair[] calldata _pairs, IDCAHub.AmountOfToken[] memory _toBorrow)
    internal
    pure
    returns (
      address[] memory _tokens,
      IDCAHub.PairIndexes[] memory _pairsToSwap,
      uint256[] memory _borrow
    )
  {
    _tokens = _calculateUniqueTokens(_pairs, _toBorrow);
    _pairsToSwap = _calculatePairIndexes(_pairs, _tokens);
    _borrow = _calculateTokensToBorrow(_toBorrow, _tokens);
  }

  /// @dev Given a list of token pairs and tokens to borrow, returns a list of all the tokens involved, sorted
  function _calculateUniqueTokens(Pair[] memory _pairs, IDCAHub.AmountOfToken[] memory _toBorrow)
    private
    pure
    returns (address[] memory _tokens)
  {
    uint256 _uniqueTokens;
    address[] memory _tokensPlaceholder = new address[](_pairs.length * 2 + _toBorrow.length);

    // Load tokens in pairs onto placeholder
    for (uint256 i; i < _pairs.length; i++) {
      bool _foundA = false;
      bool _foundB = false;
      for (uint256 j; j < _uniqueTokens && !(_foundA && _foundB); j++) {
        if (!_foundA && _tokensPlaceholder[j] == _pairs[i].tokenA) _foundA = true;
        if (!_foundB && _tokensPlaceholder[j] == _pairs[i].tokenB) _foundB = true;
      }

      if (!_foundA) _tokensPlaceholder[_uniqueTokens++] = _pairs[i].tokenA;
      if (!_foundB) _tokensPlaceholder[_uniqueTokens++] = _pairs[i].tokenB;
    }

    // Load tokens to borrow onto placeholder
    for (uint256 i; i < _toBorrow.length; i++) {
      bool _found = false;
      for (uint256 j; j < _uniqueTokens && !_found; j++) {
        if (_tokensPlaceholder[j] == _toBorrow[i].token) _found = true;
      }
      if (!_found) _tokensPlaceholder[_uniqueTokens++] = _toBorrow[i].token;
    }

    // Load sorted into new array
    _tokens = new address[](_uniqueTokens);
    for (uint256 i; i < _uniqueTokens; i++) {
      address _token = _tokensPlaceholder[i];

      // Find index where the token should be
      uint256 _tokenIndex;
      while (_tokens[_tokenIndex] < _token && _tokens[_tokenIndex] != address(0)) _tokenIndex++;

      // Move everything one place back
      for (uint256 j = i; j > _tokenIndex; j--) {
        _tokens[j] = _tokens[j - 1];
      }

      // Set token on the correct index
      _tokens[_tokenIndex] = _token;
    }
  }

  /// @dev Given a list of pairs, and a list of sorted tokens, it translates the first list into indexes of the second list. This list of indexes will
  /// be sorted. For example, if pairs are [{ tokenA, tokenB }, { tokenC, tokenB }] and tokens are: [ tokenA, tokenB, tokenC ], the following is returned
  /// [ { 0, 1 }, { 1, 1 }, { 1, 2 } ]
  function _calculatePairIndexes(Pair[] calldata _pairs, address[] memory _tokens)
    private
    pure
    returns (IDCAHub.PairIndexes[] memory _pairIndexes)
  {
    _pairIndexes = new IDCAHub.PairIndexes[](_pairs.length);
    uint256 _count;

    for (uint8 i; i < _tokens.length; i++) {
      for (uint8 j = i + 1; j < _tokens.length; j++) {
        for (uint256 k; k < _pairs.length; k++) {
          if (
            (_tokens[i] == _pairs[k].tokenA && _tokens[j] == _pairs[k].tokenB) ||
            (_tokens[i] == _pairs[k].tokenB && _tokens[j] == _pairs[k].tokenA)
          ) {
            _pairIndexes[_count++] = IDCAHubSwapHandler.PairIndexes({indexTokenA: i, indexTokenB: j});
          }
        }
      }
    }
  }

  /// @dev Given a list of tokens to borrow and a list of sorted tokens, it translated the first list into a list of amounts, sorted by the indexed of
  /// the seconds list. For example, if `toBorrow` are [{ tokenA, 100 }, { tokenC, 200 }, { tokenB, 500 }] and tokens are [ tokenA, tokenB, tokenC], the
  /// following is returned [100, 500, 200]
  function _calculateTokensToBorrow(IDCAHub.AmountOfToken[] memory _toBorrow, address[] memory _tokens)
    private
    pure
    returns (uint256[] memory _borrow)
  {
    _borrow = new uint256[](_tokens.length);

    for (uint256 i; i < _toBorrow.length; i++) {
      uint256 j;
      while (_tokens[j] != _toBorrow[i].token) j++;
      _borrow[j] = _toBorrow[i].amount;
    }
  }
}
ILegacyDCAHub.sol 79 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';

interface ILegacyDCAHub {
  /// @notice Information about a swap
  struct SwapInfo {
    // The tokens involved in the swap
    TokenInSwap[] tokens;
    // The pairs involved in the swap
    PairInSwap[] pairs;
  }

  /// @notice Information about a token's role in a swap
  struct TokenInSwap {
    // The token's address
    address token;
    // How much will be given of this token as a reward
    uint256 reward;
    // How much of this token needs to be provided by swapper
    uint256 toProvide;
    // How much of this token will be paid to the platform
    uint256 platformFee;
  }

  /// @notice Information about a pair in a swap
  struct PairInSwap {
    // The address of one of the tokens
    address tokenA;
    // The address of the other token
    address tokenB;
    // How much is 1 unit of token A when converted to B
    uint256 ratioAToB;
    // How much is 1 unit of token B when converted to A
    uint256 ratioBToA;
    // The swap intervals involved in the swap, represented as a byte
    bytes1 intervalsInSwap;
  }

  /**
   * @notice Returns all information related to the next swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   * @param tokens The tokens involved in the next swap
   * @param pairs The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @return swapInformation The information about the next swap
   */
  function getNextSwapInfo(address[] calldata tokens, IDCAHub.PairIndexes[] calldata pairs)
    external
    view
    returns (SwapInfo memory swapInformation);

  /**
   * @notice Executes a flash swap
   * @dev Will revert with:
   *      - With InvalidTokens if tokens are not sorted, or if there are duplicates
   *      - With InvalidPairs if pairs are not sorted (first by indexTokenA and then indexTokenB), or if indexTokenA >= indexTokenB for any pair
   *      - With Paused if swaps are paused by protocol
   *      - With NoSwapsToExecute if there are no swaps to execute for the given pairs
   *      - With LiquidityNotReturned if the required tokens were not back during the callback
   * @param tokens The tokens involved in the next swap
   * @param pairsToSwap The pairs that you want to swap. Each element of the list points to the index of the token in the tokens array
   * @param rewardRecipient The address to send the reward to
   * @param callbackHandler Address to call for callback (and send the borrowed tokens to)
   * @param borrow How much to borrow of each of the tokens in tokens. The amount must match the position of the token in the tokens array
   * @param callbackData Bytes to send to the caller during the callback
   * @return Information about the executed swap
   */
  function swap(
    address[] calldata tokens,
    IDCAHub.PairIndexes[] calldata pairsToSwap,
    address rewardRecipient,
    address callbackHandler,
    uint256[] calldata borrow,
    bytes calldata callbackData
  ) external returns (SwapInfo memory);
}
Permit2Transfers.sol 90 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import {IPermit2} from '../interfaces/external/IPermit2.sol';

/**
 * @title Permit2 Transfers Library
 * @author Sam Bugs
 * @notice A small library to call Permit2's transfer from methods
 */
library Permit2Transfers {
  /**
   * @notice Executes a transfer from using Permit2
   * @param _permit2 The Permit2 contract
   * @param _token The token to transfer
   * @param _amount The amount to transfer
   * @param _nonce The owner's nonce
   * @param _deadline The signature's expiration deadline
   * @param _signature The signature that allows the transfer
   * @param _recipient The address that will receive the funds
   */
  function takeFromCaller(
    IPermit2 _permit2,
    address _token,
    uint256 _amount,
    uint256 _nonce,
    uint256 _deadline,
    bytes calldata _signature,
    address _recipient
  ) internal {
    _permit2.permitTransferFrom(
      // The permit message.
      IPermit2.PermitTransferFrom({permitted: IPermit2.TokenPermissions({token: _token, amount: _amount}), nonce: _nonce, deadline: _deadline}),
      // The transfer recipient and amount.
      IPermit2.SignatureTransferDetails({to: _recipient, requestedAmount: _amount}),
      // The owner of the tokens, which must also be
      // the signer of the message, otherwise this call
      // will fail.
      msg.sender,
      // The packed signature that was the result of signing
      // the EIP712 hash of `permit`.
      _signature
    );
  }

  /**
   * @notice Executes a batch transfer from using Permit2
   * @param _permit2 The Permit2 contract
   * @param _tokens The amount of tokens to transfer
   * @param _nonce The owner's nonce
   * @param _deadline The signature's expiration deadline
   * @param _signature The signature that allows the transfer
   * @param _recipient The address that will receive the funds
   */
  function batchTakeFromCaller(
    IPermit2 _permit2,
    IPermit2.TokenPermissions[] calldata _tokens,
    uint256 _nonce,
    uint256 _deadline,
    bytes calldata _signature,
    address _recipient
  ) internal {
    if (_tokens.length > 0) {
      _permit2.permitTransferFrom(
        // The permit message.
        IPermit2.PermitBatchTransferFrom({permitted: _tokens, nonce: _nonce, deadline: _deadline}),
        // The transfer recipients and amounts.
        _buildTransferDetails(_tokens, _recipient),
        // The owner of the tokens, which must also be
        // the signer of the message, otherwise this call
        // will fail.
        msg.sender,
        // The packed signature that was the result of signing
        // the EIP712 hash of `permit`.
        _signature
      );
    }
  }

  function _buildTransferDetails(IPermit2.TokenPermissions[] calldata _tokens, address _recipient)
    private
    pure
    returns (IPermit2.SignatureTransferDetails[] memory _details)
  {
    _details = new IPermit2.SignatureTransferDetails[](_tokens.length);
    for (uint256 i; i < _details.length; ++i) {
      _details[i] = IPermit2.SignatureTransferDetails({to: _recipient, requestedAmount: _tokens[i].amount});
    }
  }
}
IDCAPermissionManager.sol 270 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
import '@mean-finance/nft-descriptors/solidity/interfaces/IDCAHubPositionDescriptor.sol';

interface IERC721BasicEnumerable {
  /**
   * @notice Count NFTs tracked by this contract
   * @return A count of valid NFTs tracked by this contract, where each one of
   *         them has an assigned and queryable owner not equal to the zero address
   */
  function totalSupply() external view returns (uint256);
}

/**
 * @title The interface for all permission related matters
 * @notice These methods allow users to set and remove permissions to their positions
 */
interface IDCAPermissionManager is IERC721, IERC721BasicEnumerable {
  /// @notice Set of possible permissions
  enum Permission {
    INCREASE,
    REDUCE,
    WITHDRAW,
    TERMINATE
  }

  /// @notice A set of permissions for a specific operator
  struct PermissionSet {
    // The address of the operator
    address operator;
    // The permissions given to the overator
    Permission[] permissions;
  }

  /// @notice A collection of permissions sets for a specific position
  struct PositionPermissions {
    // The id of the token
    uint256 tokenId;
    // The permissions to assign to the position
    PermissionSet[] permissionSets;
  }

  /**
   * @notice Emitted when permissions for a token are modified
   * @param tokenId The id of the token
   * @param permissions The set of permissions that were updated
   */
  event Modified(uint256 tokenId, PermissionSet[] permissions);

  /**
   * @notice Emitted when the address for a new descritor is set
   * @param descriptor The new descriptor contract
   */
  event NFTDescriptorSet(IDCAHubPositionDescriptor descriptor);

  /// @notice Thrown when a user tries to set the hub, once it was already set
  error HubAlreadySet();

  /// @notice Thrown when a user provides a zero address when they shouldn't
  error ZeroAddress();

  /// @notice Thrown when a user calls a method that can only be executed by the hub
  error OnlyHubCanExecute();

  /// @notice Thrown when a user tries to modify permissions for a token they do not own
  error NotOwner();

  /// @notice Thrown when a user tries to execute a permit with an expired deadline
  error ExpiredDeadline();

  /// @notice Thrown when a user tries to execute a permit with an invalid signature
  error InvalidSignature();

  /**
   * @notice The permit typehash used in the permit signature
   * @return The typehash for the permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the permission permit signature
   * @return The typehash for the permission permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMISSION_PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the multi permission permit signature
   * @return The typehash for the multi permission permit
   */
  // solhint-disable-next-line func-name-mixedcase
  function MULTI_PERMISSION_PERMIT_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the permission permit signature
   * @return The typehash for the permission set
   */
  // solhint-disable-next-line func-name-mixedcase
  function PERMISSION_SET_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The permit typehash used in the multi permission permit signature
   * @return The typehash for the position permissions
   */
  // solhint-disable-next-line func-name-mixedcase
  function POSITION_PERMISSIONS_TYPEHASH() external pure returns (bytes32);

  /**
   * @notice The domain separator used in the permit signature
   * @return The domain seperator used in encoding of permit signature
   */
  // solhint-disable-next-line func-name-mixedcase
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  /**
   * @notice Returns the NFT descriptor contract
   * @return The contract for the NFT descriptor
   */
  function nftDescriptor() external returns (IDCAHubPositionDescriptor);

  /**
   * @notice Returns the address of the DCA Hub
   * @return The address of the DCA Hub
   */
  function hub() external returns (address);

  /**
   * @notice Returns the next nonce to use for a given user
   * @param user The address of the user
   * @return nonce The next nonce to use
   */
  function nonces(address user) external returns (uint256 nonce);

  /**
   * @notice Returns whether the given address has the permission for the given token
   * @param id The id of the token to check
   * @param account The address of the user to check
   * @param permission The permission to check
   * @return Whether the user has the permission or not
   */
  function hasPermission(
    uint256 id,
    address account,
    Permission permission
  ) external view returns (bool);

  /**
   * @notice Returns whether the given address has the permissions for the given token
   * @param id The id of the token to check
   * @param account The address of the user to check
   * @param permissions The permissions to check
   * @return hasPermissions Whether the user has each permission or not
   */
  function hasPermissions(
    uint256 id,
    address account,
    Permission[] calldata permissions
  ) external view returns (bool[] memory hasPermissions);

  /**
   * @notice Sets the address for the hub
   * @dev Can only be successfully executed once. Once it's set, it can be modified again
   *      Will revert:
   *      - With ZeroAddress if address is zero
   *      - With HubAlreadySet if the hub has already been set
   * @param hub The address to set for the hub
   */
  function setHub(address hub) external;

  /**
   * @notice Mints a new NFT with the given id, and sets the permissions for it
   * @dev Will revert with OnlyHubCanExecute if the caller is not the hub
   * @param id The id of the new NFT
   * @param owner The owner of the new NFT
   * @param permissions Permissions to set for the new NFT
   */
  function mint(
    uint256 id,
    address owner,
    PermissionSet[] calldata permissions
  ) external;

  /**
   * @notice Burns the NFT with the given id, and clears all permissions
   * @dev Will revert with OnlyHubCanExecute if the caller is not the hub
   * @param id The token's id
   */
  function burn(uint256 id) external;

  /**
   * @notice Sets new permissions for the given position
   * @dev Will revert with NotOwner if the caller is not the token's owner.
   *      Operators that are not part of the given permission sets do not see their permissions modified.
   *      In order to remove permissions to an operator, provide an empty list of permissions for them
   * @param id The token's id
   * @param permissions A list of permission sets
   */
  function modify(uint256 id, PermissionSet[] calldata permissions) external;

  /**
   * @notice Sets new permissions for the given positions
   * @dev This is basically the same as executing multiple `modify`
   * @param permissions A list of position permissions to set
   */
  function modifyMany(PositionPermissions[] calldata permissions) external;

  /**
   * @notice Approves spending of a specific token ID by spender via signature
   * @param spender The account that is being approved
   * @param tokenId The ID of the token that is being approved for spending
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permit(
    address spender,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets permissions via signature
   * @dev This method works similarly to `modifyMany`, but instead of being executed by the owner, it can be set by signature
   * @param permissions The permissions to set for the different positions
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function multiPermissionPermit(
    PositionPermissions[] calldata permissions,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets permissions via signature
   * @dev This method works similarly to `modify`, but instead of being executed by the owner, it can be set my signature
   * @param permissions The permissions to set
   * @param tokenId The token's id
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permissionPermit(
    PermissionSet[] calldata permissions,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external;

  /**
   * @notice Sets a new NFT descriptor
   * @dev Will revert with ZeroAddress if address is zero
   * @param descriptor The new NFT descriptor contract
   */
  function setNFTDescriptor(IDCAHubPositionDescriptor descriptor) external;
}
IDCAHubCompanion.sol 240 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAPermissionManager.sol';
import './ILegacyDCAHub.sol';
import './ISharedTypes.sol';

/**
 * @notice This contract exposes many utils that are also available through libraries. The idea is to make
 *         these functions available here, so others don't need to deploy new contracts
 */
interface IDCAHubCompanionLibrariesHandler {
  /**
   * @notice Takes a list of pairs and returns how it would look like to execute a swap for all of them
   * @dev Please note that this function is very expensive. Ideally, it would be used for off-chain purposes
   * @param hub The address of the DCAHub
   * @param pairs The pairs to be involved in the swap
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the next swap information for privileged and non-privileged accounts
   * @param oracleData Bytes to send to the oracle when executing a quote
   * @return How executing a swap for all the given pairs would look like
   */
  function getNextSwapInfo(
    IDCAHub hub,
    Pair[] calldata pairs,
    bool calculatePrivilegedAvailability,
    bytes calldata oracleData
  ) external view returns (IDCAHub.SwapInfo memory);

  /**
   * @notice Takes a list of pairs and returns how it would look like to execute a swap for all of them
   * @dev Please note that this function is very expensive. Ideally, it would be used for off-chain purposes
   * @param hub The address of the DCAHub
   * @param pairs The pairs to be involved in the swap
   * @return How executing a swap for all the given pairs would look like
   */
  function legacyGetNextSwapInfo(ILegacyDCAHub hub, Pair[] calldata pairs) external view returns (ILegacyDCAHub.SwapInfo memory);

  /**
   * @notice Returns how many seconds left until the next swap is available for a list of pairs
   * @dev Tokens in pairs may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param hub The address of the DCAHub
   * @param pairs Pairs to check
   * @param calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   * @return The amount of seconds until next swap for each of the pairs
   */
  function secondsUntilNextSwap(
    IDCAHub hub,
    Pair[] calldata pairs,
    bool calculatePrivilegedAvailability
  ) external view returns (uint256[] memory);
}

interface IDCAHubCompanionHubProxyHandler {
  /// @notice Thrown when a user tries operate on a position that they don't have access to
  error UnauthorizedCaller();

  /**
   * @notice Creates a new position
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amount How many "from" tokens will be swapped in total
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param miscellaneous Bytes that will be emitted, and associated with the position. If empty, no event will be emitted
   * @return positionId The id of the created position
   */
  function deposit(
    IDCAHub hub,
    address from,
    address to,
    uint256 amount,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external payable returns (uint256 positionId);

  /**
   * @notice Creates a new position using the entire balance available on the contract
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param from The address of the "from" token
   * @param to The address of the "to" token
   * @param amountOfSwaps How many swaps to execute for this position
   * @param swapInterval How frequently the position's swaps should be executed
   * @param owner The address of the owner of the position being created
   * @param miscellaneous Bytes that will be emitted, and associated with the position. If empty, no event will be emitted
   * @return positionId The id of the created position
   */
  function depositWithBalanceOnContract(
    IDCAHub hub,
    address from,
    address to,
    uint32 amountOfSwaps,
    uint32 swapInterval,
    address owner,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    bytes calldata miscellaneous
  ) external payable returns (uint256 positionId);

  /**
   * @notice Call the hub and withdraws all swapped tokens from a position to a recipient
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param recipient The address to withdraw swapped tokens to
   * @return swapped How much was withdrawn
   */
  function withdrawSwapped(
    IDCAHub hub,
    uint256 positionId,
    address recipient
  ) external payable returns (uint256 swapped);

  /**
   * @notice Call the hub and withdraws all swapped tokens from multiple positions
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positions A list positions, grouped by `to` token
   * @param recipient The address to withdraw swapped tokens to
   * @return withdrawn How much was withdrawn for each token
   */
  function withdrawSwappedMany(
    IDCAHub hub,
    IDCAHub.PositionSet[] calldata positions,
    address recipient
  ) external payable returns (uint256[] memory withdrawn);

  /**
   * @notice Call the hub and takes the unswapped balance, adds the new deposited funds and modifies the position so that
   * it is executed in `newSwaps` swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param amount Amount of funds to add to the position
   * @param newSwaps The new amount of swaps
   */
  function increasePosition(
    IDCAHub hub,
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps
  ) external payable;

  /**
   * @notice Call the hub and takes the unswapped balance, adds the Companion's current balance and modifies the position so that
   * it is executed in `newSwaps` swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param newSwaps The new amount of swaps
   */
  function increasePositionWithBalanceOnContract(
    IDCAHub hub,
    uint256 positionId,
    uint32 newSwaps
  ) external payable;

  /**
   * @notice Call the hub and withdraws the specified amount from the unswapped balance and modifies the position so that
   * it is executed in newSwaps swaps
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param amount Amount of funds to withdraw from the position
   * @param newSwaps The new amount of swaps
   * @param recipient The address to send tokens to
   */
  function reducePosition(
    IDCAHub hub,
    uint256 positionId,
    uint256 amount,
    uint32 newSwaps,
    address recipient
  ) external payable;

  /**
   * @notice Calls the hub and terminates the position and sends all unswapped and swapped balance to the specified recipients
   * @dev Meant to be used as part of a multicall
   * @param hub The address of the DCAHub
   * @param positionId The position's id
   * @param recipientUnswapped The address to withdraw unswapped tokens to
   * @param recipientSwapped The address to withdraw swapped tokens to
   * @return unswapped The unswapped balance sent to `recipientUnswapped`
   * @return swapped The swapped balance sent to `recipientSwapped`
   */
  function terminate(
    IDCAHub hub,
    uint256 positionId,
    address recipientUnswapped,
    address recipientSwapped
  ) external payable returns (uint256 unswapped, uint256 swapped);

  /**
   * @notice Calls the permission manager and sets multiple permissions via signature
   * @param permissionManager The address of the permission manager
   * @param permissions The permissions to set
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function multiPermissionPermit(
    IDCAPermissionManager permissionManager,
    IDCAPermissionManager.PositionPermissions[] calldata permissions,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external payable;

  /**
   * @notice Calls the permission manager and sets permissions via signature
   * @param permissionManager The address of the permission manager
   * @param permissions The permissions to set
   * @param tokenId The token's id
   * @param deadline The deadline timestamp by which the call must be mined for the approve to work
   * @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
   * @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
   * @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
   */
  function permissionPermit(
    IDCAPermissionManager permissionManager,
    IDCAPermissionManager.PermissionSet[] calldata permissions,
    uint256 tokenId,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
  ) external payable;
}

interface IDCAHubCompanion is IDCAHubCompanionLibrariesHandler, IDCAHubCompanionHubProxyHandler {}
IPermit2.sol 45 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

// Minimal Permit2 interface, derived from
// https://github.com/Uniswap/permit2/blob/main/src/interfaces/ISignatureTransfer.sol
interface IPermit2 {
  struct TokenPermissions {
    address token;
    uint256 amount;
  }

  struct PermitTransferFrom {
    TokenPermissions permitted;
    uint256 nonce;
    uint256 deadline;
  }

  struct PermitBatchTransferFrom {
    TokenPermissions[] permitted;
    uint256 nonce;
    uint256 deadline;
  }

  struct SignatureTransferDetails {
    address to;
    uint256 requestedAmount;
  }

  // solhint-disable-next-line func-name-mixedcase
  function DOMAIN_SEPARATOR() external view returns (bytes32);

  function permitTransferFrom(
    PermitTransferFrom calldata permit,
    SignatureTransferDetails calldata transferDetails,
    address owner,
    bytes calldata signature
  ) external;

  function permitTransferFrom(
    PermitBatchTransferFrom memory permit,
    SignatureTransferDetails[] calldata transferDetails,
    address owner,
    bytes calldata signature
  ) external;
}
IGovernable.sol 66 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/**
 * @title A contract that manages a "governor" role
 */
interface IGovernable {
  /// @notice Thrown when trying to set the zero address as governor
  error GovernorIsZeroAddress();

  /// @notice Thrown when trying to execute an action that only the governor an execute
  error OnlyGovernor();

  /// @notice Thrown when trying to execute an action that only the pending governor an execute
  error OnlyPendingGovernor();

  /**
   * @notice Emitted when a new pending governor is set
   * @param newPendingGovernor The new pending governor
   */
  event PendingGovernorSet(address newPendingGovernor);

  /**
   * @notice Emitted when the pending governor accepts the role and becomes the governor
   */
  event PendingGovernorAccepted();

  /**
   * @notice Returns the address of the governor
   * @return The address of the governor
   */
  function governor() external view returns (address);

  /**
   * @notice Returns the address of the pending governor
   * @return The address of the pending governor
   */
  function pendingGovernor() external view returns (address);

  /**
   * @notice Returns whether the given account is the current governor
   * @param account The account to check
   * @return Whether it is the current governor or not
   */
  function isGovernor(address account) external view returns (bool);

  /**
   * @notice Returns whether the given account is the pending governor
   * @param account The account to check
   * @return Whether it is the pending governor or not
   */
  function isPendingGovernor(address account) external view returns (bool);

  /**
   * @notice Sets a new pending governor
   * @dev Only the current governor can execute this action
   * @param pendingGovernor The new pending governor
   */
  function setPendingGovernor(address pendingGovernor) external;

  /**
   * @notice Sets the pending governor as the governor
   * @dev Only the pending governor can execute this action
   */
  function acceptPendingGovernor() external;
}
SecondsUntilNextSwap.sol 77 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7;

import '@mean-finance/dca-v2-core/contracts/interfaces/IDCAHub.sol';
import '@mean-finance/dca-v2-core/contracts/libraries/TokenSorting.sol';
import '@mean-finance/dca-v2-core/contracts/libraries/Intervals.sol';
import '../interfaces/ISharedTypes.sol';

/**
 * @title Seconds Until Next Swap Library
 * @notice Provides functions to calculate how long users have to wait until a pair's next swap is available
 */
library SecondsUntilNextSwap {
  /**
   * @notice Returns how many seconds left until the next swap is available for a specific pair
   * @dev _tokenA and _tokenB may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param _hub The address of the DCA Hub
   * @param _tokenA One of the pair's tokens
   * @param _tokenB The other of the pair's tokens
   * @param _calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   * @return The amount of seconds until next swap. Returns 0 if a swap can already be executed and max(uint256) if there is nothing to swap
   */
  function secondsUntilNextSwap(
    IDCAHub _hub,
    address _tokenA,
    address _tokenB,
    bool _calculatePrivilegedAvailability
  ) internal view returns (uint256) {
    (address __tokenA, address __tokenB) = TokenSorting.sortTokens(_tokenA, _tokenB);
    bytes1 _activeIntervals = _hub.activeSwapIntervals(__tokenA, __tokenB);
    bytes1 _mask = 0x01;
    uint256 _smallerIntervalBlocking;
    while (_activeIntervals >= _mask && _mask > 0) {
      if (_activeIntervals & _mask == _mask) {
        (, uint224 _nextAmountToSwapAToB, uint32 _lastSwappedAt, uint224 _nextAmountToSwapBToA) = _hub.swapData(_tokenA, _tokenB, _mask);
        uint32 _swapInterval = Intervals.maskToInterval(_mask);
        uint256 _nextAvailable = ((_lastSwappedAt / _swapInterval) + 1) * _swapInterval;
        if (!_calculatePrivilegedAvailability) {
          // If the caller does not have privileges, then they will have to wait a little more to execute swaps
          _nextAvailable += _swapInterval / 3;
        }
        if (_nextAmountToSwapAToB > 0 || _nextAmountToSwapBToA > 0) {
          if (_nextAvailable <= block.timestamp) {
            return _smallerIntervalBlocking;
          } else {
            return _nextAvailable - block.timestamp;
          }
        } else if (_nextAvailable > block.timestamp) {
          _smallerIntervalBlocking = _smallerIntervalBlocking == 0 ? _nextAvailable - block.timestamp : _smallerIntervalBlocking;
        }
      }
      _mask <<= 1;
    }
    return type(uint256).max;
  }

  /**
   * @notice Returns how many seconds left until the next swap is available for a list of pairs
   * @dev Tokens in pairs may be passed in either tokenA/tokenB or tokenB/tokenA order
   * @param _hub The address of the DCA Hub
   * @param _pairs Pairs to check
   * @return _seconds The amount of seconds until next swap for each of the pairs
   * @param _calculatePrivilegedAvailability Some accounts get privileged availability and can execute swaps before others. This flag provides
   *        the possibility to calculate the seconds until next swap for privileged and non-privileged accounts
   */
  function secondsUntilNextSwap(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability
  ) internal view returns (uint256[] memory _seconds) {
    _seconds = new uint256[](_pairs.length);
    for (uint256 i; i < _pairs.length; i++) {
      _seconds[i] = secondsUntilNextSwap(_hub, _pairs[i].tokenA, _pairs[i].tokenB, _calculatePrivilegedAvailability);
    }
  }
}
DCAHubCompanion.sol 15 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import './DCAHubCompanionLibrariesHandler.sol';
import './DCAHubCompanionHubProxyHandler.sol';
import '../utils/BaseCompanion.sol';

contract DCAHubCompanion is DCAHubCompanionLibrariesHandler, DCAHubCompanionHubProxyHandler, BaseCompanion, IDCAHubCompanion {
  constructor(
    address _swapper,
    address _allowanceTarget,
    address _governor,
    IPermit2 _permit2
  ) BaseCompanion(_swapper, _allowanceTarget, _governor, _permit2) {}
}
IDCAHubPositionDescriptor.sol 16 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;

/**
 * @title The interface for generating a description for a position in a DCA Hub
 * @notice Contracts that implement this interface must return a base64 JSON with the entire description
 */
interface IDCAHubPositionDescriptor {
  /**
   * @notice Generates a positions's description, both the JSON and the image inside
   * @param hub The address of the DCA Hub
   * @param positionId The token/position id
   * @return description The position's description
   */
  function tokenURI(address hub, uint256 positionId) external view returns (string memory description);
}
Address.sol 159 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

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

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

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

import '../interfaces/IDCAHubCompanion.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

/// @dev All public functions are payable, so that they can be multicalled together with other payable functions when msg.value > 0
abstract contract DCAHubCompanionHubProxyHandler is IDCAHubCompanionHubProxyHandler {
  using SafeERC20 for IERC20;

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function permissionPermit(
    IDCAPermissionManager _permissionManager,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    uint256 _tokenId,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external payable {
    _permissionManager.permissionPermit(_permissions, _tokenId, _deadline, _v, _r, _s);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function multiPermissionPermit(
    IDCAPermissionManager _permissionManager,
    IDCAPermissionManager.PositionPermissions[] calldata _permissions,
    uint256 _deadline,
    uint8 _v,
    bytes32 _r,
    bytes32 _s
  ) external payable {
    _permissionManager.multiPermissionPermit(_permissions, _deadline, _v, _r, _s);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function deposit(
    IDCAHub _hub,
    address _from,
    address _to,
    uint256 _amount,
    uint32 _amountOfSwaps,
    uint32 _swapInterval,
    address _owner,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    bytes calldata _miscellaneous
  ) public payable virtual returns (uint256 _positionId) {
    _approveHub(address(_from), _hub, _amount);
    _positionId = _miscellaneous.length > 0
      ? _hub.deposit(_from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions, _miscellaneous)
      : _hub.deposit(_from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function depositWithBalanceOnContract(
    IDCAHub _hub,
    address _from,
    address _to,
    uint32 _amountOfSwaps,
    uint32 _swapInterval,
    address _owner,
    IDCAPermissionManager.PermissionSet[] calldata _permissions,
    bytes calldata _miscellaneous
  ) external payable returns (uint256 _positionId) {
    uint256 _amount = IERC20(_from).balanceOf(address(this));
    return deposit(_hub, _from, _to, _amount, _amountOfSwaps, _swapInterval, _owner, _permissions, _miscellaneous);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function withdrawSwapped(
    IDCAHub _hub,
    uint256 _positionId,
    address _recipient
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.WITHDRAW) returns (uint256 _swapped) {
    _swapped = _hub.withdrawSwapped(_positionId, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function withdrawSwappedMany(
    IDCAHub _hub,
    IDCAHub.PositionSet[] calldata _positions,
    address _recipient
  ) external payable returns (uint256[] memory _withdrawn) {
    for (uint256 i = 0; i < _positions.length; ++i) {
      uint256[] memory _positionIds = _positions[i].positionIds;
      for (uint256 j = 0; j < _positionIds.length; ++j) {
        _checkPermissionOrFail(_hub, _positionIds[j], IDCAPermissionManager.Permission.WITHDRAW);
      }
    }
    _withdrawn = _hub.withdrawSwappedMany(_positions, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function increasePosition(
    IDCAHub _hub,
    uint256 _positionId,
    uint256 _amount,
    uint32 _newSwaps
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.INCREASE) {
    IERC20Metadata _from = _hub.userPosition(_positionId).from;
    _approveHub(address(_from), _hub, _amount);
    _hub.increasePosition(_positionId, _amount, _newSwaps);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function increasePositionWithBalanceOnContract(
    IDCAHub _hub,
    uint256 _positionId,
    uint32 _newSwaps
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.INCREASE) {
    IERC20Metadata _from = _hub.userPosition(_positionId).from;
    uint256 _amount = _from.balanceOf(address(this));
    _approveHub(address(_from), _hub, _amount);
    _hub.increasePosition(_positionId, _amount, _newSwaps);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function reducePosition(
    IDCAHub _hub,
    uint256 _positionId,
    uint256 _amount,
    uint32 _newSwaps,
    address _recipient
  ) external payable verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.REDUCE) {
    _hub.reducePosition(_positionId, _amount, _newSwaps, _recipient);
  }

  /// @inheritdoc IDCAHubCompanionHubProxyHandler
  function terminate(
    IDCAHub _hub,
    uint256 _positionId,
    address _recipientUnswapped,
    address _recipientSwapped
  )
    external
    payable
    verifyPermission(_hub, _positionId, IDCAPermissionManager.Permission.TERMINATE)
    returns (uint256 _unswapped, uint256 _swapped)
  {
    (_unswapped, _swapped) = _hub.terminate(_positionId, _recipientUnswapped, _recipientSwapped);
  }

  function _approveHub(
    address _token,
    IDCAHub _hub,
    uint256 _amount
  ) internal {
    uint256 _allowance = IERC20(_token).allowance(address(this), address(_hub));
    if (_allowance < _amount) {
      IERC20(_token).forceApprove(address(_hub), type(uint256).max);
    }
  }

  function _checkPermissionOrFail(
    IDCAHub _hub,
    uint256 _positionId,
    IDCAPermissionManager.Permission _permission
  ) internal view {
    if (!_hub.permissionManager().hasPermission(_positionId, msg.sender, _permission)) revert UnauthorizedCaller();
  }

  modifier verifyPermission(
    IDCAHub _hub,
    uint256 _positionId,
    IDCAPermissionManager.Permission _permission
  ) {
    _checkPermissionOrFail(_hub, _positionId, _permission);
    _;
  }
}
DCAHubCompanionLibrariesHandler.sol 34 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.22;

import '../libraries/InputBuilding.sol';
import '../libraries/SecondsUntilNextSwap.sol';
import '../interfaces/IDCAHubCompanion.sol';

abstract contract DCAHubCompanionLibrariesHandler is IDCAHubCompanionLibrariesHandler {
  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function getNextSwapInfo(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability,
    bytes calldata _oracleData
  ) external view returns (IDCAHub.SwapInfo memory) {
    (address[] memory _tokens, IDCAHub.PairIndexes[] memory _indexes) = InputBuilding.buildGetNextSwapInfoInput(_pairs);
    return _hub.getNextSwapInfo(_tokens, _indexes, _calculatePrivilegedAvailability, _oracleData);
  }

  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function legacyGetNextSwapInfo(ILegacyDCAHub _hub, Pair[] calldata _pairs) external view returns (ILegacyDCAHub.SwapInfo memory) {
    (address[] memory _tokens, IDCAHub.PairIndexes[] memory _indexes) = InputBuilding.buildGetNextSwapInfoInput(_pairs);
    return _hub.getNextSwapInfo(_tokens, _indexes);
  }

  /// @inheritdoc IDCAHubCompanionLibrariesHandler
  function secondsUntilNextSwap(
    IDCAHub _hub,
    Pair[] calldata _pairs,
    bool _calculatePrivilegedAvailability
  ) external view returns (uint256[] memory) {
    return SecondsUntilNextSwap.secondsUntilNextSwap(_hub, _pairs, _calculatePrivilegedAvailability);
  }
}
IERC721.sol 135 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../../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 address zero.
     *
     * 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);
}
SafeERC20.sol 118 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

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

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

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

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

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

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

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

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

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

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

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}
IERC165.sol 25 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

Read Contract

PERMIT2 0x6afdd850 → address
PROTOCOL_TOKEN 0x585cc6a5 → address
allowanceTarget 0x910cab11 → address
getNextSwapInfo 0x23760bb1 → tuple
governor 0x0c340a24 → address
isGovernor 0xe43581b8 → bool
isPendingGovernor 0xdf08aed5 → bool
legacyGetNextSwapInfo 0x40424466 → tuple
pendingGovernor 0xe3056a34 → address
secondsUntilNextSwap 0x3142cadf → uint256[]
supportsInterface 0x01ffc9a7 → bool
swapper 0x2b3297f9 → address

Write Contract 22 functions

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

acceptPendingGovernor 0x13f6986d
No parameters
batchPermitTakeFromCaller 0xd390039a
tuple[] _tokens
uint256 _nonce
uint256 _deadline
bytes _signature
address _recipient
deposit 0x0d474cbf
address _hub
address _from
address _to
uint256 _amount
uint32 _amountOfSwaps
uint32 _swapInterval
address _owner
tuple[] _permissions
bytes _miscellaneous
returns: uint256
depositWithBalanceOnContract 0x9e200e21
address _hub
address _from
address _to
uint32 _amountOfSwaps
uint32 _swapInterval
address _owner
tuple[] _permissions
bytes _miscellaneous
returns: uint256
increasePosition 0x05ce20d6
address _hub
uint256 _positionId
uint256 _amount
uint32 _newSwaps
increasePositionWithBalanceOnContract 0x40c5710c
address _hub
uint256 _positionId
uint32 _newSwaps
multiPermissionPermit 0xc995a762
address _permissionManager
tuple[] _permissions
uint256 _deadline
uint8 _v
bytes32 _r
bytes32 _s
multicall 0xac9650d8
bytes[] _data
returns: bytes[]
permissionPermit 0x8736624e
address _permissionManager
tuple[] _permissions
uint256 _tokenId
uint256 _deadline
uint8 _v
bytes32 _r
bytes32 _s
permitTakeFromCaller 0xd9a673ef
address _token
uint256 _amount
uint256 _nonce
uint256 _deadline
bytes _signature
address _recipient
reducePosition 0xdb8266de
address _hub
uint256 _positionId
uint256 _amount
uint32 _newSwaps
address _recipient
runSwap 0xa669a7f6
address _allowanceToken
uint256 _value
bytes _swapData
address _tokenOut
returns: uint256
sendBalanceOnContractToRecipient 0x340b532f
address _token
address _recipient
sendToRecipient 0x3a79d674
address _token
uint256 _amount
address _recipient
setPendingGovernor 0xf235757f
address _pendingGovernor
setSwapper 0xeffca705
address _newSwapper
address _newAllowanceTarget
simulate 0x3ed242b4
bytes[] _calls
returns: tuple[]
simulateAndRevert 0xbcbef206
bytes _call
takeFromCaller 0x37b0c09d
address _token
uint256 _amount
address _recipient
terminate 0x5f963dcf
address _hub
uint256 _positionId
address _recipientUnswapped
address _recipientSwapped
returns: uint256, uint256
withdrawSwapped 0x1f66925c
address _hub
uint256 _positionId
address _recipient
returns: uint256
withdrawSwappedMany 0xffdeb00d
address _hub
tuple[] _positions
address _recipient
returns: uint256[]

Recent Transactions

No transactions found for this address