Address Contract Verified
Address
0x4fA0C488F321A1D089f7E5f951fe8C43F2064709
Balance
0.000000002 ETH
Nonce
1
Code Size
10337 bytes
Creator
0x39E5351E...57BE at tx 0x1f5bf3dd...a3a14f
Indexed Transactions
0
Contract Bytecode
10337 bytes
0x6080604052600436106101695760003560e01c806359c57ee2116100d157806395fe863c1161008a578063d492714811610064578063d492714814610437578063d64d4ba514610457578063fb4daeec14610477578063ffa1ad741461048a57600080fd5b806395fe863c146103d2578063c1d8e08a146103e7578063d3b3ca771461041757600080fd5b806359c57ee2146103075780635daf206a146103275780636b55e99114610347578063753f94471461035a5780637da0a8771461037a57806388e3b76a146103b257600080fd5b80631da5f315116101235780631da5f3151461023f5780631ec82cb81461025f578063415a21871461027f578063435fd56d1461029f578063522f6815146102c7578063568f3254146102e757600080fd5b8062124e3d14610175578063078aa9641461019d57806311e8701c146101bd57806317f565d2146101df5780631945b784146101ff5780631c9af2e31461021f57600080fd5b3661017057005b600080fd5b34801561018157600080fd5b5061018a61049f565b6040519081526020015b60405180910390f35b3480156101a957600080fd5b5061018a6101b8366004611ca5565b61052f565b3480156101c957600080fd5b506101dd6101d8366004611d03565b610546565b005b3480156101eb57600080fd5b5061018a6101fa366004611d68565b6105de565b34801561020b57600080fd5b5061018a61021a366004611dc3565b610659565b34801561022b57600080fd5b506101dd61023a366004611e37565b6106e9565b34801561024b57600080fd5b5061018a61025a366004611ed1565b610762565b34801561026b57600080fd5b506101dd61027a366004611f7a565b610949565b34801561028b57600080fd5b506101dd61029a366004611d03565b61098f565b6102b26102ad366004611fbb565b610a07565b60408051928352602083019190915201610194565b3480156102d357600080fd5b506101dd6102e2366004612040565b610d34565b3480156102f357600080fd5b506101dd61030236600461206c565b610dd5565b34801561031357600080fd5b5061018a610322366004612118565b610e3e565b34801561033357600080fd5b506102b261034236600461215b565b610f11565b6101dd610355366004611d03565b611115565b34801561036657600080fd5b5061018a610375366004612282565b611189565b34801561038657600080fd5b5060005461039a906001600160a01b031681565b6040516001600160a01b039091168152602001610194565b3480156103be57600080fd5b5061018a6103cd366004612357565b6111fe565b3480156103de57600080fd5b5061018a6112f3565b3480156103f357600080fd5b50610407610402366004611d03565b611354565b6040519015158152602001610194565b34801561042357600080fd5b5061018a6104323660046123b1565b6113c3565b34801561044357600080fd5b5061018a610452366004612412565b61145b565b34801561046357600080fd5b5061018a6104723660046124be565b611487565b61018a610485366004612535565b61158b565b34801561049657600080fd5b5061018a600981565b6000807f000000000000000000000000449d117117838ffa61263b61da6301aa2a88b13a6001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610500573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061052491906125a7565b509195945050505050565b600061053d838360016111fe565b90505b92915050565b60405163095ea7b360e01b81526001600160a01b037f000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156481166004830152600019602483015282169063095ea7b3906044016020604051808303816000875af11580156105b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105da91906125f7565b5050565b600061064e856001600160a01b0316635001f3b56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610621573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106459190612614565b868686866113c3565b90505b949350505050565b60006001600160a01b03821673b4272071ecadd69d933adcd19ca99fe80664fc0814806106a257506001600160a01b03821673b58e61c3098d85632df34eecfb899a1ed80921cb145b156106d3576106af6112f3565b836106b861049f565b6106c29190612647565b6106cc919061265e565b9050610540565b6106db6112f3565b6106c26305f5e10085612647565b60005b8381101561075a576107523386868481811061070a5761070a612680565b905060200201602081019061071f9190611d03565b85858581811061073157610731612680565b90506020020135896001600160a01b0316611724909392919063ffffffff16565b6001016106ec565b505050505050565b6000805487906001600160a01b031633148015906107895750336001600160a01b03821614155b156107ae576040516340b3b69360e11b81523360048201526024015b60405180910390fd5b6001600160a01b03891663d505accf89308960208801356107d560608a0160408b01612696565b6040516001600160e01b031960e088901b1681526001600160a01b0395861660048201529490931660248501526044840191909152606483015260ff166084820152606086013560a4820152608086013560c482015260e401600060405180830381600087803b15801561084857600080fd5b505af115801561085c573d6000803e3d6000fd5b505050833515905061092a5760006108798b8b8b308b8b8b611791565b905060008b6001600160a01b0316635001f3b56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108df9190612614565b90506108f66001600160a01b03821633873561181f565b610916896109058735856126b9565b6001600160a01b038416919061181f565b6109218535836126b9565b9350505061093c565b6109398a8a8a8a8a8a8a611791565b91505b5098975050505050505050565b6000546001600160a01b03163314610976576040516340b3b69360e11b81523360048201526024016107a5565b61098a6001600160a01b038416838361181f565b505050565b6000546001600160a01b031633146109bc576040516340b3b69360e11b81523360048201526024016107a5565b600080546001600160a01b0319166001600160a01b0383169081178255604051909133917f6b1afd5bf12dcac0f44e8afd235c6681297d3124624a7a7a0d34abd84b7986259190a350565b6000806000876001600160a01b0316635001f3b56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6e9190612614565b9050610a7988611354565b15610b8357610a888782610659565b9250876001600160a01b03166369365c528483338b8b8b6040518763ffffffff1660e01b8152600401610abf9594939291906126cc565b60206040518083038185885af1158015610add573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610b029190612720565b915034831015610b7e57600033610b1985346126b9565b604051600081818185875af1925050503d8060008114610b55576040519150601f19603f3d011682016040523d82523d6000602084013e610b5a565b606091505b5050905080610b7c5760405163f82a0ab760e01b815260040160405180910390fd5b505b610d29565b6040516370a0823160e01b81526001600160a01b038981166004830152600091908316906370a0823190602401602060405180830381865afa158015610bcd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf19190612720565b9050610bfe89898761158b565b6040516370a0823160e01b81526001600160a01b038b811660048301529195506000918416906370a0823190602401602060405180830381865afa158015610c4a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6e9190612720565b9050610c7a82826126b9565b8914610cad5788610c8b83836126b9565b604051630a720dfb60e01b8152600481019290925260248201526044016107a5565b60405163349b2e2960e11b81526001600160a01b038b16906369365c5290610ce190869033908e908e908e906004016126cc565b6020604051808303816000875af1158015610d00573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d249190612720565b935050505b509550959350505050565b6000546001600160a01b03163314610d61576040516340b3b69360e11b81523360048201526024016107a5565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114610dae576040519150601f19603f3d011682016040523d82523d6000602084013e610db3565b606091505b505090508061098a5760405163f82a0ab760e01b815260040160405180910390fd5b60005b85811015610e3457610e2b88888884818110610df657610df6612680565b9050602002016020810190610e0b9190611d03565b878785818110610e1d57610e1d612680565b9050602002013586866113c3565b50600101610dd8565b5050505050505050565b60006001600160a01b03841615801590610e5c5750610e5c84611354565b15610ece57610ec785856001600160a01b0316635001f3b56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ea3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061021a9190612614565b9050610651565b610ec78584848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506111fe915050565b6000806000896001600160a01b0316635001f3b56040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f789190612614565b6040516370a0823160e01b81526001600160a01b038c811660048301529192506000918316906370a0823190602401602060405180830381865afa158015610fc4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe89190612720565b9050610ff78a898b8a8f611487565b6040516370a0823160e01b81526001600160a01b038d811660048301529195506000918416906370a0823190602401602060405180830381865afa158015611043573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110679190612720565b905061107382826126b9565b8b14611084578a610c8b83836126b9565b6001600160a01b038c166369365c52843361109f86866126b9565b8b8b6040518663ffffffff1660e01b81526004016110c19594939291906126cc565b6020604051808303816000875af11580156110e0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111049190612720565b935050505097509795505050505050565b6000816001600160a01b03163460405160006040518083038185875af1925050503d8060008114611162576040519150601f19603f3d011682016040523d82523d6000602084013e611167565b606091505b50509050806105da5760405163f82a0ab760e01b815260040160405180910390fd5b6000805488906001600160a01b031633148015906111b05750336001600160a01b03821614155b156111d0576040516340b3b69360e11b81523360048201526024016107a5565b6111e08b8b8b308c8c8c8c610762565b60608501526111ef8484611850565b9b9a5050505050505050505050565b6000811561129e57604051632f80bb1d60e01b81526001600160a01b037f000000000000000000000000b27308f9f90d607463bb33ea1bebb41c27ce5ab61690632f80bb1d906112549086908890600401612789565b6020604051808303816000875af1158015611273573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112979190612720565b90506112ec565b60405163cdca175360e01b81526001600160a01b037f000000000000000000000000b27308f9f90d607463bb33ea1bebb41c27ce5ab6169063cdca1753906112549086908890600401612789565b9392505050565b6000807f0000000000000000000000005f4ec3df9cbd43714fe2740f5e3616155c5b84196001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a060405180830381865afa158015610500573d6000803e3d6000fd5b6000600480836001600160a01b031663e06174e46040518163ffffffff1660e01b8152600401602060405180830381865afa158015611397573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113bb9190612720565b161492915050565b60006113da6001600160a01b038716338787611724565b60405163349b2e2960e11b81526001600160a01b038616906369365c529061140e90899033908990899089906004016126cc565b6020604051808303816000875af115801561142d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114519190612720565b9695505050505050565b600061146c888833308a8a8a611791565b606084015261147b8383611850565b98975050505050505050565b60007f000000000000000000000000e592427a0aece92de3edee1f18e0157c058615646114bf6001600160a01b038616333089611724565b6040805160a0810182528581526001600160a01b0380861660208301524282840152606082018a9052608082018990529151631e51809360e31b8152909183169063f28c0498906115149084906004016127fc565b6020604051808303816000875af1158015611533573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115579190612720565b925086831015611580576115803361156f858a6126b9565b6001600160a01b038916919061181f565b505095945050505050565b6040805160a0810182528281526001600160a01b03808616602083015242828401526060820185905234608083018190529251631e51809360e31b81526000937f000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564939284169163f28c049891906116069085906004016127fc565b60206040518083038185885af1158015611624573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906116499190612720565b92503483101561171b57816001600160a01b03166312210e8a6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561168e57600080fd5b505af11580156116a2573d6000803e3d6000fd5b50600092503391506116b6905085346126b9565b604051600081818185875af1925050503d80600081146116f2576040519150601f19603f3d011682016040523d82523d6000602084013e6116f7565b606091505b50509050806117195760405163f82a0ab760e01b815260040160405180910390fd5b505b50509392505050565b6040516001600160a01b03848116602483015283811660448301526064820183905261178b9186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506119d2565b50505050565b60006117a86001600160a01b038816878a87611724565b60405163349b2e2960e11b81526001600160a01b038916906369365c52906117dc908a9089908990899089906004016126cc565b6020604051808303816000875af11580156117fb573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061147b9190612720565b6040516001600160a01b0383811660248301526044820183905261098a91859182169063a9059cbb90606401611759565b600081156119c9576020830180513090915261186b84611a35565b91507f000000000000000000000000b27308f9f90d607463bb33ea1bebb41c27ce5ab66001600160a01b0316634aa4a4fc6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ef9190612614565b6001600160a01b0316632e1a7d4d836040518263ffffffff1660e01b815260040161191c91815260200190565b600060405180830381600087803b15801561193657600080fd5b505af115801561194a573d6000803e3d6000fd5b505050506000816001600160a01b03168360405160006040518083038185875af1925050503d806000811461199b576040519150601f19603f3d011682016040523d82523d6000602084013e6119a0565b606091505b50509050806119c25760405163f82a0ab760e01b815260040160405180910390fd5b5050610540565b61053d83611a35565b60006119e76001600160a01b03841683611b03565b90508051600014158015611a0c575080806020019051810190611a0a91906125f7565b155b1561098a57604051635274afe760e01b81526001600160a01b03841660048201526024016107a5565b60405163c04b8d5960e01b81526000906001600160a01b037f000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564169063c04b8d5990611a849085906004016127fc565b6020604051808303816000875af1158015611aa3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ac79190612720565b90508160800151811015611afe576080820151604051630a720dfb60e01b81526004810191909152602481018290526044016107a5565b919050565b606061053d8383600060608251600014158015611b2857506001600160a01b0384163b155b15611b5157604051639eb1341360e01b81526001600160a01b03851660048201526024016107a5565b600080856001600160a01b03168486604051611b6d919061280f565b60006040518083038185875af1925050503d8060008114611baa576040519150601f19603f3d011682016040523d82523d6000602084013e611baf565b606091505b50915091508115611bc35791506112ec9050565b805115611bd257805160208201fd5b60405162461bcd60e51b815260206004820152600660248201526519985a5b195960d21b60448201526064016107a5565b634e487b7160e01b600052604160045260246000fd5b600082601f830112611c2a57600080fd5b81356001600160401b0380821115611c4457611c44611c03565b604051601f8301601f19908116603f01168101908282118183101715611c6c57611c6c611c03565b81604052838152866020858801011115611c8557600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060408385031215611cb857600080fd5b8235915060208301356001600160401b03811115611cd557600080fd5b611ce185828601611c19565b9150509250929050565b6001600160a01b0381168114611d0057600080fd5b50565b600060208284031215611d1557600080fd5b81356112ec81611ceb565b60008083601f840112611d3257600080fd5b5081356001600160401b03811115611d4957600080fd5b602083019150836020828501011115611d6157600080fd5b9250929050565b60008060008060608587031215611d7e57600080fd5b8435611d8981611ceb565b93506020850135925060408501356001600160401b03811115611dab57600080fd5b611db787828801611d20565b95989497509550505050565b60008060408385031215611dd657600080fd5b823591506020830135611de881611ceb565b809150509250929050565b60008083601f840112611e0557600080fd5b5081356001600160401b03811115611e1c57600080fd5b6020830191508360208260051b8501011115611d6157600080fd5b600080600080600060608688031215611e4f57600080fd5b8535611e5a81611ceb565b945060208601356001600160401b0380821115611e7657600080fd5b611e8289838a01611df3565b90965094506040880135915080821115611e9b57600080fd5b50611ea888828901611df3565b969995985093965092949392505050565b600060a08284031215611ecb57600080fd5b50919050565b600080600080600080600080610160898b031215611eee57600080fd5b8835611ef981611ceb565b97506020890135611f0981611ceb565b96506040890135611f1981611ceb565b95506060890135611f2981611ceb565b94506080890135935060a08901356001600160401b03811115611f4b57600080fd5b611f578b828c01611d20565b9094509250611f6b90508a60c08b01611eb9565b90509295985092959890939650565b600080600060608486031215611f8f57600080fd5b8335611f9a81611ceb565b92506020840135611faa81611ceb565b929592945050506040919091013590565b600080600080600060808688031215611fd357600080fd5b8535611fde81611ceb565b94506020860135935060408601356001600160401b038082111561200157600080fd5b61200d89838a01611d20565b9095509350606088013591508082111561202657600080fd5b5061203388828901611c19565b9150509295509295909350565b6000806040838503121561205357600080fd5b823561205e81611ceb565b946020939093013593505050565b60008060008060008060006080888a03121561208757600080fd5b873561209281611ceb565b965060208801356001600160401b03808211156120ae57600080fd5b6120ba8b838c01611df3565b909850965060408a01359150808211156120d357600080fd5b6120df8b838c01611df3565b909650945060608a01359150808211156120f857600080fd5b506121058a828b01611d20565b989b979a50959850939692959293505050565b6000806000806060858703121561212e57600080fd5b84359350602085013561214081611ceb565b925060408501356001600160401b03811115611dab57600080fd5b600080600080600080600060c0888a03121561217657600080fd5b873561218181611ceb565b965060208801359550604088013561219881611ceb565b94506060880135935060808801356001600160401b03808211156121bb57600080fd5b6121c78b838c01611c19565b945060a08a01359150808211156120f857600080fd5b600060a082840312156121ef57600080fd5b60405160a081016001600160401b03828210818311171561221257612212611c03565b81604052829350843591508082111561222a57600080fd5b5061223785828601611c19565b825250602083013561224881611ceb565b806020830152506040830135604082015260608301356060820152608083013560808201525092915050565b8015158114611d0057600080fd5b60008060008060008060008060006101808a8c0312156122a157600080fd5b89356122ac81611ceb565b985060208a01356122bc81611ceb565b975060408a01356122cc81611ceb565b965060608a0135955060808a01356001600160401b03808211156122ef57600080fd5b6122fb8d838e01611d20565b90975095508591506123108d60a08e01611eb9565b94506101408c013591508082111561232757600080fd5b506123348c828d016121dd565b9250506101608a013561234681612274565b809150509295985092959850929598565b60008060006060848603121561236c57600080fd5b8335925060208401356001600160401b0381111561238957600080fd5b61239586828701611c19565b92505060408401356123a681612274565b809150509250925092565b6000806000806000608086880312156123c957600080fd5b85356123d481611ceb565b945060208601356123e481611ceb565b93506040860135925060608601356001600160401b0381111561240657600080fd5b611ea888828901611d20565b600080600080600080600060c0888a03121561242d57600080fd5b873561243881611ceb565b9650602088013561244881611ceb565b95506040880135945060608801356001600160401b038082111561246b57600080fd5b6124778b838c01611d20565b909650945060808a013591508082111561249057600080fd5b5061249d8a828b016121dd565b92505060a08801356124ae81612274565b8091505092959891949750929550565b600080600080600060a086880312156124d657600080fd5b853594506020860135935060408601356124ef81611ceb565b925060608601356001600160401b0381111561250a57600080fd5b61251688828901611c19565b925050608086013561252781611ceb565b809150509295509295909350565b60008060006060848603121561254a57600080fd5b833561255581611ceb565b92506020840135915060408401356001600160401b0381111561257757600080fd5b61258386828701611c19565b9150509250925092565b805169ffffffffffffffffffff81168114611afe57600080fd5b600080600080600060a086880312156125bf57600080fd5b6125c88661258d565b94506020860151935060408601519250606086015191506125eb6080870161258d565b90509295509295909350565b60006020828403121561260957600080fd5b81516112ec81612274565b60006020828403121561262657600080fd5b81516112ec81611ceb565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761054057610540612631565b60008261267b57634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b6000602082840312156126a857600080fd5b813560ff811681146112ec57600080fd5b8181038181111561054057610540612631565b6001600160a01b038681168252851660208201526040810184905260806060820181905281018290526000828460a0840137600060a0848401015260a0601f19601f85011683010190509695505050505050565b60006020828403121561273257600080fd5b5051919050565b60005b8381101561275457818101518382015260200161273c565b50506000910152565b60008151808452612775816020860160208601612739565b601f01601f19169290920160200192915050565b60408152600061279c604083018561275d565b90508260208301529392505050565b6000815160a084526127c060a085018261275d565b6020848101516001600160a01b031690860152604080850151908601526060808501519086015260809384015193909401929092525090919050565b60208152600061053d60208301846127ab565b60008251612821818460208701612739565b919091019291505056fea26469706673582212200ab5567e117c74bef75a597d9c93f4b94adc519b838070110a61fb2923d9a29764736f6c63430008190033
Verified Source Code Full Match
Compiler: v0.8.25+commit.b61c2a91
EVM: paris
Optimization: Yes (200 runs)
IERC20.sol 92 lines
/**
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2016-2019 zOS Global Limited
*
*/
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
// Optional functions
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
/**
* @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.
*
* > 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);
}
Address.sol 74 lines
// SPDX-License-Identifier: MIT
// Copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol
// and modified it.
pragma solidity ^0.8.0;
library Address {
/// @param target Target address to call the function on.
error Address_NotTransferNorContract(address target);
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 weiValue) internal returns (bytes memory) {
if (data.length != 0 && !isContract(target)) {
revert Address_NotTransferNorContract(target);
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else if (returndata.length > 0) {
assembly{
revert (add (returndata, 0x20), mload (returndata))
}
} else {
revert("failed");
}
}
}
Ownable.sol 59 lines
// SPDX-License-Identifier: MIT
//
// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol
//
// Modifications:
// - Replaced Context._msgSender() with msg.sender
// - Made leaner
// - Extracted interface
pragma solidity ^0.8.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
error Ownable_NotOwner(address sender);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor (address initialOwner) {
owner = initialOwner;
emit OwnershipTransferred(address(0), owner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) external onlyOwner {
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
function _checkOwner() internal view {
if (msg.sender != owner) {
revert Ownable_NotOwner(msg.sender);
}
}
}
SafeERC20.sol 98 lines
// SPDX-License-Identifier: MIT
// coppied and adjusted from OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../ERC20/IERC20.sol";
import {IERC20Permit} from "../ERC20/IERC20Permit.sol";
import {Address} from "./Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
if (nonceAfter != nonceBefore + 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
IERC20Permit.sol 75 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
// Copied from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/draft-IERC20Permit.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit is IERC20 {
/*//////////////////////////////////////////////////////////////
Custom errors
//////////////////////////////////////////////////////////////*/
/// Block timestamp must to be before deadline.
/// @param deadline The deadline of the permit.
/// @param blockTimestamp The timestamp of the execution block.
error Permit_DeadlineExpired(uint256 deadline, uint256 blockTimestamp);
/// Recovered address must be owner and not zero address.
/// @param signerAddress The recovered signer address.
error Permit_InvalidSigner(address signerAddress);
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
IBrokerbot.sol 40 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../ERC20/IERC20.sol";
import "../ERC20/IERC20Permit.sol";
interface IBrokerbot {
/*//////////////////////////////////////////////////////////////
Custom errors
//////////////////////////////////////////////////////////////*/
error Brokerbot_BuyingDisabled();
error Brokerbot_SellingDisabled();
/// Sender(msg.sender) has to be incoming token or paymenthub.
/// @param sender The msg.sender.
error Brokerbot_InvalidSender(address sender);
/// target.call() wasn't successful.
/// @param target The receiver of the Eth.
/// @param amount The withdraw amount.
error Brokerbot_WithdrawFailed(address target, uint256 amount);
/// Sender(msg.sender) needs to be owner or paymenthub.
/// @param sender The msg.sender.
error Brokerbot_NotAuthorized(address sender);
function paymenthub() external view returns (address);
function base() external view returns (IERC20);
function token() external view returns (IERC20Permit);
function settings() external view returns (uint256);
// @return The amount of shares bought on buying or how much in the base currency is transfered on selling
function processIncoming(IERC20 token_, address from, uint256 amount, bytes calldata ref) external payable returns (uint256);
function getBuyPrice(uint256 shares) external view returns (uint256);
function getSellPrice(uint256 shares) external view returns (uint256);
}
IUniswapV3.sol 123 lines
// SPDX-License-Identifier: MIT
// Copied from
// https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/interfaces/IQuoter.sol
// https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/ISwapRouter.sol
pragma solidity ^0.8.0;
import "../ERC20/IERC20.sol";
interface IQuoter {
/// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
/// @param tokenIn The token being swapped in
/// @param tokenOut The token being swapped out
/// @param fee The fee of the token pool to consider for the pair
/// @param amountOut The desired output amount
/// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
function quoteExactOutputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountOut,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountIn);
/// @notice Returns the amount in required for a given exact output swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
/// @param amountOut The amount of the last token to receive
/// @return amountIn The amount of first token required to be paid
function quoteExactOutput(bytes memory path, uint256 amountOut) external returns (uint256 amountIn);
/// @notice Returns the amount out received for a given exact input swap without executing the swap
/// @param path The path of the swap, i.e. each token pair and the pool fee
/// @param amountIn The amount of the first token to swap
/// @return amountOut The amount of the last token that would be received
function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut);
/// @notice Returns the amount out received for a given exact input but for a swap of a single pool
/// @param tokenIn The token being swapped in
/// @param tokenOut The token being swapped out
/// @param fee The fee of the token pool to consider for the pair
/// @param amountIn The desired input amount
/// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
/// @return amountOut The amount of `tokenOut` that would be received
function quoteExactInputSingle(
address tokenIn,
address tokenOut,
uint24 fee,
uint256 amountIn,
uint160 sqrtPriceLimitX96
) external returns (uint256 amountOut);
// solhint-disable-next-line func-name-mixedcase
function WETH9() external view returns (address);
}
interface ISwapRouter {
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
function refundETH() external payable;
}
/// @title Interface for WETH9
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}
PaymentHub.sol 506 lines
/**
* SPDX-License-Identifier: LicenseRef-Aktionariat
*
* MIT License with Automated License Fee Payments
*
* Copyright (c) 2022 Aktionariat AG (aktionariat.com)
*
* Permission is hereby granted to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - All automated license fee payments integrated into this and related Software
* are preserved.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pragma solidity ^0.8.0;
import "../utils/Address.sol";
import "../ERC20/IERC20.sol";
import "../ERC20/IERC20Permit.sol";
import "./IUniswapV3.sol";
import "../utils/Ownable.sol";
import "./IBrokerbot.sol";
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "../utils/SafeERC20.sol";
/**
* A hub for payments. This allows tokens that do not support ERC 677 to enjoy similar functionality,
* namely interacting with a token-handling smart contract in one transaction, without having to set an allowance first.
* Instead, an allowance needs to be set only once, namely for this contract.
* Further, it supports automatic conversion from Ether to the payment currency through Uniswap or the reception of Ether
* using the current exchange rate as found in the chainlink oracle.
*/
contract PaymentHub {
using SafeERC20 for IERC20;
// Version history
// Version 4: added path to pay with any ecr20 via uniswap
// Version 5: added sell via permit
// Version 6: added transferEther function
// Version 7: added sell against eth and erc20, version, add permitinfo/swapinfo struct
// Version 8: use SafeERC20 for transfers
// Version 9: change payFromEther to include a swap path
uint256 public constant VERSION = 9;
uint256 private constant KEEP_ETHER = 0x4; // copied from brokerbot
uint256 private constant DENOMINATOR = 1e8;
address private constant XCHF_TOKEN = 0xB4272071eCAdd69d933AdcD19cA99fe80664fc08;
address private constant ZCHF_TOKEN = 0xB58E61C3098d85632Df34EecfB899A1Ed80921cB;
IQuoter private immutable uniswapQuoter;
ISwapRouter private immutable uniswapRouter;
AggregatorV3Interface internal immutable priceFeedCHFUSD;
AggregatorV3Interface internal immutable priceFeedETHUSD;
address public trustedForwarder;
struct PermitInfo {
uint256 exFee;
uint256 deadline;
uint8 v;
bytes32 r;
bytes32 s;
}
// event to when new forwarder is set
event ForwarderChanged(address indexed _oldForwarder, address indexed _newForwarder);
/*//////////////////////////////////////////////////////////////
Custom errors
//////////////////////////////////////////////////////////////*/
/// Failguard when an erc20 transfer returns false.
error PaymentHub_TransferFailed();
/// Sender not trusted.
/// @param sender The msg.sender of this transaction.
error PaymentHub_InvalidSender(address sender);
/// swap with less base token as required.
/// @param amountBase Required amount.
/// @param swappedAmount Swapped amount.
error PaymentHub_SwapError(uint256 amountBase, uint256 swappedAmount);
constructor(address _trustedForwarder, IQuoter _quoter, ISwapRouter swapRouter, AggregatorV3Interface _aggregatorCHFUSD, AggregatorV3Interface _aggregatorETHUSD) {
trustedForwarder = _trustedForwarder;
uniswapQuoter = _quoter;
uniswapRouter = swapRouter;
priceFeedCHFUSD = _aggregatorCHFUSD;
priceFeedETHUSD = _aggregatorETHUSD;
}
modifier onlySellerAndForwarder(address seller) {
if (msg.sender != trustedForwarder && msg.sender != seller) {
revert PaymentHub_InvalidSender(msg.sender);
}
_;
}
modifier onlyForwarder() {
if (msg.sender != trustedForwarder) {
revert PaymentHub_InvalidSender(msg.sender);
}
_;
}
/**
* @notice Change the trusted forwarder.
* @param newForwarder The new trusted forwarder.
*/
function changeForwarder(address newForwarder) external onlyForwarder {
trustedForwarder = newForwarder;
emit ForwarderChanged(msg.sender, newForwarder);
}
/**
* Get price in ERC20
* This is the method that the Brokerbot widget should use to quote the price to the user.
* @param amountInBase The amount of the base currency for the exact output.
* @param path The encoded path of the swap from erc20 to base.
* @return amount quoted to pay
*/
function getPriceInERC20(uint256 amountInBase, bytes memory path) public returns (uint256) {
return getPriceERC20(amountInBase, path, true);
}
/**
* @notice Get price for given amount and path swapped via uniswap.
* @param amount The exact amount which you want get out (exactOutput) or you put in (exactInput).
* @param path The path of the swap (inreverse order for exactOutput).
* @param exactOutput True if exactOutput should be used or false if exactInput should be used.
*/
function getPriceERC20(uint256 amount, bytes memory path, bool exactOutput) public returns (uint256) {
if (exactOutput) {
return uniswapQuoter.quoteExactOutput(
path,
amount
);
} else {
return uniswapQuoter.quoteExactInput(
path,
amount
);
}
}
/**
* Get price in Ether depding on brokerbot setting.
* If keep ETH is set price is from oracle.
* This is the method that the Brokerbot widget should use to quote the price to the user.
* @return The price in wei.
*/
function getPriceInEther(uint256 amountInBase, IBrokerbot brokerBot, bytes calldata path) public returns (uint256) {
if ((address(brokerBot) != address(0)) && hasSettingKeepEther(brokerBot)) {
return getPriceInEtherFromOracle(amountInBase, IBrokerbot(brokerBot).base());
} else {
return getPriceERC20(amountInBase, path, true);
}
}
/**
* Price in ETH with 18 decimals
*/
function getPriceInEtherFromOracle(uint256 amountInBase, IERC20 base) public view returns (uint256) {
if(address(base) == XCHF_TOKEN || address(base) == ZCHF_TOKEN) {
return getLatestPriceCHFUSD() * amountInBase / getLatestPriceETHUSD();
} else {
return amountInBase * DENOMINATOR / getLatestPriceETHUSD();
}
}
/**
* Returns the latest price of eth/usd pair from chainlink with 8 decimals
*/
function getLatestPriceETHUSD() public view returns (uint256) {
(, int256 price, , , ) = priceFeedETHUSD.latestRoundData();
return uint256(price);
}
/**
* Returns the latest price of chf/usd pair from chainlink with 8 decimals
*/
function getLatestPriceCHFUSD() public view returns (uint256) {
(, int256 price, , , ) = priceFeedCHFUSD.latestRoundData();
return uint256(price);
}
/**
* Convenience method to swap ether into base and pay a target address
*/
function payFromEther(address recipient, uint256 amountInBase, bytes memory path) public payable returns (uint256 amountIn) {
ISwapRouter swapRouter = uniswapRouter;
// The parameter path is encoded as (tokenOut, fee, tokenIn/tokenOut, fee, tokenIn)
ISwapRouter.ExactOutputParams memory params =
ISwapRouter.ExactOutputParams({
path: path,
recipient: recipient,
// solhint-disable-next-line not-rely-on-time
deadline: block.timestamp,
amountOut: amountInBase,
amountInMaximum: msg.value
});
// Executes the swap, returning the amountIn actually spent.
amountIn = swapRouter.exactOutput{value: msg.value}(params);
// For exact output swaps, the amountInMaximum may not have all been spent.
// If the actual amount spent (amountIn) is less than the specified maximum amount, we must refund the msg.sender and approve the swapRouter to spend 0.
if (amountIn < msg.value) {
swapRouter.refundETH();
(bool success, ) = msg.sender.call{value:msg.value - amountIn}(""); // return change
if (!success) {
revert PaymentHub_TransferFailed();
}
}
}
/// @dev The calling address must approve this contract to spend its ERC20 for this function to succeed. As the amount of input ERC20 is variable,
/// the calling address will need to approve for a slightly higher or infinit amount, anticipating some variance.
/// @param amountOut The desired amount of baseCurrency.
/// @param amountInMaximum The maximum amount of ERC20 willing to be swapped for the specified amountOut of baseCurrency.
/// @param erc20In The address of the erc20 token to pay with.
/// @param path The encoded path of the swap from erc20 to base.
/// @param recipient The reciving address - brokerbot.
/// @return amountIn The amountIn of ERC20 actually spent to receive the desired amountOut.
function payFromERC20(uint256 amountOut, uint256 amountInMaximum, address erc20In, bytes memory path, address recipient) public returns (uint256 amountIn) {
ISwapRouter swapRouter = uniswapRouter;
// Transfer the specified `amountInMaximum` to this contract.
IERC20(erc20In).safeTransferFrom(msg.sender, address(this), amountInMaximum);
// The parameter path is encoded as (tokenOut, fee, tokenIn/tokenOut, fee, tokenIn)
ISwapRouter.ExactOutputParams memory params =
ISwapRouter.ExactOutputParams({
path: path,
recipient: recipient,
// solhint-disable-next-line not-rely-on-time
deadline: block.timestamp,
amountOut: amountOut,
amountInMaximum: amountInMaximum
});
// Executes the swap, returning the amountIn actually spent.
amountIn = swapRouter.exactOutput(params);
// If the swap did not require the full amountInMaximum to achieve the exact amountOut then we refund msg.sender and approve the router to spend 0.
if (amountIn < amountInMaximum) {
IERC20(erc20In).safeTransfer(msg.sender, amountInMaximum - amountIn);
}
}
///This function appoves infinite allowance for Uniswap, this is safe as the paymenthub should never hold any token (see also recover() ).
///@dev This function needs to be called before using the PaymentHub the first time with a new ERC20 token.
///@param erc20In The erc20 addresse to approve.
function approveERC20(address erc20In) external {
IERC20(erc20In).approve(address(uniswapRouter), type(uint256).max);
}
function multiPay(IERC20 token, address[] calldata recipients, uint256[] calldata amounts) public {
for (uint i=0; i<recipients.length; i++) {
IERC20(token).safeTransferFrom(msg.sender, recipients[i], amounts[i]);
}
}
/**
* Can (at least in theory) save some gas as the sender balance only is touched in one transaction.
*/
function multiPayAndNotify(IERC20 token, IBrokerbot[] calldata brokerbots, uint256[] calldata amounts, bytes calldata ref) external {
for (uint i=0; i<brokerbots.length; i++) {
payAndNotify(token, brokerbots[i], amounts[i], ref);
}
}
/**
* @notice Allows to make a base currency payment from the sender to the brokerbot, given an allowance to this contract.
* @dev Equivalent to xchf.transferAndCall(brokerbot, amountInBase)
* @param brokerbot The brokerbot to pay and receive the shares from.
* @param amountInBase The amount of base currency used to buy shares.
* @param ref The reference data blob.
* @return The amount of shares bought
*/
function payAndNotify(IBrokerbot brokerbot, uint256 amountInBase, bytes calldata ref) external returns (uint256) {
return payAndNotify(brokerbot.base(), brokerbot, amountInBase, ref);
}
function payAndNotify(IERC20 token, IBrokerbot brokerbot, uint256 amount, bytes calldata ref) public returns (uint256) {
token.safeTransferFrom(msg.sender, address(brokerbot), amount);
return brokerbot.processIncoming(token, msg.sender, amount, ref);
}
/**
* @notice Pay with Ether to buy shares.
* @param brokerbot The brokerbot to pay and receive the shares from.
* @param amountBase The amount of base currency used to buy shares.
* @param ref The reference data blob.
* @param path The Uniswap path from ETH to base currency (uses exactOuput => reverse order)
* @return priceInEther The amount of Ether spent.
* @return sharesOut The amount of shares bought.
*/
function payFromEtherAndNotify(IBrokerbot brokerbot, uint256 amountBase, bytes calldata ref, bytes memory path) external payable returns (uint256 priceInEther, uint256 sharesOut) {
IERC20 base = brokerbot.base();
// Check if the brokerbot has setting to keep ETH
if (hasSettingKeepEther(brokerbot)) {
priceInEther = getPriceInEtherFromOracle(amountBase, base);
sharesOut = brokerbot.processIncoming{value: priceInEther}(base, msg.sender, amountBase, ref);
// Pay back ETH that was overpaid
if (priceInEther < msg.value) {
(bool success, ) = msg.sender.call{value:msg.value - priceInEther}(""); // return change
if (!success) {
revert PaymentHub_TransferFailed();
}
}
} else {
uint256 balanceBefore = IERC20(base).balanceOf(address(brokerbot));
priceInEther = payFromEther(address(brokerbot), amountBase, path);
uint256 balanceAfter = IERC20(base).balanceOf(address(brokerbot));
if (amountBase != (balanceAfter - balanceBefore)) { // check that the swap was successful with correct currency
revert PaymentHub_SwapError(amountBase, balanceAfter - balanceBefore);
}
sharesOut = brokerbot.processIncoming(base, msg.sender, amountBase, ref); // not sending msg.value as this is already done in payFromEther function
}
}
/***
* @notice Pay from any ERC20 token (which has Uniswapv3 ERC20-ETH pool) and send swapped base currency to brokerbot.
* @notice The needed amount needs to be approved at the ERC20 contract beforehand
* @param brokerbot The brokerbot to pay and receive the shares from.
* @param amountBase The amount of base currency used to buy shares.
* @param erc20 The address of the ERC20 token to pay.
* @param amountInMaximum The maximum amount of the ERC20 to pay (should include some slippage).
* @param path The encoded path of the swap from erc20 to base currency.
* @param ref Reference data blob.
* @return amountIn The amount erc20 spent to buy shares.
* @return amountOut The amount of shares received by the brokerbot.
*/
function payFromERC20AndNotify(IBrokerbot brokerbot, uint256 amountBase, address erc20, uint256 amountInMaximum, bytes memory path, bytes calldata ref) external returns (uint256 amountIn, uint256 amountOut) {
IERC20 base = brokerbot.base();
uint256 balanceBefore = IERC20(base).balanceOf(address(brokerbot));
amountIn = payFromERC20(amountBase, amountInMaximum, erc20, path, address(brokerbot));
uint256 balanceAfter = IERC20(base).balanceOf(address(brokerbot));
if (amountBase != (balanceAfter - balanceBefore)) {
revert PaymentHub_SwapError(amountBase, balanceAfter - balanceBefore);
}
amountOut = brokerbot.processIncoming(base, msg.sender, balanceAfter - balanceBefore, ref);
}
/**
* @notice Sell shares with permit
* @param brokerbot The brokerbot to recive the shares.
* @param seller The address of the seller.
* @param recipient The address of the recipient of the sell preceeds.
* @param amountToSell The amount the seller wants to sell.
* @param ref Reference e.g. insider declaration and the type of sell.
* @param permitInfo Information about the permit.
* @return The base currency amount for the selling of the shares.
*/
function sellSharesWithPermit(IBrokerbot brokerbot, IERC20Permit shares, address seller, address recipient, uint256 amountToSell, bytes calldata ref, PermitInfo calldata permitInfo) public onlySellerAndForwarder(seller) returns (uint256) {
// Call permit to set allowance
shares.permit(seller, address(this), amountToSell, permitInfo.deadline, permitInfo.v, permitInfo.r,permitInfo.s);
// process sell
if (permitInfo.exFee > 0){
uint256 proceeds = _sellShares(brokerbot, shares, seller, address(this), amountToSell, ref);
IERC20 currency = brokerbot.base();
currency.safeTransfer(msg.sender, permitInfo.exFee);
currency.safeTransfer(recipient, proceeds - permitInfo.exFee);
return proceeds - permitInfo.exFee;
} else {
return _sellShares(brokerbot, shares, seller, recipient, amountToSell, ref);
}
}
/**
* @notice With this function a user can sell shares with permit and swap them to a desired token.
* @param brokerbot The brokerbot of the shares to sell.
* @param shares The (draggable)shares address.
* @param seller The seller address.
* @param amountToSell The amount of shares to sell.
* @param ref Reference e.g. insider declaration and the type of sell.
* @param permitInfo Information about the permit.
* @param params Information about the swap.
* @return The output amount of the swap to the desired token.
*/
function sellSharesWithPermitAndSwap(IBrokerbot brokerbot, IERC20Permit shares, address seller, uint256 amountToSell, bytes calldata ref, PermitInfo calldata permitInfo, ISwapRouter.ExactInputParams memory params, bool unwrapWeth) external onlySellerAndForwarder(seller) returns (uint256) {
params.amountIn = sellSharesWithPermit(brokerbot, shares, seller, address(this), amountToSell, ref, permitInfo);
return _swap(params, unwrapWeth);
}
/**
* @notice With this function a user can sell shares and swap them to a desired token. The user has to approve the paymenthub before on the shares contract.
* @param brokerbot The brokerbot of the shares to sell.
* @param shares The (draggable)shares address.
* @param amountToSell The amount of shares to sell.
* @param ref Reference e.g. insider declaration and the type of sell.
* @param params Information about the swap.
* @return The output amount of the swap to the desired token.
*/
function sellSharesAndSwap(IBrokerbot brokerbot, IERC20 shares, uint256 amountToSell, bytes calldata ref, ISwapRouter.ExactInputParams memory params, bool unwrapWeth) external returns (uint256) {
params.amountIn = _sellShares(brokerbot, shares, msg.sender, address(this), amountToSell, ref);
return _swap(params, unwrapWeth);
}
/**
* @notice Transfers shares to brokerbot and executes the selling.
* @param brokerbot The brokerbot of the shares to sell.
* @param shares The (draggable)shares address.
* @param seller The seller address.
* @param recipient The recipient of the base currency tokens. (this can be a 3rd party to off-ramp or the paymenthub itself if a swap will be done direct after)
* @param amountToSell The amount of shares to sell.
* @param ref Reference e.g. insider declaration and the type of sell.
* @return The base currency amount for the selling of the shares.
*/
function _sellShares(IBrokerbot brokerbot, IERC20 shares, address seller, address recipient, uint256 amountToSell, bytes calldata ref ) internal returns (uint256) {
// send shares token to brokerbot
shares.safeTransferFrom(seller, address(brokerbot), amountToSell);
// process sell on brokerbot
return brokerbot.processIncoming(shares, recipient, amountToSell, ref);
}
/**
* @notice Swap (base currency) token according to given path and unwrap weth if needed.
* @param params Information about the swap (includes path).
* @return amountOut The output amount of the swap to the desired token.
*/
function _swap(ISwapRouter.ExactInputParams memory params, bool unwrapWeth) internal returns(uint256 amountOut) {
// if weth should be unwrapped, swap recipient is this contract and eth is send to seller
if (unwrapWeth){
address seller = params.recipient;
params.recipient = address(this);
amountOut = _swapToERC20(params);
IWETH9(uniswapQuoter.WETH9()).withdraw(amountOut);
(bool success, ) = payable(seller).call{value:amountOut}("");
if (!success) revert PaymentHub_TransferFailed();
} else {
amountOut = _swapToERC20(params);
}
}
/**
* @notice Calls the Uniswap router to swap tokens according to given path.
* @param params Information about the swap (includes path).
* @return amountOut The output amount of the swap to the desired token.
*/
function _swapToERC20(ISwapRouter.ExactInputParams memory params) internal returns(uint256 amountOut) {
amountOut = uniswapRouter.exactInput(params);
if (amountOut < params.amountOutMinimum){
revert PaymentHub_SwapError(params.amountOutMinimum, amountOut);
}
}
/**
* Checks if the brokerbot has setting enabled to keep ether
*/
function hasSettingKeepEther(IBrokerbot brokerbot) public view returns (bool) {
return brokerbot.settings() & KEEP_ETHER == KEEP_ETHER;
}
/**
* @notice In case tokens have been accidentally sent directly to this contract. Only Forwarder can withdraw, else a MEV bot will intercept it.
* @param ercAddress The erc20 address.
* @param to The address to transfer tokens to.
* @param amount The amount of tokens to transfer.
*/
function recover(IERC20 ercAddress, address to, uint256 amount) external onlyForwarder {
ercAddress.safeTransfer(to, amount);
}
/**
* @notice Transfer ether to a given address. Only Forwarder can withdraw, else a MEV bot will intercept it.
* @param to The address to transfer ether to.
*/
function withdrawEther(address to, uint256 amount) external onlyForwarder {
(bool success, ) = payable(to).call{value:amount}("");
if (!success) {
revert PaymentHub_TransferFailed();
}
}
/**
* @notice Transfer ether to a given address.
* @dev Used with the mutlisigwallet.
* @param to The address to transfer ether to.
*/
function transferEther(address to) external payable {
(bool success, ) = payable(to).call{value:msg.value}("");
if (!success) {
revert PaymentHub_TransferFailed();
}
}
// solhint-disable-next-line no-empty-blocks
receive() external payable {
// Important to receive ETH refund from Uniswap
}
}
AggregatorV3Interface.sol 35 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
function getRoundData(uint80 _roundId)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
Read Contract
VERSION 0xffa1ad74 → uint256
getLatestPriceCHFUSD 0x00124e3d → uint256
getLatestPriceETHUSD 0x95fe863c → uint256
getPriceInEtherFromOracle 0x1945b784 → uint256
hasSettingKeepEther 0xc1d8e08a → bool
trustedForwarder 0x7da0a877 → address
Write Contract 19 functions
These functions modify contract state and require a wallet transaction to execute.
approveERC20 0x11e8701c
address erc20In
changeForwarder 0x415a2187
address newForwarder
getPriceERC20 0x88e3b76a
uint256 amount
bytes path
bool exactOutput
returns: uint256
getPriceInERC20 0x078aa964
uint256 amountInBase
bytes path
returns: uint256
getPriceInEther 0x59c57ee2
uint256 amountInBase
address brokerBot
bytes path
returns: uint256
multiPay 0x1c9af2e3
address token
address[] recipients
uint256[] amounts
multiPayAndNotify 0x568f3254
address token
address[] brokerbots
uint256[] amounts
bytes ref
payAndNotify 0x17f565d2
address brokerbot
uint256 amountInBase
bytes ref
returns: uint256
payAndNotify 0xd3b3ca77
address token
address brokerbot
uint256 amount
bytes ref
returns: uint256
payFromERC20 0xd64d4ba5
uint256 amountOut
uint256 amountInMaximum
address erc20In
bytes path
address recipient
returns: uint256
payFromERC20AndNotify 0x5daf206a
address brokerbot
uint256 amountBase
address erc20
uint256 amountInMaximum
bytes path
bytes ref
returns: uint256, uint256
payFromEther 0xfb4daeec
address recipient
uint256 amountInBase
bytes path
returns: uint256
payFromEtherAndNotify 0x435fd56d
address brokerbot
uint256 amountBase
bytes ref
bytes path
returns: uint256, uint256
recover 0x1ec82cb8
address ercAddress
address to
uint256 amount
sellSharesAndSwap 0x991f7849
address brokerbot
address shares
uint256 amountToSell
bytes ref
tuple params
bool unwrapWeth
returns: uint256
sellSharesWithPermit 0xbe8325fe
address brokerbot
address shares
address seller
address recipient
uint256 amountToSell
bytes ref
tuple permitInfo
returns: uint256
sellSharesWithPermitAndSwap 0x17370200
address brokerbot
address shares
address seller
uint256 amountToSell
bytes ref
tuple permitInfo
tuple params
bool unwrapWeth
returns: uint256
transferEther 0x6b55e991
address to
withdrawEther 0x522f6815
address to
uint256 amount
Recent Transactions
No transactions found for this address