Address: 0xA9e9c94A7D54694FDB44739aD93D493e17a16d17
Balance (XRP): 0 XRP
Bytecode: 0x608060405234801561001057600080fd5b50600436106102925760003560e01c806358827d7a11610160578063b4398244116100d8578063e89dec6d1161008c578063ef260ec711610071578063ef260ec714610522578063f0860e241461057e578063f22eac5a1461059157600080fd5b8063e89dec6d14610506578063eef4ee971461051957600080fd5b8063c5af31c6116100bd578063c5af31c6146104cd578063d8fafb80146104e0578063ddc2d272146104f357600080fd5b8063b4398244146104b1578063b91512c4146104ba57600080fd5b80638ad452f41161012f5780639342c8f4116101145780639342c8f414610482578063a3ffa96814610495578063b0682f051461049e57600080fd5b80638ad452f414610466578063922f21231461046f57600080fd5b806358827d7a1461042e57806366666aa9146104375780637ca39a1b1461044057806381fbfb8b1461045357600080fd5b8063421adfa01161020e5780634f30d73d116101c2578063529dd5ea116101a7578063529dd5ea14610409578063538e30c41461041257806357ded9c91461042557600080fd5b80634f30d73d146103e357806351e189c7146103f657600080fd5b80634899c325116101f35780634899c325146103c95780634c942b75146103d25780634e71d92d146103db57600080fd5b8063421adfa01461035b57806348750f55146103c057600080fd5b80632ab50eb8116102655780632e1a7d4d1161024a5780632e1a7d4d1461032c57806332cf1c4b1461033f57806337e9f64a1461035257600080fd5b80632ab50eb8146103025780632cc138be1461031557600080fd5b8063129874aa14610297578063130c84c5146102ac5780632300b6b4146102bf57806327c7e10e146102ef575b600080fd5b6102aa6102a5366004612f37565b61059a565b005b6102aa6102ba366004612f71565b610bec565b6003546102d2906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6102aa6102fd366004612f95565b610d28565b6102aa610310366004612f71565b610e02565b61031e600a5481565b6040519081526020016102e6565b6102aa61033a366004612f95565b610f47565b6102aa61034d366004612f95565b61144c565b61031e600b5481565b610398610369366004612f71565b601060205260009081526040902080546001820154600283015460038401546004909401549293919290919085565b604080519586526020860194909452928401919091526060830152608082015260a0016102e6565b61031e60045481565b61031e60095481565b61031e60085481565b6102aa6115f8565b6015546102d2906001600160a01b031681565b6102aa610404366004612f95565b611819565b61031e60065481565b600c546102d2906001600160a01b031681565b61031e60075481565b61031e60115481565b61031e600e5481565b6000546102d2906001600160a01b031681565b6002546102d2906001600160a01b031681565b61031e60135481565b6102aa61047d366004612f95565b611a49565b6102aa610490366004612f95565b611b56565b61031e600f5481565b6102aa6104ac366004612f37565b611cfb565b61031e60145481565b6102aa6104c8366004612f95565b611e2a565b6012546102d2906001600160a01b031681565b6102aa6104ee366004612f95565b611ef9565b6001546102d2906001600160a01b031681565b600d546102d2906001600160a01b031681565b61031e60165481565b610535610530366004612f71565b611f98565b6040805184518152602080860151908201528482015191810191909152606080850151908201526080938401519381019390935260a083019190915260c082015260e0016102e6565b6102aa61058c366004612f95565b612117565b61031e60055481565b6012546001600160a01b031633148015906105c05750600c546001600160a01b03163314155b6106375760405162461bcd60e51b815260206004820152603660248201527f43616d706169676e3a2041646d696e732063616e2774207573652066756e637460448201527f696f6e7320666f72206e6f726d616c2075736572732e0000000000000000000060648201526084015b60405180910390fd5b81801515806106465750600082115b6106b85760405162461bcd60e51b815260206004820152602260248201527f43616d706169676e3a204e6f20616d6f756e7420746f2070617274696369706160448201527f7465000000000000000000000000000000000000000000000000000000000000606482015260840161062e565b821561073b576001546040516323b872dd60e01b8152336004820152306024820152604481018590526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610715573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107399190612fae565b505b81156107d8576000546040516323b872dd60e01b8152336004820152306024820152604481018490526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610798573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107bc9190612fae565b5060006107c8836121b6565b90506107d48183612fe6565b9150505b600254600480546040517ff94d46680000000000000000000000000000000000000000000000000000000081529182015260609182916001600160a01b039091169063f94d466890602401600060405180830381865afa158015610840573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261086891908101906130d5565b50809250819350505060008160055481518110610887576108876131a3565b602002602001015182600654815181106108a3576108a36131a3565b60200260200101516127106108b891906131b9565b6108c291906131d0565b6001546015546040516370a0823160e01b81526001600160a01b0391821660048201529293506000929116906370a0823190602401602060405180830381865afa158015610914573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061093891906131f2565b6000546015546040516370a0823160e01b81526001600160a01b0391821660048201529116906370a0823190602401602060405180830381865afa158015610984573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109a891906131f2565b6109b4906127106131b9565b6109be91906131d0565b905081620186a0601654836109d391906131b9565b6109dd91906131d0565b6109e79083612fe6565b1080610a17575081620186a060165483610a0191906131b9565b610a0b91906131d0565b610a15908361320b565b115b15610a8a5760405162461bcd60e51b815260206004820152602860248201527f43616d706169676e3a2053706f74207072696365206973206e6f7420696e207460448201527f68652072616e6765000000000000000000000000000000000000000000000000606482015260840161062e565b60008360055481518110610aa057610aa06131a3565b60200260200101518460065481518110610abc57610abc6131a3565b602002602001015187610acf91906131b9565b610ad991906131d0565b9050806013541015610b535760405162461bcd60e51b815260206004820152602d60248201527f43616d706169676e3a204e6f7420656e6f75676820737570706f72746564205260448201527f4f4f54206c697175696469747900000000000000000000000000000000000000606482015260840161062e565b6000610b5f8288612329565b90508160136000828254610b73919061320b565b90915550610b8c9050610b876002836131d0565b612627565b601354604080518b8152602081018b905290810189905260608101849052608081019190915233907fe141f5f2329ad97522d2ba49d8f2d828ddc836bdb51752e1705ff28fa5654f859060a00160405180910390a2505050505050505050565b6012546001600160a01b03163314610c575760405162461bcd60e51b815260206004820152602860248201527f43616d706169676e3a204f6e6c7920726f6f744c697175696469747941646d696044820152676e2063616e20646f60c01b606482015260840161062e565b6001600160a01b03811660009081526010602052604090205415610ccf5760405162461bcd60e51b815260206004820152602960248201527f43616d706169676e3a204e65772061646d696e206d757374206e6f7420686176604482015268329030903330b9369760b91b606482015260840161062e565b6012805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b03831690811790915560405133907fff4f9b4b4f2f150b1ba83e9019181f4226b79ca4a78675f450df96dc395a7adc90600090a350565b80600e6000828254610d3a9190612fe6565b9091555050600d546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015610d96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dba9190612fae565b50600e5460405133917f4fc3291c5fdd84d3320598b37c4f43d9e202e63bbe876ce52a59b5fe0d3b468c91610df791858252602082015260400190565b60405180910390a250565b600c546001600160a01b03163314610e665760405162461bcd60e51b815260206004820152602160248201527f43616d706169676e3a204f6e6c792072657761726441646d696e2063616e20646044820152606f60f81b606482015260840161062e565b6001600160a01b03811660009081526010602052604090205415610ede5760405162461bcd60e51b815260206004820152602960248201527f43616d706169676e3a204e65772061646d696e206d757374206e6f7420686176604482015268329030903330b9369760b91b606482015260840161062e565b600c805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383169081179091556040805133815260208101929092527fe7e6a6cf18608ac05d4e554e20e710ddfb4c05723b09a320d4569a1d8924249791015b60405180910390a150565b6012546001600160a01b03163314801590610f6d5750600c546001600160a01b03163314155b610fdf5760405162461bcd60e51b815260206004820152603660248201527f43616d706169676e3a2041646d696e732063616e2774207573652066756e637460448201527f696f6e7320666f72206e6f726d616c2075736572732e00000000000000000000606482015260840161062e565b6000610fea8261284a565b600080546040516370a0823160e01b815230600482015292935090916001600160a01b03909116906370a0823190602401602060405180830381865afa158015611038573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061105c91906131f2565b6001546040516370a0823160e01b81523060048201529192506000916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156110aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ce91906131f2565b90506110e36110dd8580612fe6565b30612add565b600080546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801561112c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115091906131f2565b6001546040516370a0823160e01b81523060048201529192506000916001600160a01b03909116906370a0823190602401602060405180830381865afa15801561119e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c291906131f2565b6001549091506001600160a01b031663a9059cbb336111e1868561320b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303816000875af1158015611244573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112689190612fae565b506000620f42408661127a878661320b565b61128491906131b9565b61128e91906131d0565b905060008161129d878661320b565b6112a7919061320b565b905080601360008282546112bb9190612fe6565b909155505081156113d3576003546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561130f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133391906131f2565b9050611340836000612329565b506003546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa15801561138a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ae91906131f2565b90506113ba828261320b565b601460008282546113cb9190612fe6565b909155505050505b337f194c8d0132d20112211dfa71bb87a92766fde4f4318e08efd2cc4a6e188e509d89611400888761320b565b61140a8a8961320b565b6014546013546040805195865260208601949094529284019190915260608301869052608083015260a082015260c00160405180910390a25050505050505050565b6012546001600160a01b031633146114b75760405162461bcd60e51b815260206004820152602860248201527f43616d706169676e3a204f6e6c7920726f6f744c697175696469747941646d696044820152676e2063616e20646f60c01b606482015260840161062e565b80601354101561152f5760405162461bcd60e51b815260206004820152603560248201527f43616d706169676e3a204e6f7420656e6f75676820737570706f72746564206c60448201527f697175696469747920746f2074616b65206261636b0000000000000000000000606482015260840161062e565b60005460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af1158015611580573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a49190612fae565b5080601360008282546115b7919061320b565b909155505060135460405133917f0bd184f7ddefaa06d15b877f08712c2c417daf79c03d2e27ef9b2d598214f6cd91610df791858252602082015260400190565b6012546001600160a01b0316331480159061161e5750600c546001600160a01b03163314155b6116905760405162461bcd60e51b815260206004820152603660248201527f43616d706169676e3a2041646d696e732063616e2774207573652066756e637460448201527f696f6e7320666f72206e6f726d616c2075736572732e00000000000000000000606482015260840161062e565b600061169a612cc9565b9050600081116116ec5760405162461bcd60e51b815260206004820152601d60248201527f43616d706169676e3a204e6f207265776172647320746f20636c61696d000000604482015260640161062e565b600080546040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611735573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061175991906131f2565b90506117688260065433612cf2565b600080546040516370a0823160e01b81523360048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156117b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d591906131f2565b9050337f47cee97cb7acd717b3c0aa1435d004cd5b3c8c57d70dbceb4e4458bbd60e39d4611803848461320b565b60405190815260200160405180910390a2505050565b6012546001600160a01b031633146118845760405162461bcd60e51b815260206004820152602860248201527f43616d706169676e3a204f6e6c7920726f6f744c697175696469747941646d696044820152676e2063616e20646f60c01b606482015260840161062e565b601154600b546118949190612fe6565b42116119085760405162461bcd60e51b815260206004820152602c60248201527f43616d706169676e3a204e6f742061626c6520746f207769746864726177206c60448201527f6971756964697479207965740000000000000000000000000000000000000000606482015260840161062e565b8060145410156119805760405162461bcd60e51b815260206004820152603160248201527f43616d706169676e3a204e6f7420656e6f756768206c6f636b6564206c69717560448201527f696469747920746f207769746864726177000000000000000000000000000000606482015260840161062e565b8060146000828254611992919061320b565b909155505060035460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af11580156119e8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a0c9190612fae565b5060145460405133917fe5415affb4ffe99860d0494beccdc939c69b8e3e46fb8a1ba2924171e3d226ec91610df791858252602082015260400190565b600c546001600160a01b03163314611aad5760405162461bcd60e51b815260206004820152602160248201527f43616d706169676e3a204f6e6c792072657761726441646d696e2063616e20646044820152606f60f81b606482015260840161062e565b60008111611afd5760405162461bcd60e51b815260206004820152601f60248201527f5265776172644661726d3a206170722073686f756c64206e6f74206265203000604482015260640161062e565b60075481600f54611b0e91906131b9565b611b1891906131d0565b600f55600781905560408051828152602081018390527fdaeec2f286b1dbb9f17b60282bb0606d5bb175c6dc719cbb273b5585a988c58d9101610f3c565b600c546001600160a01b03163314611bba5760405162461bcd60e51b815260206004820152602160248201527f43616d706169676e3a204f6e6c792072657761726441646d696e2063616e20646044820152606f60f81b606482015260840161062e565b80600e541015611c325760405162461bcd60e51b815260206004820152602c60248201527f43616d706169676e3a204e6f7420656e6f7567682072657761726420706f6f6c60448201527f20746f2077697468647261770000000000000000000000000000000000000000606482015260840161062e565b80600e6000828254611c44919061320b565b9091555050600d5460405163a9059cbb60e01b8152336004820152602481018390526001600160a01b039091169063a9059cbb906044016020604051808303816000875af1158015611c9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cbe9190612fae565b50600e5460405133917f5211fd942ffe1469ee31f69ade1fce9c198026c15223b5d413da09b389f815bb91610df791858252602082015260400190565b600c546001600160a01b03163314611d5f5760405162461bcd60e51b815260206004820152602160248201527f43616d706169676e3a204f6e6c792072657761726441646d696e2063616e20646044820152606f60f81b606482015260840161062e565b808210611dd45760405162461bcd60e51b815260206004820152603b60248201527f43616d706169676e3a206e65772073746172742074696d652073686f756c642060448201527f62652065616c696572207468616e206e657720656e642074696d650000000000606482015260840161062e565b600a829055600b8190556040805183815260208101839052908101839052606081018290527f37834cf3dc97ff0eb4b71ed43625b5fca1dac2462dc1575567e44f234e01acaa9060800160405180910390a15050565b6000546040516323b872dd60e01b8152336004820152306024820152604481018390526001600160a01b03909116906323b872dd906064016020604051808303816000875af1158015611e81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ea59190612fae565b508060136000828254611eb89190612fe6565b909155505060135460405133917f65650ff3d1a7c58637a908c2a143eae5a6a5011a64d6ebc36d386b484cbff26591610df791858252602082015260400190565b600c546001600160a01b03163314611f5d5760405162461bcd60e51b815260206004820152602160248201527f43616d706169676e3a204f6e6c792072657761726441646d696e2063616e20646044820152606f60f81b606482015260840161062e565b600981905560408051828152602081018390527f8ab176f2e50064685cb903642dd6f3a1f3e4523dbac18b2cd26eedd5c08a12319101610f3c565b611fca6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b506001600160a01b038116600090815260106020908152604091829020825160a08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004909101546080820152600f54600e54600a544211801561203c5750600b548360600151105b156120f05760006301e13380600a5485606001511161205d57600a54612063565b84606001515b600b54421061207457600b54612076565b425b612080919061320b565b6007548651620f424091612093916131b9565b61209d91906131d0565b6120a791906131b9565b6120b191906131d0565b905080846040018181516120c59190612fe6565b905250600f546120d690829061320b565b925080600e546120e6919061320b565b4260608601529150505b6009546080840151612102904261320b565b111561211057825160208401525b9193909250565b600c546001600160a01b0316331461217b5760405162461bcd60e51b815260206004820152602160248201527f43616d706169676e3a204f6e6c792072657761726441646d696e2063616e20646044820152606f60f81b606482015260840161062e565b600881905560408051828152602081018390527f42b8d30fd8705bab65aa0d8fdd49e095d288d97ef5823b582066faa7f7be63d89101610f3c565b6000806040518060c001604052806004548152602001600060018111156121df576121df61321e565b8152600080546001600160a01b039081166020840152600154166040830152606082018690526080909101906040519080825280601f01601f191660200182016040528015612235576020820181803683370190505b50905260408051608081018252308082526000602083018190529282015260608101829052600254929350916001600160a01b0316906352bbbe2990849084906122824262015180612fe6565b6040518563ffffffff1660e01b81526004016122a1949392919061327a565b6020604051808303816000875af11580156122c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e491906131f2565b604080518681526020810183905291945033917ff3f50fa855b1527faf70ea3175249c9553a972b7b865f628210a92d54d57b58b910160405180910390a25050919050565b60408051600280825260608201835260009283929190602083019080368337505060005460065483519394506001600160a01b03909116928492508110612372576123726131a3565b6001600160a01b03928316602091820292909201015260015460055483519190921691839181106123a5576123a56131a3565b6001600160a01b039290921660209283029190910182015260408051600280825260608201835260009391929091830190803683370190505090508481600654815181106123f5576123f56131a3565b602002602001018181525050838160055481518110612416576124166131a3565b6020026020010181815250506000600182600060405160200161243b93929190613379565b60408051808303601f190181526080830182528583526020830185905282820181905260006060840181905260035492516370a0823160e01b8152306004820152919450916001600160a01b0316906370a0823190602401602060405180830381865afa1580156124b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124d491906131f2565b600254600480546040517fb95cac280000000000000000000000000000000000000000000000000000000081529394506001600160a01b039092169263b95cac28926125289290913091829189910161344c565b600060405180830381600087803b15801561254257600080fd5b505af1158015612556573d6000803e3d6000fd5b50506003546040516370a0823160e01b8152306004820152600093506001600160a01b0390911691506370a0823190602401602060405180830381865afa1580156125a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125c991906131f2565b90506125d5828261320b565b604080518a8152602081018c905290810182905290975033907f6e6688d904f1f86e8a2198740b1d760a63ca385eba87f3e91482cc80a6159eae9060600160405180910390a250505050505092915050565b600a54421015801561263a5750600b5442105b6126ac5760405162461bcd60e51b815260206004820152602660248201527f43616d706169676e3a204e6f742073746172746564206f7220616c726561647960448201527f20656e6465640000000000000000000000000000000000000000000000000000606482015260840161062e565b336000818152601060205260409020906126c590612ee1565b60018101548154036126dc57426004820155612727565b80546126e9908390612fe6565b828260040154426126fa919061320b565b61270491906131b9565b61270e91906131d0565b8160040160008282546127219190612fe6565b90915550505b8181600001600082825461273b9190612fe6565b9091555050600b546301e133809061275490429061320b565b620f42406007548561276691906131b9565b61277091906131d0565b61277a91906131b9565b61278491906131d0565b600f60008282546127959190612fe6565b9091555050600f54600e5410156127ee5760405162461bcd60e51b815260206004820152601d60248201527f43616d706169676e3a204661726d696e67206361702069732066756c6c000000604482015260640161062e565b80546004820154600f5460405133937fd8c2f11574a5eecf390218f75754fcba994e8f07970429ff8ad5ecfa83c27b1a9361283e9388845260208401929092526040830152606082015260800190565b60405180910390a25050565b6000816000036128c25760405162461bcd60e51b815260206004820152602c60248201527f43616d706169676e3a20556e6661726d656420616d6f756e742073686f756c6460448201527f206e6f74206265207a65726f0000000000000000000000000000000000000000606482015260840161062e565b336000818152601060205260409020906128db90612ee1565b80548311156129525760405162461bcd60e51b815260206004820152603260248201527f43616d706169676e3a204e6f742061626c6520746f207769746864726177206d60448201527f6f7265207468616e206465706f73697465640000000000000000000000000000606482015260840161062e565b4260085482600401546129659190612fe6565b106129b25760405162461bcd60e51b815260206004820152601760248201527f43616d706169676e3a204c6f636b757020706572696f64000000000000000000604482015260640161062e565b8054600182015410156129e257805460018201546129d390620f42406131b9565b6129dd91906131d0565b6129e7565b620f42405b9150828160000160008282546129fd919061320b565b90915550508054620f424090612a149084906131b9565b612a1e91906131d0565b6001820155600b546301e1338090612a3790429061320b565b620f424060075486612a4991906131b9565b612a5391906131d0565b612a5d91906131b9565b612a6791906131d0565b600f6000828254612a78919061320b565b909155505080546001820154600f5460408051878152602081019490945283019190915260608201526080810183905233907f37052343ae983e9ad4892ebff3949071e8256148e7899d557ba119215e102fa89060a00160405180910390a250919050565b604080516002808252606082018352600092602083019080368337505060005460065483519394506001600160a01b03909116928492508110612b2257612b226131a3565b6001600160a01b0392831660209182029290920101526001546005548351919092169183918110612b5557612b556131a3565b6001600160a01b0392909216602092830291909101820152604080516002808252606082018352600093919290918301908036833701905050905060008160065481518110612ba657612ba66131a3565b60200260200101818152505060008160055481518110612bc857612bc86131a3565b6020026020010181815250506000600185604051602001612bea92919061349c565b60408051601f198184030181526080830182528583526020830185905282820181905260006060840152600254600480549351638bdb391360e01b81529295506001600160a01b0390911692638bdb391392612c4c9230918b9188910161344c565b600060405180830381600087803b158015612c6657600080fd5b505af1158015612c7a573d6000803e3d6000fd5b50505050846001600160a01b03167f93da91b64bcc6d667d7d60b09931224b2d49df395db28658ef58bcacc65925a587604051612cb991815260200190565b60405180910390a2505050505050565b3360008181526010602052604081209091612ce390612ee1565b60020180546000909155919050565b604080516002808252606082018352600092602083019080368337505060005460065483519394506001600160a01b03909116928492508110612d3757612d376131a3565b6001600160a01b0392831660209182029290920101526001546005548351919092169183918110612d6a57612d6a6131a3565b6001600160a01b0392909216602092830291909101820152604080516002808252606082018352600093919290918301908036833701905050905060008160065481518110612dbb57612dbb6131a3565b60200260200101818152505060008160055481518110612ddd57612ddd6131a3565b6020026020010181815250506000808686604051602001612e00939291906134b7565b60408051601f198184030181526080830182528583526020830185905282820181905260006060840152600254600480549351638bdb391360e01b81529295506001600160a01b0390911692638bdb391392612e629230918b9188910161344c565b600060405180830381600087803b158015612e7c57600080fd5b505af1158015612e90573d6000803e3d6000fd5b5050604080518a8152602081018a90526001600160a01b03891693507f606653469951f3ed083eee1887e34fd4d7a7028e379131cde985d913fcbc4ba192500160405180910390a250505050505050565b6001600160a01b0381166000908152601060205260408120908080612f0585611f98565b6020830151600188015560608301516003880155604090920151600290960195909555600f94909455505050600e5550565b60008060408385031215612f4a57600080fd5b50508035926020909101359150565b6001600160a01b0381168114612f6e57600080fd5b50565b600060208284031215612f8357600080fd5b8135612f8e81612f59565b9392505050565b600060208284031215612fa757600080fd5b5035919050565b600060208284031215612fc057600080fd5b81518015158114612f8e57600080fd5b634e487b7160e01b600052601160045260246000fd5b80820180821115612ff957612ff9612fd0565b92915050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561303e5761303e612fff565b604052919050565b600067ffffffffffffffff82111561306057613060612fff565b5060051b60200190565b600082601f83011261307b57600080fd5b8151602061309061308b83613046565b613015565b82815260059290921b840181019181810190868411156130af57600080fd5b8286015b848110156130ca57805183529183019183016130b3565b509695505050505050565b6000806000606084860312156130ea57600080fd5b835167ffffffffffffffff8082111561310257600080fd5b818601915086601f83011261311657600080fd5b8151602061312661308b83613046565b82815260059290921b8401810191818101908a84111561314557600080fd5b948201945b8386101561316c57855161315d81612f59565b8252948201949082019061314a565b9189015191975090935050508082111561318557600080fd5b506131928682870161306a565b925050604084015190509250925092565b634e487b7160e01b600052603260045260246000fd5b8082028115828204841417612ff957612ff9612fd0565b6000826131ed57634e487b7160e01b600052601260045260246000fd5b500490565b60006020828403121561320457600080fd5b5051919050565b81810381811115612ff957612ff9612fd0565b634e487b7160e01b600052602160045260246000fd5b6000815180845260005b8181101561325a5760208185018101518683018201520161323e565b506000602082860101526020601f19601f83011685010191505092915050565b60e08152845160e0820152600060208601516002811061329c5761329c61321e565b61010083015260408601516001600160a01b03908116610120840152606087015116610140830152608086015161016083015260a086015160c06101808401526132ea6101a0840182613234565b91505061332c60208301866001600160a01b03808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b60a082019390935260c0015292915050565b600081518084526020808501945080840160005b8381101561336e57815187529582019590820190600101613352565b509495945050505050565b60006004851061338b5761338b61321e565b848252606060208301526133a2606083018561333e565b905060ff83166040830152949350505050565b8051608080845281519084018190526000916020919082019060a0860190845b818110156133fa5783516001600160a01b0316835292840192918401916001016133d5565b505082850151915085810383870152613413818361333e565b925050506040830151848203604086015261342e8282613234565b9150506060830151613444606086018215159052565b509392505050565b84815260006001600160a01b0380861660208401528085166040840152506080606083015261347e60808301846133b5565b9695505050505050565b600381106134985761349861321e565b9052565b604081016134aa8285613488565b8260208301529392505050565b606081016134c58286613488565b60208201939093526040015291905056fea2646970667358221220908452a470abd145e1317174ce7303231825d5656dc9b95a706a15f56a7c567464736f6c63430008130033
Campaign.sol
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.19; import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol"; import "./MoaiUtils.sol"; import "./RewardFarm.sol"; contract Campaign is MoaiUtils, RewardFarm { // Configurations uint public liquiditySupportLockupPeriod = 2 * 365 days; // 2 years address public rootLiquidityAdmin; // Futureverse uint public liquiditySupport; uint public lockedLiquidity; address public nativeXrpRootLpTokenAddress; uint public spotPriceLimit = 5000; // 5% constructor( address rootTokenAddr_, address xrpTokenAddr_, address vaultAddress_, address nativeXrpRootLpTokenAddress_, address bptAddr_, bytes32 poolId_ ) { rewardAdmin = msg.sender; rootLiquidityAdmin = msg.sender; rootTokenAddr = rootTokenAddr_; xrpTokenAddr = xrpTokenAddr_; moaiVaultAddr = vaultAddress_; xrpRootBptAddr = bptAddr_; rewardTokenAddr = bptAddr_; moaiPoolId = poolId_; nativeXrpRootLpTokenAddress = nativeXrpRootLpTokenAddress_; IERC20[] memory poolTokens; uint[] memory poolTokenBalances; (poolTokens, poolTokenBalances, ) = IVault(moaiVaultAddr).getPoolTokens( moaiPoolId ); require( poolTokens.length == 2, "Campaign: The pool should be two tokens" ); require( poolTokenBalances[0] > 0 && poolTokenBalances[1] > 0, "Campaign: The pool should have liquidity" ); xrpIndex = (poolTokens[0] == IERC20(xrpTokenAddr)) ? 0 : 1; rootIndex = (xrpIndex == 0) ? 1 : 0; require( poolTokens[xrpIndex] == IERC20(xrpTokenAddr) && poolTokens[rootIndex] == IERC20(rootTokenAddr), "Campaign: The pool should be XRP-ROOT pool" ); IERC20(rootTokenAddr).approve(moaiVaultAddr, type(uint256).max); IERC20(xrpTokenAddr).approve(moaiVaultAddr, type(uint256).max); } modifier onlyRootLiquidityAdmin() { require( msg.sender == rootLiquidityAdmin, "Campaign: Only rootLiquidityAdmin can do" ); _; } modifier onlyNormalUser() { require( msg.sender != rootLiquidityAdmin && msg.sender != rewardAdmin, "Campaign: Admins can't use functions for normal users." ); _; } event Participate( address indexed participant, uint amountXrpIn, uint amountRootIn, uint amountXrpForJoin, uint amountPairedRootForJoin, uint remainedRootLiquidtySupport ); event Withdraw( address indexed sender, uint amountBPTRequested, uint amountXrpOut, uint amountRootInitialOut, uint freedRootAmount, uint lockedLiquidity, uint liquiditySupport ); event Claim(address indexed claimer, uint amountRoot); event SupportLiquidity( address indexed sender, uint amountRoot, uint liquiditySupport ); event TakebackLiquidity( address indexed sender, uint amountRoot, uint liquiditySupport ); event WithdrawLiquidityAsBPTAfterLockup( address indexed sender, uint amountBPT, uint lockedLiquidity ); event RootLiquidityAdminChanged( address indexed prevAdmin, address indexed newAdmin ); /* Campaign Participation Scenario 1. Users add liquidity through this part 2-A. If there are $ROOT, swap all into $XRP via Moai Finance 2-B. Provide $XRP-$ROOT liquidity and all the $ROOTs are from Futureverse Note) The half of LP tokens should belong to Futureverse 3. Farm users' LP tokens Campaign Part - interact with users and Moai Finance contracts - Not directly interact with farm variables */ function participate( uint amountXrpIn, uint amountRootIn ) external onlyNormalUser { uint amountXrp = amountXrpIn; require( amountXrpIn > 0 || amountRootIn > 0, "Campaign: No amount to participate" ); if (amountXrpIn > 0) { IERC20(xrpTokenAddr).transferFrom( msg.sender, address(this), amountXrpIn ); } if (amountRootIn > 0) { IERC20(rootTokenAddr).transferFrom( msg.sender, address(this), amountRootIn ); uint xrpOut = _swapRootToXrp(amountRootIn); amountXrp += xrpOut; } IERC20[] memory poolTokens; uint[] memory poolTokenBalances; (poolTokens, poolTokenBalances, ) = IVault(moaiVaultAddr).getPoolTokens( moaiPoolId ); uint moaiPoolSpotPrice = (1e4 * poolTokenBalances[rootIndex]) / (poolTokenBalances[xrpIndex]); uint nativePoolSpotPrice = (1e4 * IERC20(rootTokenAddr).balanceOf(nativeXrpRootLpTokenAddress)) / (IERC20(xrpTokenAddr).balanceOf(nativeXrpRootLpTokenAddress)); if ( nativePoolSpotPrice + ((nativePoolSpotPrice * spotPriceLimit) / 1e5) < moaiPoolSpotPrice || nativePoolSpotPrice - ((nativePoolSpotPrice * spotPriceLimit) / 1e5) > moaiPoolSpotPrice ) { revert("Campaign: Spot price is not in the range"); } uint pairedAmountRoot = (amountXrp * poolTokenBalances[rootIndex]) / (poolTokenBalances[xrpIndex]); require( liquiditySupport >= pairedAmountRoot, "Campaign: Not enough supported ROOT liquidity" ); uint amountBPT = _joinPool(pairedAmountRoot, amountXrp); liquiditySupport -= pairedAmountRoot; _farm(amountBPT / 2); emit Participate( msg.sender, amountXrpIn, amountRootIn, amountXrp, pairedAmountRoot, liquiditySupport ); } function withdraw(uint amount) external onlyNormalUser { uint toBeLockedRatio = _unfarm(amount); uint beforeRootAmount = IERC20(rootTokenAddr).balanceOf(address(this)); uint beforeXrpAmount = IERC20(xrpTokenAddr).balanceOf(address(this)); _exitPool(amount + amount, address(this)); uint afterRootAmount = IERC20(rootTokenAddr).balanceOf(address(this)); uint afterXrpAmount = IERC20(xrpTokenAddr).balanceOf(address(this)); // transfer xrp to user IERC20(xrpTokenAddr).transfer( msg.sender, afterXrpAmount - beforeXrpAmount ); uint toBeLockedRootAmount = ((afterRootAmount - beforeRootAmount) * toBeLockedRatio) / 1e6; uint freedRootAmount = (afterRootAmount - beforeRootAmount) - toBeLockedRootAmount; liquiditySupport += freedRootAmount; if (toBeLockedRootAmount > 0) { uint beforeBptAmount = IERC20(xrpRootBptAddr).balanceOf( address(this) ); _joinPool(toBeLockedRootAmount, 0); uint afterBptAmount = IERC20(xrpRootBptAddr).balanceOf( address(this) ); lockedLiquidity += (afterBptAmount - beforeBptAmount); } emit Withdraw( msg.sender, amount, afterXrpAmount - beforeXrpAmount, afterRootAmount - beforeRootAmount, freedRootAmount, lockedLiquidity, liquiditySupport ); } function claim() external onlyNormalUser { uint rewardAmount = _returnAndClearRewardAmount(); require(rewardAmount > 0, "Campaign: No rewards to claim"); uint beforeRootAmount = IERC20(rootTokenAddr).balanceOf(msg.sender); _exitPoolSingle(rewardAmount, rootIndex, msg.sender); uint afterRootAmount = IERC20(rootTokenAddr).balanceOf(msg.sender); emit Claim(msg.sender, afterRootAmount - beforeRootAmount); } // Support $ROOT liquidity function supportLiquidity(uint amount) external { IERC20(rootTokenAddr).transferFrom(msg.sender, address(this), amount); liquiditySupport += amount; emit SupportLiquidity(msg.sender, amount, liquiditySupport); } function changeRootLiquidityAdmin( address newAdmin ) external onlyRootLiquidityAdmin { require( farms[newAdmin].amountFarmed == 0, "Campaign: New admin must not have a farm." ); rootLiquidityAdmin = newAdmin; emit RootLiquidityAdminChanged(msg.sender, newAdmin); } function takebackSupport(uint amount) external onlyRootLiquidityAdmin { require( liquiditySupport >= amount, "Campaign: Not enough supported liquidity to take back" ); IERC20(rootTokenAddr).transfer(msg.sender, amount); liquiditySupport -= amount; emit TakebackLiquidity(msg.sender, amount, liquiditySupport); } function withdrawSupportAfterCampaign( uint amount ) external onlyRootLiquidityAdmin { require( block.timestamp > rewardEndTime + liquiditySupportLockupPeriod, "Campaign: Not able to withdraw liquidity yet" ); require( lockedLiquidity >= amount, "Campaign: Not enough locked liquidity to withdraw" ); lockedLiquidity -= amount; IERC20(xrpRootBptAddr).transfer(msg.sender, amount); emit WithdrawLiquidityAsBPTAfterLockup( msg.sender, amount, lockedLiquidity ); } }
MoaiUtils.sol
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.19; import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol"; import "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol"; import "@balancer-labs/v2-interfaces/contracts/pool-weighted/WeightedPoolUserData.sol"; contract MoaiUtils { address public rootTokenAddr; address public xrpTokenAddr; address public moaiVaultAddr; address public xrpRootBptAddr; bytes32 public moaiPoolId; uint public xrpIndex; uint public rootIndex; event SwapRootToXrp( address indexed sender, uint amountRootIn, uint amountXrpOut ); event JoinPool( address indexed sender, uint amountXrp, uint amountRoot, uint amountBPT ); event ExitPool(address indexed recipient, uint amountBPT); event ExitPoolSingle( address indexed recipient, uint amountBPT, uint exitAssetIndex ); function _swapRootToXrp(uint amountRootIn) internal returns (uint xrpOut) { IVault.SingleSwap memory singleSwap = IVault.SingleSwap({ poolId: moaiPoolId, kind: IVault.SwapKind.GIVEN_IN, assetIn: IAsset(rootTokenAddr), assetOut: IAsset(xrpTokenAddr), amount: amountRootIn, userData: new bytes(0) }); IVault.FundManagement memory funds = IVault.FundManagement({ sender: address(this), fromInternalBalance: false, recipient: payable(address(this)), toInternalBalance: false }); xrpOut = IVault(moaiVaultAddr).swap( singleSwap, funds, 0, block.timestamp + 1 days ); emit SwapRootToXrp(msg.sender, amountRootIn, xrpOut); } function _joinPool( uint amountRoot, uint amountXrp ) internal returns (uint joinedBPT) { IAsset[] memory joinAsset = new IAsset[](2); joinAsset[rootIndex] = IAsset(rootTokenAddr); joinAsset[xrpIndex] = IAsset(xrpTokenAddr); uint[] memory joinAmountsIn = new uint[](2); joinAmountsIn[rootIndex] = amountRoot; joinAmountsIn[xrpIndex] = amountXrp; bytes memory userData = abi.encode( WeightedPoolUserData.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, joinAmountsIn, 0 ); IVault.JoinPoolRequest memory request = IVault.JoinPoolRequest({ assets: joinAsset, maxAmountsIn: joinAmountsIn, userData: userData, fromInternalBalance: false }); uint amountBPTBeforeJoin = IERC20(xrpRootBptAddr).balanceOf( address(this) ); IVault(moaiVaultAddr).joinPool( moaiPoolId, address(this), address(this), request ); uint amountBPTAfterJoin = IERC20(xrpRootBptAddr).balanceOf( address(this) ); joinedBPT = amountBPTAfterJoin - amountBPTBeforeJoin; emit JoinPool(msg.sender, amountXrp, amountRoot, joinedBPT); } function _exitPool(uint exitBPTAmount, address recipient) internal { IAsset[] memory exitAsset = new IAsset[](2); exitAsset[rootIndex] = IAsset(rootTokenAddr); exitAsset[xrpIndex] = IAsset(xrpTokenAddr); uint[] memory exitAmountsOut = new uint[](2); exitAmountsOut[rootIndex] = 0; exitAmountsOut[xrpIndex] = 0; bytes memory userData = abi.encode( WeightedPoolUserData.ExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT, exitBPTAmount ); IVault.ExitPoolRequest memory request = IVault.ExitPoolRequest({ assets: exitAsset, minAmountsOut: exitAmountsOut, userData: userData, toInternalBalance: false }); IVault(moaiVaultAddr).exitPool( moaiPoolId, address(this), payable(recipient), request ); emit ExitPool(recipient, exitBPTAmount); } function _exitPoolSingle( uint exitBPTAmount, uint exitAssetIndex, address recipient ) internal { IAsset[] memory exitAsset = new IAsset[](2); exitAsset[rootIndex] = IAsset(rootTokenAddr); exitAsset[xrpIndex] = IAsset(xrpTokenAddr); uint[] memory exitAmountsOut = new uint[](2); exitAmountsOut[rootIndex] = 0; exitAmountsOut[xrpIndex] = 0; bytes memory userData = abi.encode( WeightedPoolUserData.ExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, exitBPTAmount, exitAssetIndex ); IVault.ExitPoolRequest memory request = IVault.ExitPoolRequest({ assets: exitAsset, minAmountsOut: exitAmountsOut, userData: userData, toInternalBalance: false }); IVault(moaiVaultAddr).exitPool( moaiPoolId, address(this), payable(recipient), request ); emit ExitPoolSingle(recipient, exitBPTAmount, exitAssetIndex); } }
RewardFarm.sol
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.19; import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol"; contract RewardFarm { // Configurations uint public apr = 100000; // 100% = 1000000, 1e6 // User can't withdraw its deposit before 'userLockupPeriod' has passed since its last deposit uint public userLockupPeriod = 24 hours; // If a deposit is locked up more than 'periodToLockupLPSupport', // the supported liquidity by Futureverse becomes locked up for 2 years // The locked up BPT isn't freed when the user withdraw from this campaign uint public periodToLockupLPSupport = 30 days; uint public rewardStartTime = type(uint256).max - 1; uint public rewardEndTime = type(uint256).max; address public rewardAdmin; // Moai Finance address public rewardTokenAddr; uint public rewardPool; uint public rewardToBePaid; struct Farm { uint amountFarmed; uint amountPairedBPTLocked; uint unclaimedRewards; uint lastRewardTime; uint depositedTime; } mapping(address => Farm) public farms; modifier onlyRewardAdmin() { require(msg.sender == rewardAdmin, "Campaign: Only rewardAdmin can do"); _; } event Farmed( address indexed sender, uint amountFarmedBPTIn, uint amountFarmedBPT, uint depositedTime, uint totalRewardToBePaid ); event UnFarmed( address indexed sender, uint amountFarmedBPTOut, uint amountFarmedBPT, uint amountPairedBPTLocked, uint totalRewardToBePaid, uint toBeLockedRatio ); event ProvideRewards( address indexed sender, uint amountBPTIn, uint rewardPool ); event WithdrawRewards( address indexed sender, uint amountBPTOut, uint rewardPool ); event AprChanged(uint prevApr, uint newApr); event UserLockupPeriodChanged( uint prevUserLockupPeriod, uint newUserLockupPeriod ); event RewardTimeChanged( uint prevRewardStartTime, uint prevRewardEndTime, uint newRewardStartTime, uint newRewardEndTime ); event PeriodToLockupLPSupportChanged( uint prevPeriodToLockupLPSupport, uint newPeriodToLockupLPSupport ); event RewardAdminChanged(address prevRewardAdmin, address newRewardAdmin); // Farm LP tokens for rewards function _farm(uint amount) internal { require( block.timestamp >= rewardStartTime && block.timestamp < rewardEndTime, "Campaign: Not started or already ended" ); Farm storage farm = farms[msg.sender]; _accrue(msg.sender); if (farm.amountFarmed == farm.amountPairedBPTLocked) { farm.depositedTime = block.timestamp; } else { // If there is a farmed amount whose paired LP support is not locked up, // its new depositTime is an internally dividing point in inversely proportional to deposited amounts farm.depositedTime += ((block.timestamp - farm.depositedTime) * amount) / (farm.amountFarmed + amount); } farm.amountFarmed += amount; rewardToBePaid += (((amount * apr) / 1e6) * (rewardEndTime - block.timestamp)) / 365 days; require(rewardPool >= rewardToBePaid, "Campaign: Farming cap is full"); emit Farmed( msg.sender, amount, farm.amountFarmed, farm.depositedTime, rewardToBePaid ); } // Campaign part should lock 'toBeLockedRatio' of BPT as BPT and remain $ROOT is freed function _unfarm(uint amount) internal returns (uint toBeLockedRatio) { require(amount != 0, "Campaign: Unfarmed amount should not be zero"); Farm storage farm = farms[msg.sender]; _accrue(msg.sender); require( farm.amountFarmed >= amount, "Campaign: Not able to withdraw more than deposited" ); require( farm.depositedTime + userLockupPeriod < block.timestamp, "Campaign: Lockup period" ); toBeLockedRatio = farm.amountPairedBPTLocked >= farm.amountFarmed ? 1e6 : (1e6 * farm.amountPairedBPTLocked) / farm.amountFarmed; farm.amountFarmed -= amount; farm.amountPairedBPTLocked = (farm.amountFarmed * toBeLockedRatio) / 1e6; rewardToBePaid -= (((amount * apr) / 1e6) * (rewardEndTime - block.timestamp)) / 365 days; emit UnFarmed( msg.sender, amount, farm.amountFarmed, farm.amountPairedBPTLocked, rewardToBePaid, toBeLockedRatio ); } function _returnAndClearRewardAmount() internal returns (uint amount) { Farm storage farm = farms[msg.sender]; _accrue(msg.sender); amount = farm.unclaimedRewards; farm.unclaimedRewards = 0; } function _accrue(address account) internal { Farm storage farm = farms[account]; ( Farm memory farmSimulated, uint rewardToBePaidSimulated, uint rewardPoolSimulated ) = simulateAccrue(account); farm.amountPairedBPTLocked = farmSimulated.amountPairedBPTLocked; farm.lastRewardTime = farmSimulated.lastRewardTime; farm.unclaimedRewards = farmSimulated.unclaimedRewards; rewardToBePaid = rewardToBePaidSimulated; rewardPool = rewardPoolSimulated; } function simulateAccrue( address account ) public view returns ( Farm memory farmSimulated, uint rewardToBePaidSimulated, uint rewardPoolSimulated ) { farmSimulated = farms[account]; rewardToBePaidSimulated = rewardToBePaid; rewardPoolSimulated = rewardPool; if ( block.timestamp > rewardStartTime && farmSimulated.lastRewardTime < rewardEndTime ) { uint reward = (((farmSimulated.amountFarmed * apr) / 1e6) * (( block.timestamp < rewardEndTime ? block.timestamp : rewardEndTime ) - ( farmSimulated.lastRewardTime > rewardStartTime ? farmSimulated.lastRewardTime : rewardStartTime ))) / 365 days; farmSimulated.unclaimedRewards += reward; rewardToBePaidSimulated = rewardToBePaid - reward; rewardPoolSimulated = rewardPool - reward; farmSimulated.lastRewardTime = block.timestamp; } if ( block.timestamp - farmSimulated.depositedTime > periodToLockupLPSupport ) { farmSimulated.amountPairedBPTLocked = farmSimulated.amountFarmed; } } // Provide farm reward with $XRP-$ROOT BPT function provideRewards(uint amount) external { rewardPool += amount; IERC20(rewardTokenAddr).transferFrom(msg.sender, address(this), amount); emit ProvideRewards(msg.sender, amount, rewardPool); } // Note) some farmers can't receive rewards after reward withdrawal. function withdrawRewards(uint amount) external onlyRewardAdmin { require( rewardPool >= amount, "Campaign: Not enough reward pool to withdraw" ); rewardPool -= amount; IERC20(rewardTokenAddr).transfer(msg.sender, amount); emit WithdrawRewards(msg.sender, amount, rewardPool); } function changeApr(uint newApr) external onlyRewardAdmin { require(newApr > 0, "RewardFarm: apr should not be 0"); rewardToBePaid = (rewardToBePaid * newApr) / apr; apr = newApr; emit AprChanged(apr, newApr); } function changeUserLockupPeriod( uint newLockupPeriod ) external onlyRewardAdmin { userLockupPeriod = newLockupPeriod; emit UserLockupPeriodChanged(userLockupPeriod, newLockupPeriod); } function changeRewardTime( uint newStartTime, uint newEndTime ) external onlyRewardAdmin { require( newStartTime < newEndTime, "Campaign: new start time should be ealier than new end time" ); rewardStartTime = newStartTime; rewardEndTime = newEndTime; emit RewardTimeChanged( rewardStartTime, rewardEndTime, newStartTime, newEndTime ); } function changePeriodToLockupLPSupport( uint newPeriodToLockupLPSupport ) external onlyRewardAdmin { periodToLockupLPSupport = newPeriodToLockupLPSupport; emit PeriodToLockupLPSupportChanged( periodToLockupLPSupport, newPeriodToLockupLPSupport ); } function changeRewardAdmin(address newAdmin) external onlyRewardAdmin { require( farms[newAdmin].amountFarmed == 0, "Campaign: New admin must not have a farm." ); rewardAdmin = newAdmin; emit RewardAdminChanged(msg.sender, newAdmin); } }
IAsset.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; /** * @dev This is an empty interface used to represent either ERC20-conforming token contracts or ETH (using the zero * address sentinel value). We're just relying on the fact that `interface` can be used to declare new address-like * types. * * This concept is unrelated to a Pool's Asset Managers. */ interface IAsset { // solhint-disable-previous-line no-empty-blocks }
IVault.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma experimental ABIEncoderV2; import "../solidity-utils/openzeppelin/IERC20.sol"; import "../solidity-utils/helpers/IAuthentication.sol"; import "../solidity-utils/helpers/ISignaturesValidator.sol"; import "../solidity-utils/helpers/ITemporarilyPausable.sol"; import "../solidity-utils/misc/IWETH.sol"; import "./IAsset.sol"; import "./IAuthorizer.sol"; import "./IFlashLoanRecipient.sol"; import "./IProtocolFeesCollector.sol"; pragma solidity >=0.7.0 <0.9.0; /** * @dev Full external interface for the Vault core contract - no external or public methods exist in the contract that * don't override one of these declarations. */ interface IVault is ISignaturesValidator, ITemporarilyPausable, IAuthentication { // Generalities about the Vault: // // - Whenever documentation refers to 'tokens', it strictly refers to ERC20-compliant token contracts. Tokens are // transferred out of the Vault by calling the `IERC20.transfer` function, and transferred in by calling // `IERC20.transferFrom`. In these cases, the sender must have previously allowed the Vault to use their tokens by // calling `IERC20.approve`. The only deviation from the ERC20 standard that is supported is functions not returning // a boolean value: in these scenarios, a non-reverting call is assumed to be successful. // // - All non-view functions in the Vault are non-reentrant: calling them while another one is mid-execution (e.g. // while execution control is transferred to a token contract during a swap) will result in a revert. View // functions can be called in a re-reentrant way, but doing so might cause them to return inconsistent results. // Contracts calling view functions in the Vault must make sure the Vault has not already been entered. // // - View functions revert if referring to either unregistered Pools, or unregistered tokens for registered Pools. // Authorizer // // Some system actions are permissioned, like setting and collecting protocol fees. This permissioning system exists // outside of the Vault in the Authorizer contract: the Vault simply calls the Authorizer to check if the caller // can perform a given action. /** * @dev Returns the Vault's Authorizer. */ function getAuthorizer() external view returns (IAuthorizer); /** * @dev Sets a new Authorizer for the Vault. The caller must be allowed by the current Authorizer to do this. * * Emits an `AuthorizerChanged` event. */ function setAuthorizer(IAuthorizer newAuthorizer) external; /** * @dev Emitted when a new authorizer is set by `setAuthorizer`. */ event AuthorizerChanged(IAuthorizer indexed newAuthorizer); // Relayers // // Additionally, it is possible for an account to perform certain actions on behalf of another one, using their // Vault ERC20 allowance and Internal Balance. These accounts are said to be 'relayers' for these Vault functions, // and are expected to be smart contracts with sound authentication mechanisms. For an account to be able to wield // this power, two things must occur: // - The Authorizer must grant the account the permission to be a relayer for the relevant Vault function. This // means that Balancer governance must approve each individual contract to act as a relayer for the intended // functions. // - Each user must approve the relayer to act on their behalf. // This double protection means users cannot be tricked into approving malicious relayers (because they will not // have been allowed by the Authorizer via governance), nor can malicious relayers approved by a compromised // Authorizer or governance drain user funds, since they would also need to be approved by each individual user. /** * @dev Returns true if `user` has approved `relayer` to act as a relayer for them. */ function hasApprovedRelayer(address user, address relayer) external view returns (bool); /** * @dev Allows `relayer` to act as a relayer for `sender` if `approved` is true, and disallows it otherwise. * * Emits a `RelayerApprovalChanged` event. */ function setRelayerApproval( address sender, address relayer, bool approved ) external; /** * @dev Emitted every time a relayer is approved or disapproved by `setRelayerApproval`. */ event RelayerApprovalChanged(address indexed relayer, address indexed sender, bool approved); // Internal Balance // // Users can deposit tokens into the Vault, where they are allocated to their Internal Balance, and later // transferred or withdrawn. It can also be used as a source of tokens when joining Pools, as a destination // when exiting them, and as either when performing swaps. This usage of Internal Balance results in greatly reduced // gas costs when compared to relying on plain ERC20 transfers, leading to large savings for frequent users. // // Internal Balance management features batching, which means a single contract call can be used to perform multiple // operations of different kinds, with different senders and recipients, at once. /** * @dev Returns `user`'s Internal Balance for a set of tokens. */ function getInternalBalance(address user, IERC20[] memory tokens) external view returns (uint256[] memory); /** * @dev Performs a set of user balance operations, which involve Internal Balance (deposit, withdraw or transfer) * and plain ERC20 transfers using the Vault's allowance. This last feature is particularly useful for relayers, as * it lets integrators reuse a user's Vault allowance. * * For each operation, if the caller is not `sender`, it must be an authorized relayer for them. */ function manageUserBalance(UserBalanceOp[] memory ops) external payable; /** * @dev Data for `manageUserBalance` operations, which include the possibility for ETH to be sent and received without manual WETH wrapping or unwrapping. */ struct UserBalanceOp { UserBalanceOpKind kind; IAsset asset; uint256 amount; address sender; address payable recipient; } // There are four possible operations in `manageUserBalance`: // // - DEPOSIT_INTERNAL // Increases the Internal Balance of the `recipient` account by transferring tokens from the corresponding // `sender`. The sender must have allowed the Vault to use their tokens via `IERC20.approve()`. // // ETH can be used by passing the ETH sentinel value as the asset and forwarding ETH in the call: it will be wrapped // and deposited as WETH. Any ETH amount remaining will be sent back to the caller (not the sender, which is // relevant for relayers). // // Emits an `InternalBalanceChanged` event. // // // - WITHDRAW_INTERNAL // Decreases the Internal Balance of the `sender` account by transferring tokens to the `recipient`. // // ETH can be used by passing the ETH sentinel value as the asset. This will deduct WETH instead, unwrap it and send // it to the recipient as ETH. // // Emits an `InternalBalanceChanged` event. // // // - TRANSFER_INTERNAL // Transfers tokens from the Internal Balance of the `sender` account to the Internal Balance of `recipient`. // // Reverts if the ETH sentinel value is passed. // // Emits an `InternalBalanceChanged` event. // // // - TRANSFER_EXTERNAL // Transfers tokens from `sender` to `recipient`, using the Vault's ERC20 allowance. This is typically used by // relayers, as it lets them reuse a user's Vault allowance. // // Reverts if the ETH sentinel value is passed. // // Emits an `ExternalBalanceTransfer` event. enum UserBalanceOpKind { DEPOSIT_INTERNAL, WITHDRAW_INTERNAL, TRANSFER_INTERNAL, TRANSFER_EXTERNAL } /** * @dev Emitted when a user's Internal Balance changes, either from calls to `manageUserBalance`, or through * interacting with Pools using Internal Balance. * * Because Internal Balance works exclusively with ERC20 tokens, ETH deposits and withdrawals will use the WETH * address. */ event InternalBalanceChanged(address indexed user, IERC20 indexed token, int256 delta); /** * @dev Emitted when a user's Vault ERC20 allowance is used by the Vault to transfer tokens to an external account. */ event ExternalBalanceTransfer(IERC20 indexed token, address indexed sender, address recipient, uint256 amount); // Pools // // There are three specialization settings for Pools, which allow for cheaper swaps at the cost of reduced // functionality: // // - General: no specialization, suited for all Pools. IGeneralPool is used for swap request callbacks, passing the // balance of all tokens in the Pool. These Pools have the largest swap costs (because of the extra storage reads), // which increase with the number of registered tokens. // // - Minimal Swap Info: IMinimalSwapInfoPool is used instead of IGeneralPool, which saves gas by only passing the // balance of the two tokens involved in the swap. This is suitable for some pricing algorithms, like the weighted // constant product one popularized by Balancer V1. Swap costs are smaller compared to general Pools, and are // independent of the number of registered tokens. // // - Two Token: only allows two tokens to be registered. This achieves the lowest possible swap gas cost. Like // minimal swap info Pools, these are called via IMinimalSwapInfoPool. enum PoolSpecialization { GENERAL, MINIMAL_SWAP_INFO, TWO_TOKEN } /** * @dev Registers the caller account as a Pool with a given specialization setting. Returns the Pool's ID, which * is used in all Pool-related functions. Pools cannot be deregistered, nor can the Pool's specialization be * changed. * * The caller is expected to be a smart contract that implements either `IGeneralPool` or `IMinimalSwapInfoPool`, * depending on the chosen specialization setting. This contract is known as the Pool's contract. * * Note that the same contract may register itself as multiple Pools with unique Pool IDs, or in other words, * multiple Pools may share the same contract. * * Emits a `PoolRegistered` event. */ function registerPool(PoolSpecialization specialization) external returns (bytes32); /** * @dev Emitted when a Pool is registered by calling `registerPool`. */ event PoolRegistered(bytes32 indexed poolId, address indexed poolAddress, PoolSpecialization specialization); /** * @dev Returns a Pool's contract address and specialization setting. */ function getPool(bytes32 poolId) external view returns (address, PoolSpecialization); /** * @dev Registers `tokens` for the `poolId` Pool. Must be called by the Pool's contract. * * Pools can only interact with tokens they have registered. Users join a Pool by transferring registered tokens, * exit by receiving registered tokens, and can only swap registered tokens. * * Each token can only be registered once. For Pools with the Two Token specialization, `tokens` must have a length * of two, that is, both tokens must be registered in the same `registerTokens` call, and they must be sorted in * ascending order. * * The `tokens` and `assetManagers` arrays must have the same length, and each entry in these indicates the Asset * Manager for the corresponding token. Asset Managers can manage a Pool's tokens via `managePoolBalance`, * depositing and withdrawing them directly, and can even set their balance to arbitrary amounts. They are therefore * expected to be highly secured smart contracts with sound design principles, and the decision to register an * Asset Manager should not be made lightly. * * Pools can choose not to assign an Asset Manager to a given token by passing in the zero address. Once an Asset * Manager is set, it cannot be changed except by deregistering the associated token and registering again with a * different Asset Manager. * * Emits a `TokensRegistered` event. */ function registerTokens( bytes32 poolId, IERC20[] memory tokens, address[] memory assetManagers ) external; /** * @dev Emitted when a Pool registers tokens by calling `registerTokens`. */ event TokensRegistered(bytes32 indexed poolId, IERC20[] tokens, address[] assetManagers); /** * @dev Deregisters `tokens` for the `poolId` Pool. Must be called by the Pool's contract. * * Only registered tokens (via `registerTokens`) can be deregistered. Additionally, they must have zero total * balance. For Pools with the Two Token specialization, `tokens` must have a length of two, that is, both tokens * must be deregistered in the same `deregisterTokens` call. * * A deregistered token can be re-registered later on, possibly with a different Asset Manager. * * Emits a `TokensDeregistered` event. */ function deregisterTokens(bytes32 poolId, IERC20[] memory tokens) external; /** * @dev Emitted when a Pool deregisters tokens by calling `deregisterTokens`. */ event TokensDeregistered(bytes32 indexed poolId, IERC20[] tokens); /** * @dev Returns detailed information for a Pool's registered token. * * `cash` is the number of tokens the Vault currently holds for the Pool. `managed` is the number of tokens * withdrawn and held outside the Vault by the Pool's token Asset Manager. The Pool's total balance for `token` * equals the sum of `cash` and `managed`. * * Internally, `cash` and `managed` are stored using 112 bits. No action can ever cause a Pool's token `cash`, * `managed` or `total` balance to be greater than 2^112 - 1. * * `lastChangeBlock` is the number of the block in which `token`'s total balance was last modified (via either a * join, exit, swap, or Asset Manager update). This value is useful to avoid so-called 'sandwich attacks', for * example when developing price oracles. A change of zero (e.g. caused by a swap with amount zero) is considered a * change for this purpose, and will update `lastChangeBlock`. * * `assetManager` is the Pool's token Asset Manager. */ function getPoolTokenInfo(bytes32 poolId, IERC20 token) external view returns ( uint256 cash, uint256 managed, uint256 lastChangeBlock, address assetManager ); /** * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of * the tokens' `balances` changed. * * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order. * * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same * order as passed to `registerTokens`. * * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo` * instead. */ function getPoolTokens(bytes32 poolId) external view returns ( IERC20[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock ); /** * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized * Pool shares. * * If the caller is not `sender`, it must be an authorized relayer for them. * * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces * these maximums. * * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent * back to the caller (not the sender, which is important for relayers). * * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final * `assets` array might not be sorted. Pools with no registered tokens cannot be joined. * * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be * withdrawn from Internal Balance: attempting to do so will trigger a revert. * * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement * their own custom logic. This typically requires additional information from the user (such as the expected number * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed * directly to the Pool's contract, as is `recipient`. * * Emits a `PoolBalanceChanged` event. */ function joinPool( bytes32 poolId, address sender, address recipient, JoinPoolRequest memory request ) external payable; struct JoinPoolRequest { IAsset[] assets; uint256[] maxAmountsIn; bytes userData; bool fromInternalBalance; } /** * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see * `getPoolTokenInfo`). * * If the caller is not `sender`, it must be an authorized relayer for them. * * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault: * it just enforces these minimums. * * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit. * * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited. * * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise, * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to * do so will trigger a revert. * * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the * `tokens` array. This array must match the Pool's registered tokens. * * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement * their own custom logic. This typically requires additional information from the user (such as the expected number * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and * passed directly to the Pool's contract. * * Emits a `PoolBalanceChanged` event. */ function exitPool( bytes32 poolId, address sender, address payable recipient, ExitPoolRequest memory request ) external; struct ExitPoolRequest { IAsset[] assets; uint256[] minAmountsOut; bytes userData; bool toInternalBalance; } /** * @dev Emitted when a user joins or exits a Pool by calling `joinPool` or `exitPool`, respectively. */ event PoolBalanceChanged( bytes32 indexed poolId, address indexed liquidityProvider, IERC20[] tokens, int256[] deltas, uint256[] protocolFeeAmounts ); enum PoolBalanceChangeKind { JOIN, EXIT } // Swaps // // Users can swap tokens with Pools by calling the `swap` and `batchSwap` functions. To do this, // they need not trust Pool contracts in any way: all security checks are made by the Vault. They must however be // aware of the Pools' pricing algorithms in order to estimate the prices Pools will quote. // // The `swap` function executes a single swap, while `batchSwap` can perform multiple swaps in sequence. // In each individual swap, tokens of one kind are sent from the sender to the Pool (this is the 'token in'), // and tokens of another kind are sent from the Pool to the recipient in exchange (this is the 'token out'). // More complex swaps, such as one token in to multiple tokens out can be achieved by batching together // individual swaps. // // There are two swap kinds: // - 'given in' swaps, where the amount of tokens in (sent to the Pool) is known, and the Pool determines (via the // `onSwap` hook) the amount of tokens out (to send to the recipient). // - 'given out' swaps, where the amount of tokens out (received from the Pool) is known, and the Pool determines // (via the `onSwap` hook) the amount of tokens in (to receive from the sender). // // Additionally, it is possible to chain swaps using a placeholder input amount, which the Vault replaces with // the calculated output of the previous swap. If the previous swap was 'given in', this will be the calculated // tokenOut amount. If the previous swap was 'given out', it will use the calculated tokenIn amount. These extended // swaps are known as 'multihop' swaps, since they 'hop' through a number of intermediate tokens before arriving at // the final intended token. // // In all cases, tokens are only transferred in and out of the Vault (or withdrawn from and deposited into Internal // Balance) after all individual swaps have been completed, and the net token balance change computed. This makes // certain swap patterns, such as multihops, or swaps that interact with the same token pair in multiple Pools, cost // much less gas than they would otherwise. // // It also means that under certain conditions it is possible to perform arbitrage by swapping with multiple // Pools in a way that results in net token movement out of the Vault (profit), with no tokens being sent in (only // updating the Pool's internal accounting). // // To protect users from front-running or the market changing rapidly, they supply a list of 'limits' for each token // involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the // minimum amount of tokens to receive (by passing a negative value) is specified. // // Additionally, a 'deadline' timestamp can also be provided, forcing the swap to fail if it occurs after // this point in time (e.g. if the transaction failed to be included in a block promptly). // // If interacting with Pools that hold WETH, it is possible to both send and receive ETH directly: the Vault will do // the wrapping and unwrapping. To enable this mechanism, the IAsset sentinel value (the zero address) must be // passed in the `assets` array instead of the WETH address. Note that it is possible to combine ETH and WETH in the // same swap. Any excess ETH will be sent back to the caller (not the sender, which is relevant for relayers). // // Finally, Internal Balance can be used when either sending or receiving tokens. enum SwapKind { GIVEN_IN, GIVEN_OUT } /** * @dev Performs a swap with a single Pool. * * If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens * taken from the Pool, which must be greater than or equal to `limit`. * * If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens * sent to the Pool, which must be less than or equal to `limit`. * * Internal Balance usage and the recipient are determined by the `funds` struct. * * Emits a `Swap` event. */ function swap( SingleSwap memory singleSwap, FundManagement memory funds, uint256 limit, uint256 deadline ) external payable returns (uint256); /** * @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on * the `kind` value. * * `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address). * Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault. * * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be * used to extend swap behavior. */ struct SingleSwap { bytes32 poolId; SwapKind kind; IAsset assetIn; IAsset assetOut; uint256 amount; bytes userData; } /** * @dev Performs a series of swaps with one or multiple Pools. In each individual swap, the caller determines either * the amount of tokens sent to or received from the Pool, depending on the `kind` value. * * Returns an array with the net Vault asset balance deltas. Positive amounts represent tokens (or ETH) sent to the * Vault, and negative amounts represent tokens (or ETH) sent by the Vault. Each delta corresponds to the asset at * the same index in the `assets` array. * * Swaps are executed sequentially, in the order specified by the `swaps` array. Each array element describes a * Pool, the token to be sent to this Pool, the token to receive from it, and an amount that is either `amountIn` or * `amountOut` depending on the swap kind. * * Multihop swaps can be executed by passing an `amount` value of zero for a swap. This will cause the amount in/out * of the previous swap to be used as the amount in for the current one. In a 'given in' swap, 'tokenIn' must equal * the previous swap's `tokenOut`. For a 'given out' swap, `tokenOut` must equal the previous swap's `tokenIn`. * * The `assets` array contains the addresses of all assets involved in the swaps. These are either token addresses, * or the IAsset sentinel value for ETH (the zero address). Each entry in the `swaps` array specifies tokens in and * out by referencing an index in `assets`. Note that Pools never interact with ETH directly: it will be wrapped to * or unwrapped from WETH by the Vault. * * Internal Balance usage, sender, and recipient are determined by the `funds` struct. The `limits` array specifies * the minimum or maximum amount of each token the vault is allowed to transfer. * * `batchSwap` can be used to make a single swap, like `swap` does, but doing so requires more gas than the * equivalent `swap` call. * * Emits `Swap` events. */ function batchSwap( SwapKind kind, BatchSwapStep[] memory swaps, IAsset[] memory assets, FundManagement memory funds, int256[] memory limits, uint256 deadline ) external payable returns (int256[] memory); /** * @dev Data for each individual swap executed by `batchSwap`. The asset in and out fields are indexes into the * `assets` array passed to that function, and ETH assets are converted to WETH. * * If `amount` is zero, the multihop mechanism is used to determine the actual amount based on the amount in/out * from the previous swap, depending on the swap kind. * * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be * used to extend swap behavior. */ struct BatchSwapStep { bytes32 poolId; uint256 assetInIndex; uint256 assetOutIndex; uint256 amount; bytes userData; } /** * @dev Emitted for each individual swap performed by `swap` or `batchSwap`. */ event Swap( bytes32 indexed poolId, IERC20 indexed tokenIn, IERC20 indexed tokenOut, uint256 amountIn, uint256 amountOut ); /** * @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the * `recipient` account. * * If the caller is not `sender`, it must be an authorized relayer for them. * * If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20 * transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender` * must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of * `joinPool`. * * If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of * transferred. This matches the behavior of `exitPool`. * * Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a * revert. */ struct FundManagement { address sender; bool fromInternalBalance; address payable recipient; bool toInternalBalance; } /** * @dev Simulates a call to `batchSwap`, returning an array of Vault asset deltas. Calls to `swap` cannot be * simulated directly, but an equivalent `batchSwap` call can and will yield the exact same result. * * Each element in the array corresponds to the asset at the same index, and indicates the number of tokens (or ETH) * the Vault would take from the sender (if positive) or send to the recipient (if negative). The arguments it * receives are the same that an equivalent `batchSwap` call would receive. * * Unlike `batchSwap`, this function performs no checks on the sender or recipient field in the `funds` struct. * This makes it suitable to be called by off-chain applications via eth_call without needing to hold tokens, * approve them for the Vault, or even know a user's address. * * Note that this function is not 'view' (due to implementation details): the client code must explicitly execute * eth_call instead of eth_sendTransaction. */ function queryBatchSwap( SwapKind kind, BatchSwapStep[] memory swaps, IAsset[] memory assets, FundManagement memory funds ) external returns (int256[] memory assetDeltas); // Flash Loans /** * @dev Performs a 'flash loan', sending tokens to `recipient`, executing the `receiveFlashLoan` hook on it, * and then reverting unless the tokens plus a proportional protocol fee have been returned. * * The `tokens` and `amounts` arrays must have the same length, and each entry in these indicates the loan amount * for each token contract. `tokens` must be sorted in ascending order. * * The 'userData' field is ignored by the Vault, and forwarded as-is to `recipient` as part of the * `receiveFlashLoan` call. * * Emits `FlashLoan` events. */ function flashLoan( IFlashLoanRecipient recipient, IERC20[] memory tokens, uint256[] memory amounts, bytes memory userData ) external; /** * @dev Emitted for each individual flash loan performed by `flashLoan`. */ event FlashLoan(IFlashLoanRecipient indexed recipient, IERC20 indexed token, uint256 amount, uint256 feeAmount); // Asset Management // // Each token registered for a Pool can be assigned an Asset Manager, which is able to freely withdraw the Pool's // tokens from the Vault, deposit them, or assign arbitrary values to its `managed` balance (see // `getPoolTokenInfo`). This makes them extremely powerful and dangerous. Even if an Asset Manager only directly // controls one of the tokens in a Pool, a malicious manager could set that token's balance to manipulate the // prices of the other tokens, and then drain the Pool with swaps. The risk of using Asset Managers is therefore // not constrained to the tokens they are managing, but extends to the entire Pool's holdings. // // However, a properly designed Asset Manager smart contract can be safely used for the Pool's benefit, // for example by lending unused tokens out for interest, or using them to participate in voting protocols. // // This concept is unrelated to the IAsset interface. /** * @dev Performs a set of Pool balance operations, which may be either withdrawals, deposits or updates. * * Pool Balance management features batching, which means a single contract call can be used to perform multiple * operations of different kinds, with different Pools and tokens, at once. * * For each operation, the caller must be registered as the Asset Manager for `token` in `poolId`. */ function managePoolBalance(PoolBalanceOp[] memory ops) external; struct PoolBalanceOp { PoolBalanceOpKind kind; bytes32 poolId; IERC20 token; uint256 amount; } /** * Withdrawals decrease the Pool's cash, but increase its managed balance, leaving the total balance unchanged. * * Deposits increase the Pool's cash, but decrease its managed balance, leaving the total balance unchanged. * * Updates don't affect the Pool's cash balance, but because the managed balance changes, it does alter the total. * The external amount can be either increased or decreased by this call (i.e., reporting a gain or a loss). */ enum PoolBalanceOpKind { WITHDRAW, DEPOSIT, UPDATE } /** * @dev Emitted when a Pool's token Asset Manager alters its balance via `managePoolBalance`. */ event PoolBalanceManaged( bytes32 indexed poolId, address indexed assetManager, IERC20 indexed token, int256 cashDelta, int256 managedDelta ); // Protocol Fees // // Some operations cause the Vault to collect tokens in the form of protocol fees, which can then be withdrawn by // permissioned accounts. // // There are two kinds of protocol fees: // // - flash loan fees: charged on all flash loans, as a percentage of the amounts lent. // // - swap fees: a percentage of the fees charged by Pools when performing swaps. For a number of reasons, including // swap gas costs and interface simplicity, protocol swap fees are not charged on each individual swap. Rather, // Pools are expected to keep track of how much they have charged in swap fees, and pay any outstanding debts to the // Vault when they are joined or exited. This prevents users from joining a Pool with unpaid debt, as well as // exiting a Pool in debt without first paying their share. /** * @dev Returns the current protocol fee module. */ function getProtocolFeesCollector() external view returns (IProtocolFeesCollector); /** * @dev Safety mechanism to pause most Vault operations in the event of an emergency - typically detection of an * error in some part of the system. * * The Vault can only be paused during an initial time period, after which pausing is forever disabled. * * While the contract is paused, the following features are disabled: * - depositing and transferring internal balance * - transferring external balance (using the Vault's allowance) * - swaps * - joining Pools * - Asset Manager interactions * * Internal Balance can still be withdrawn, and Pools exited. */ function setPaused(bool paused) external; /** * @dev Returns the Vault's WETH instance. */ function WETH() external view returns (IWETH); // solhint-disable-previous-line func-name-mixedcase }
IAuthorizer.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; interface IAuthorizer { /** * @dev Returns true if `account` can perform the action described by `actionId` in the contract `where`. */ function canPerform( bytes32 actionId, address account, address where ) external view returns (bool); }
IWETH.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; import "../openzeppelin/IERC20.sol"; /** * @dev Interface for WETH9. * See https://github.com/gnosis/canonical-weth/blob/0dd1ea3e295eef916d0c6223ec63141137d22d67/contracts/WETH9.sol */ interface IWETH is IERC20 { function deposit() external payable; function withdraw(uint256 amount) external; }
IFlashLoanRecipient.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; // Inspired by Aave Protocol's IFlashLoanReceiver. import "../solidity-utils/openzeppelin/IERC20.sol"; interface IFlashLoanRecipient { /** * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient. * * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the * Vault, or else the entire flash loan will revert. * * `userData` is the same value passed in the `IVault.flashLoan` call. */ function receiveFlashLoan( IERC20[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData ) external; }
IProtocolFeesCollector.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; pragma experimental ABIEncoderV2; import "../solidity-utils/openzeppelin/IERC20.sol"; import "./IVault.sol"; import "./IAuthorizer.sol"; interface IProtocolFeesCollector { event SwapFeePercentageChanged(uint256 newSwapFeePercentage); event FlashLoanFeePercentageChanged(uint256 newFlashLoanFeePercentage); function withdrawCollectedFees( IERC20[] calldata tokens, uint256[] calldata amounts, address recipient ) external; function setSwapFeePercentage(uint256 newSwapFeePercentage) external; function setFlashLoanFeePercentage(uint256 newFlashLoanFeePercentage) external; function getSwapFeePercentage() external view returns (uint256); function getFlashLoanFeePercentage() external view returns (uint256); function getCollectedFeeAmounts(IERC20[] memory tokens) external view returns (uint256[] memory feeAmounts); function getAuthorizer() external view returns (IAuthorizer); function vault() external view returns (IVault); }
WeightedPoolUserData.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; import "../solidity-utils/openzeppelin/IERC20.sol"; library WeightedPoolUserData { // In order to preserve backwards compatibility, make sure new join and exit kinds are added at the end of the enum. enum JoinKind { INIT, EXACT_TOKENS_IN_FOR_BPT_OUT, TOKEN_IN_FOR_EXACT_BPT_OUT, ALL_TOKENS_IN_FOR_EXACT_BPT_OUT } enum ExitKind { EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, EXACT_BPT_IN_FOR_TOKENS_OUT, BPT_IN_FOR_EXACT_TOKENS_OUT } function joinKind(bytes memory self) internal pure returns (JoinKind) { return abi.decode(self, (JoinKind)); } function exitKind(bytes memory self) internal pure returns (ExitKind) { return abi.decode(self, (ExitKind)); } // Joins function initialAmountsIn(bytes memory self) internal pure returns (uint256[] memory amountsIn) { (, amountsIn) = abi.decode(self, (JoinKind, uint256[])); } function exactTokensInForBptOut(bytes memory self) internal pure returns (uint256[] memory amountsIn, uint256 minBPTAmountOut) { (, amountsIn, minBPTAmountOut) = abi.decode(self, (JoinKind, uint256[], uint256)); } function tokenInForExactBptOut(bytes memory self) internal pure returns (uint256 bptAmountOut, uint256 tokenIndex) { (, bptAmountOut, tokenIndex) = abi.decode(self, (JoinKind, uint256, uint256)); } function allTokensInForExactBptOut(bytes memory self) internal pure returns (uint256 bptAmountOut) { (, bptAmountOut) = abi.decode(self, (JoinKind, uint256)); } // Exits function exactBptInForTokenOut(bytes memory self) internal pure returns (uint256 bptAmountIn, uint256 tokenIndex) { (, bptAmountIn, tokenIndex) = abi.decode(self, (ExitKind, uint256, uint256)); } function exactBptInForTokensOut(bytes memory self) internal pure returns (uint256 bptAmountIn) { (, bptAmountIn) = abi.decode(self, (ExitKind, uint256)); } function bptInForExactTokensOut(bytes memory self) internal pure returns (uint256[] memory amountsOut, uint256 maxBPTAmountIn) { (, amountsOut, maxBPTAmountIn) = abi.decode(self, (ExitKind, uint256[], uint256)); } }
IERC20.sol
// SPDX-License-Identifier: MIT pragma solidity >=0.7.0 <0.9.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @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); }
IAuthentication.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; interface IAuthentication { /** * @dev Returns the action identifier associated with the external function described by `selector`. */ function getActionId(bytes4 selector) external view returns (bytes32); }
ISignaturesValidator.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; /** * @dev Interface for the SignatureValidator helper, used to support meta-transactions. */ interface ISignaturesValidator { /** * @dev Returns the EIP712 domain separator. */ function getDomainSeparator() external view returns (bytes32); /** * @dev Returns the next nonce used by an address to sign messages. */ function getNextNonce(address user) external view returns (uint256); }
ITemporarilyPausable.sol
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity >=0.7.0 <0.9.0; /** * @dev Interface for the TemporarilyPausable helper. */ interface ITemporarilyPausable { /** * @dev Emitted every time the pause state changes by `_setPaused`. */ event PausedStateChanged(bool paused); /** * @dev Returns the current paused state. */ function getPausedState() external view returns ( bool paused, uint256 pauseWindowEndTime, uint256 bufferPeriodEndTime ); }
Gas Token: