Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0xEB74EC1d4C1DAB412D5d6674F6833FD19d3118Ce
Balance 0 ETH
Nonce 1
Code Size 10479 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

10479 bytes
0x608060405234801561001057600080fd5b50600436106102325760003560e01c8063935a8b8411610130578063b15f67b3116100b8578063d10b5a5b1161007c578063d10b5a5b146105b6578063d3f730fd146105dd578063dd62ed3e146105fd578063f8ba4cff14610610578063ffaad6a51461061857600080fd5b8063b15f67b314610547578063b6b55f251461056e578063bbba205d14610581578063bfe69c8d14610590578063cde68041146105a357600080fd5b8063a1654379116100ff578063a165437914610498578063a628ef09146104c6578063a9059cbb146104ed578063ab9ba7f414610500578063aba7f15e1461052057600080fd5b8063935a8b841461042b5780639555a9421461043e57806395d89b411461045157806397008d6c1461045957600080fd5b80632e1a7d4d116101be57806343631bfe1161018257806343631bfe146103705780635f2d5f6e1461039b57806368a9674d146103dc57806370a08231146103ef578063790add031461041857600080fd5b80632e1a7d4d14610309578063313ce5671461031c57806334a1ca891461034d578063372500ab146103605780633ba0b9a91461036857600080fd5b80631b3ed722116102055780631b3ed7221461029f5780631f986445146102c6578063205c2878146102d057806323b872dd146102e35780632a846398146102f657600080fd5b806306fdde0314610237578063095ea7b314610255578063110496e51461027857806318160ddd1461028d575b600080fd5b61023f61062b565b60405161024c91906122bd565b60405180910390f35b61026861026336600461230c565b6106bd565b604051901515815260200161024c565b61028b610286366004612344565b610737565b005b6002545b60405190815260200161024c565b6102917f0000000000000000000000000000000000000000000000000de0b6b3a764000081565b610291620f424081565b61028b6102de36600461230c565b610746565b6102686102f136600461237b565b610752565b6102916103043660046123b7565b61078f565b61028b6103173660046123d2565b610938565b60405160ff7f000000000000000000000000000000000000000000000000000000000000000616815260200161024c565b61028b61035b3660046123eb565b610947565b61028b610c1d565b610291610c29565b61038361037e3660046123d2565b610cd6565b6040516001600160681b03909116815260200161024c565b6103c46103a93660046123b7565b6005602052600090815260409020546001600160401b031681565b6040516001600160401b03909116815260200161024c565b61028b6103ea36600461237b565b610cf5565b6102916103fd3660046123b7565b6001600160a01b031660009081526020819052604090205490565b610291610426366004612433565b610d06565b6102916104393660046123b7565b610d1e565b61028b61044c36600461237b565b610d54565b61023f610d60565b6104807f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab084081565b6040516001600160a01b03909116815260200161024c565b6102686104a63660046123eb565b600160209081526000928352604080842090915290825290205460ff1681565b6102917f000000000000000000000000000000000000000000000000000000e8d4a5100081565b6102686104fb36600461230c565b610d6f565b61029161050e3660046123b7565b60066020526000908152604090205481565b6102917f00000000000000000000000000000000000000000000000000038d7ea4c6800081565b6104807f0000000000000000000000001b0e765f6224c21223aea2af16c1c46e38885a4081565b61028b61057c3660046123d2565b610d7c565b610291670de0b6b3a764000081565b61028b61059e3660046123b7565b610d88565b6102686105b13660046123eb565b610e0a565b6104807f000000000000000000000000c00e94cb662c3520282e6f5717214004a7f2688881565b6102916105eb3660046123b7565b60076020526000908152604090205481565b61029161060b3660046123eb565b610e56565b61028b610e77565b61028b61062636600461230c565b610ef2565b60606003805461063a90612450565b80601f016020809104026020016040519081016040528092919081815260200182805461066690612450565b80156106b35780601f10610688576101008083540402835291602001916106b3565b820191906000526020600020905b81548152906001019060200180831161069657829003601f168201915b5050505050905090565b60006001600160a01b0383166106e65760405163d92e233d60e01b815260040160405180910390fd5b6000198203610700576106fb33846001610efe565b61072d565b81600003610714576106fb33846000610efe565b60405163749b593960e01b815260040160405180910390fd5b5060015b92915050565b610742338383610efe565b5050565b61074233338484610fc8565b600061075e8433610e0a565b61077a576040516282b42960e81b815260040160405180910390fd5b6107858484846112f9565b5060019392505050565b60008061079a611425565b6001600160a01b03851660009081526005602052604081205491935091506107cb906001600160401b0316836124a0565b6001600160401b0316905060007f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000038d7ea4c680008361084261083d896001600160a01b031660009081526020819052604090205490565b6116fb565b6001600160681b031661085591906124c7565b61085f91906124de565b61086991906124de565b6001600160a01b03861660009081526006602052604090205461088c9190612500565b6001600160a01b038616600090815260076020526040812054919250670de0b6b3a76400007f0000000000000000000000000000000000000000000000000de0b6b3a76400006108fc7f000000000000000000000000000000000000000000000000000000e8d4a51000866124c7565b61090691906124c7565b61091091906124de565b9050600082821161092257600061092c565b61092c8383612513565b98975050505050505050565b61094433333384610fc8565b50565b6109518233610e0a565b61096d576040516282b42960e81b815260040160405180910390fd5b61097682610d88565b6001600160a01b0382166000908152600760209081526040808320546006909252822054909190670de0b6b3a7640000907f0000000000000000000000000000000000000000000000000de0b6b3a7640000906109f4907f000000000000000000000000000000000000000000000000000000e8d4a51000906124c7565b6109fe91906124c7565b610a0891906124de565b9050600082821115610bb357610a1e8383612513565b6001600160a01b0386811660009081526007602052604090819020859055516313fe176560e21b81527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08408216600482015230602482018190526044820152600160648201529192507f0000000000000000000000001b0e765f6224c21223aea2af16c1c46e38885a401690634ff85d9490608401600060405180830381600087803b158015610acc57600080fd5b505af1158015610ae0573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092507f000000000000000000000000c00e94cb662c3520282e6f5717214004a7f268886001600160a01b031691506370a0823190602401602060405180830381865afa158015610b4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b6f9190612526565b905080821115610b7d578091505b610bb16001600160a01b037f000000000000000000000000c00e94cb662c3520282e6f5717214004a7f26888168684611729565b505b7f000000000000000000000000c00e94cb662c3520282e6f5717214004a7f268886001600160a01b03167ffc30cddea38e2bf4d6ea7d3f9ed3b6ad7f176419f4963bd81318067a4aee73fe82604051610c0e91815260200190565b60405180910390a25050505050565b610c273333610947565b565b600080610c34611425565b509050610cd081610ccb7f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cc09190612550565b61083d90600a61264f565b61178c565b91505090565b600080610ce1611425565b509050610cee81846117bc565b9392505050565b610d01338484846117e6565b505050565b600080610d11611425565b509050610cee818461178c565b6001600160a01b03811660009081526020819052604081205480600003610d485750600092915050565b610cee610426826116fb565b610d0133848484610fc8565b60606004805461063a90612450565b600061072d3384846112f9565b610944333333846117e6565b60405163bfe69c8d60e01b81523060048201527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b03169063bfe69c8d90602401600060405180830381600087803b158015610de957600080fd5b505af1158015610dfd573d6000803e3d6000fd5b5050505061094481611bc7565b6000816001600160a01b0316836001600160a01b03161480610cee5750506001600160a01b03918216600090815260016020908152604080832093909416825291909152205460ff1690565b6000610e628383610e0a565b610e6d576000610cee565b5060001992915050565b60405163bfe69c8d60e01b81523060048201527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b03169063bfe69c8d90602401600060405180830381600087803b158015610ed857600080fd5b505af1158015610eec573d6000803e3d6000fd5b50505050565b610742333384846117e6565b6001600160a01b038316610f255760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038216610f4c5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038381166000818152600160209081526040808320948716808452949091529020805460ff19168415151790557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92583610fad576000610fb1565b6000195b6040519081526020015b60405180910390a3505050565b610fd28385610e0a565b610fee576040516282b42960e81b815260040160405180910390fd5b6000610ff984610d1e565b905081811015611007578091505b816000036110285760405163749b593960e01b815260040160405180910390fd5b60405163bfe69c8d60e01b81523060048201527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b03169063bfe69c8d90602401600060405180830381600087803b15801561108957600080fd5b505af115801561109d573d6000803e3d6000fd5b505060405163bfe69c8d60e01b81526001600160a01b0387811660048301527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab084016925063bfe69c8d9150602401600060405180830381600087803b15801561110457600080fd5b505af1158015611118573d6000803e3d6000fd5b50505050600061113d856001600160a01b031660009081526020819052604090205490565b60405163dc4abafd60e01b81523060048201529091506000906001600160a01b037f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab0840169063dc4abafd90602401606060405180830381865afa1580156111a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111cb9190612675565b805190915061121b866111df600a886124de565b6111ea90600a6124c7565b6001600160a01b037f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab0840169190611729565b60405163dc4abafd60e01b81523060048201527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b03169063dc4abafd90602401606060405180830381865afa15801561127f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a39190612675565b805190925060006112b482846126f7565b6001600160681b031690508085116112c95750835b6112d289611bc7565b6112ed896112df836116fb565b6001600160681b0316611d02565b50505050505050505050565b6001600160a01b0383166113205760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382166113475760405163d92e233d60e01b815260040160405180910390fd5b611352838383611dd8565b6001600160a01b0383166000908152602081905260409020548082111561139457604051632e7a668d60e21b8152600481018390526024015b60405180910390fd5b6001600160a01b038085166000908152602081905260408082208585039055918516815290812080548492906113cb908490612500565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161141791815260200190565b60405180910390a350505050565b60008060007f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b031663b9f0baf76040518163ffffffff1660e01b815260040161010060405180830381865afa158015611489573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ad9190612750565b905060008160c00151426114c19190612827565b825160408401519192509064ffffffffff8316156116f05760007f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b031663189bb2f16040518163ffffffff1660e01b8152600401602060405180830381865afa158015611539573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061155d9190612526565b905060007f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b0316637eb711316040518163ffffffff1660e01b8152600401602060405180830381865afa1580156115bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115e39190612526565b60405163d955759d60e01b8152600481018290529091506000906001600160a01b037f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab0840169063d955759d90602401602060405180830381865afa15801561164e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116729190612845565b6001600160401b031690506116aa6116a5866001600160401b03168864ffffffffff16846116a091906124c7565b611e63565b611e78565b6116b49086612860565b94506116e06116a56116cd64ffffffffff8916866124c7565b89608001516001600160681b0316611ea2565b6116ea9085612860565b93505050505b909590945092505050565b60006001600160681b0382111561172557604051630dc7925560e11b815260040160405180910390fd5b5090565b6040516001600160a01b038316602482015260448101829052610d0190849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611eb2565b600066038d7ea4c680006117b26001600160401b0385166001600160681b0385166124c7565b610cee91906124de565b6000610cee6001600160401b0384166117dc66038d7ea4c68000856124c7565b61083d91906124de565b60405163cde6804160e01b81526001600160a01b03848116600483015285811660248301527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab0840169063cde6804190604401602060405180830381865afa158015611854573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118789190612880565b611894576040516282b42960e81b815260040160405180910390fd5b6040516370a0823160e01b81526001600160a01b0384811660048301526000917f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab0840909116906370a0823190602401602060405180830381865afa1580156118ff573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119239190612526565b905080821115611931578091505b816000036119525760405163749b593960e01b815260040160405180910390fd5b60405163bfe69c8d60e01b81523060048201527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b03169063bfe69c8d90602401600060405180830381600087803b1580156119b357600080fd5b505af11580156119c7573d6000803e3d6000fd5b505060405163bfe69c8d60e01b81526001600160a01b0387811660048301527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab084016925063bfe69c8d9150602401600060405180830381600087803b158015611a2e57600080fd5b505af1158015611a42573d6000803e3d6000fd5b505060405163dc4abafd60e01b8152306004820152600092507f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b0316915063dc4abafd90602401606060405180830381865afa158015611aad573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ad19190612675565b8051909150611b0b6001600160a01b037f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab084016873087611f87565b60405163dc4abafd60e01b81523060048201527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b03169063dc4abafd90602401606060405180830381865afa158015611b6f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b939190612675565b8051909250611ba186611bc7565b611bbd86611baf84846126f7565b6001600160681b0316611fbf565b5050505050505050565b6001600160a01b03811660009081526020819052604081205490611be961206f565b6001600160a01b0385166000908152600560205260408120549193509150611c1a906001600160401b0316836124a0565b6001600160401b031690507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000038d7ea4c6800082611c71866116fb565b6001600160681b0316611c8491906124c7565b611c8e91906124de565b611c9891906124de565b6001600160a01b03851660009081526006602052604081208054909190611cc0908490612500565b9091555050506001600160a01b03929092166000908152600560205260409020805467ffffffffffffffff19166001600160401b039093169290921790915550565b6001600160a01b038216611d295760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03821660009081526020819052604090205480821115611d6657604051632e7a668d60e21b81526004810183905260240161138b565b6001600160a01b0383166000908152602081905260408120838303905560028054849290611d95908490612513565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610fbb565b60405163bfe69c8d60e01b81523060048201527f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b03169063bfe69c8d90602401600060405180830381600087803b158015611e3957600080fd5b505af1158015611e4d573d6000803e3d6000fd5b50505050611e5a83611bc7565b610d0182611bc7565b6000670de0b6b3a76400006117b283856124c7565b60006001600160401b03821115611725576040516372a1cb5160e11b815260040160405180910390fd5b6000816117b2620f4240856124c7565b6000611f07826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166121099092919063ffffffff16565b9050805160001480611f28575080806020019051810190611f289190612880565b610d015760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161138b565b6040516001600160a01b0380851660248301528316604482015260648101829052610eec9085906323b872dd60e01b90608401611755565b6001600160a01b038216611fe65760405163d92e233d60e01b815260040160405180910390fd5b8060026000828254611ff89190612500565b90915550506001600160a01b03821660009081526020819052604081208054839290612025908490612500565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b60008060007f0000000000000000000000003afdc9bca9213a35503b077a6072f3d0d5ab08406001600160a01b031663b9f0baf76040518163ffffffff1660e01b815260040161010060405180830381865afa1580156120d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120f79190612750565b80516040909101519094909350915050565b60606121188484600085612120565b949350505050565b6060824710156121815760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840161138b565b600080866001600160a01b0316858760405161219d919061289d565b60006040518083038185875af1925050503d80600081146121da576040519150601f19603f3d011682016040523d82523d6000602084013e6121df565b606091505b50915091506121f0878383876121fb565b979650505050505050565b6060831561226a578251600003612263576001600160a01b0385163b6122635760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161138b565b5081612118565b612118838381511561227f5781518083602001fd5b8060405162461bcd60e51b815260040161138b91906122bd565b60005b838110156122b457818101518382015260200161229c565b50506000910152565b60208152600082518060208401526122dc816040850160208701612299565b601f01601f19169190910160400192915050565b80356001600160a01b038116811461230757600080fd5b919050565b6000806040838503121561231f57600080fd5b612328836122f0565b946020939093013593505050565b801515811461094457600080fd5b6000806040838503121561235757600080fd5b612360836122f0565b9150602083013561237081612336565b809150509250929050565b60008060006060848603121561239057600080fd5b612399846122f0565b92506123a7602085016122f0565b9150604084013590509250925092565b6000602082840312156123c957600080fd5b610cee826122f0565b6000602082840312156123e457600080fd5b5035919050565b600080604083850312156123fe57600080fd5b612407836122f0565b9150612415602084016122f0565b90509250929050565b6001600160681b038116811461094457600080fd5b60006020828403121561244557600080fd5b8135610cee8161241e565b600181811c9082168061246457607f821691505b60208210810361248457634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b6001600160401b038281168282160390808211156124c0576124c061248a565b5092915050565b80820281158282048414176107315761073161248a565b6000826124fb57634e487b7160e01b600052601260045260246000fd5b500490565b808201808211156107315761073161248a565b818103818111156107315761073161248a565b60006020828403121561253857600080fd5b5051919050565b805160ff8116811461230757600080fd5b60006020828403121561256257600080fd5b610cee8261253f565b600181815b808511156125a657816000190482111561258c5761258c61248a565b8085161561259957918102915b93841c9390800290612570565b509250929050565b6000826125bd57506001610731565b816125ca57506000610731565b81600181146125e057600281146125ea57612606565b6001915050610731565b60ff8411156125fb576125fb61248a565b50506001821b610731565b5060208310610133831016604e8410600b8410161715612629575081810a610731565b612633838361256b565b80600019048211156126475761264761248a565b029392505050565b6000610cee60ff8416836125ae565b80516001600160401b038116811461230757600080fd5b60006060828403121561268757600080fd5b604051606081018181106001600160401b03821117156126b757634e487b7160e01b600052604160045260246000fd5b6040528251600c81900b81146126cc57600080fd5b81526126da6020840161265e565b60208201526126eb6040840161265e565b60408201529392505050565b600c82810b9082900b036c7fffffffffffffffffffffffff1981126c7fffffffffffffffffffffffff821317156107315761073161248a565b80516123078161241e565b805164ffffffffff8116811461230757600080fd5b600061010080838503121561276457600080fd5b604051908101906001600160401b038211818310171561279457634e487b7160e01b600052604160045260246000fd5b816040526127a18461265e565b81526127af6020850161265e565b60208201526127c06040850161265e565b60408201526127d16060850161265e565b6060820152608084015191506127e68261241e565b8160808201526127f860a08501612730565b60a082015261280960c0850161273b565b60c082015261281a60e0850161253f565b60e0820152949350505050565b64ffffffffff8281168282160390808211156124c0576124c061248a565b60006020828403121561285757600080fd5b610cee8261265e565b6001600160401b038181168382160190808211156124c0576124c061248a565b60006020828403121561289257600080fd5b8151610cee81612336565b600082516128af818460208701612299565b919091019291505056fea264697066735822122098623f6c06c1738518713d1aa9e46188eaf9b405905505489170cd0bdb6aca0564736f6c63430008130033

Verified Source Code Full Match

Compiler: v0.8.19+commit.7dd6d404 EVM: paris Optimization: Yes (200 runs)
IERC20Metadata.sol 28 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
IERC20Permit.sol 90 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

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

    /**
     * @dev 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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

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

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

    /**
     * @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);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @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, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @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.isContract(address(token));
    }
}
Address.sol 244 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

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

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

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

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

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

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}
IRewardable.sol 31 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

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

/**
 * @title IRewardable
 * @notice A simple interface mixin to support claiming of rewards.
 */
interface IRewardable {
    /// Emitted whenever a reward token balance is claimed
    /// @param erc20 The ERC20 of the reward token
    /// @param amount {qTok}
    event RewardsClaimed(IERC20 indexed erc20, uint256 amount);

    /// Claim rewards earned by holding a balance of the ERC20 token
    /// Must emit `RewardsClaimed` for each token rewards are claimed for
    /// @custom:interaction
    function claimRewards() external;
}

/**
 * @title IRewardableComponent
 * @notice A simple interface mixin to support claiming of rewards.
 */
interface IRewardableComponent is IRewardable {
    /// Claim rewards for a single ERC20
    /// Must emit `RewardsClaimed` for each token rewards are claimed for
    /// @custom:interaction
    function claimRewardsSingle(IERC20 erc20) external;
}
CFiatV3Wrapper.sol 329 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./vendor/CometInterface.sol";
import "./WrappedERC20.sol";
import "./vendor/ICometRewards.sol";
import "./ICFiatV3Wrapper.sol";
import "./CometHelpers.sol";

/**
 * @title CFiatV3Wrapper
 * @notice Wrapper for Compound V3 fiat coins such as cUSDCv3, cUSDTv3 / COMET that acts
 * as a stable-balance ERC20, instead of rebasing token. {comet} will be used as the unit
 * for the underlying token, and {wComet} will be used as the unit for wrapped tokens.
 */
contract CFiatV3Wrapper is ICFiatV3Wrapper, WrappedERC20, CometHelpers {
    using SafeERC20 for IERC20;

    CometInterface public immutable underlyingComet;
    ICometRewards public immutable rewardsAddr;
    IERC20 public immutable rewardERC20;
    uint256 public immutable trackingIndexScale;
    uint256 public immutable rescaleFactor;
    uint256 internal immutable accrualDescaleFactor;
    uint256 public immutable multiplier;
    uint8 internal immutable cometDecimals;

    mapping(address => uint64) public baseTrackingIndex; // uint64 for consistency with CometHelpers
    mapping(address => uint256) public baseTrackingAccrued; // uint256 to avoid overflow in L:199
    mapping(address => uint256) public rewardsClaimed;

    constructor(
        address ctokenv3,
        address rewardsAddr_,
        address rewardERC20_,
        string memory name,
        string memory symbol,
        uint256 rewardMultiplier
    ) WrappedERC20(name, symbol) {
        if (ctokenv3 == address(0)) revert ZeroAddress();

        rewardsAddr = ICometRewards(rewardsAddr_);
        rewardERC20 = IERC20(rewardERC20_);
        underlyingComet = CometInterface(ctokenv3);
        cometDecimals = underlyingComet.decimals();
        // for principal <> present calculations
        trackingIndexScale = underlyingComet.trackingIndexScale();
        // scaling factor for rewards
        rescaleFactor = 10**(18 - cometDecimals);
        accrualDescaleFactor = 10**(cometDecimals - 6);
        multiplier = rewardMultiplier;
    }

    /// @return number of decimals
    function decimals() public view override(IERC20Metadata, WrappedERC20) returns (uint8) {
        return cometDecimals;
    }

    /// @param amount {Comet} The amount of cTokenV3 to deposit
    function deposit(uint256 amount) external {
        _deposit(msg.sender, msg.sender, msg.sender, amount);
    }

    /// @param dst The dst to deposit into
    /// @param amount {Comet} The amount of cTokenV3 to deposit
    function depositTo(address dst, uint256 amount) external {
        _deposit(msg.sender, msg.sender, dst, amount);
    }

    /// @param src The address to deposit from
    /// @param dst The address to deposit to
    /// @param amount {Comet} The amount of cTokenV3 to deposit
    function depositFrom(
        address src,
        address dst,
        uint256 amount
    ) external {
        _deposit(msg.sender, src, dst, amount);
    }

    /// Only called internally to run the deposit logic
    /// Takes `amount` fo cTokenV3 from `src` and deposits to `dst` account in the wrapper.
    /// @param operator The address calling the contract (msg.sender)
    /// @param src The address to deposit from
    /// @param dst The address to deposit to
    /// @param amount {Comet} The amount of cTokenv3 to deposit
    function _deposit(
        address operator,
        address src,
        address dst,
        uint256 amount
    ) internal {
        if (!underlyingComet.hasPermission(src, operator)) revert Unauthorized();
        // {Comet}
        uint256 srcBal = underlyingComet.balanceOf(src);
        if (amount > srcBal) amount = srcBal;
        if (amount == 0) revert BadAmount();

        underlyingComet.accrueAccount(address(this));
        underlyingComet.accrueAccount(src);

        CometInterface.UserBasic memory wrappedBasic = underlyingComet.userBasic(address(this));
        int104 wrapperPrePrinc = wrappedBasic.principal;

        IERC20(address(underlyingComet)).safeTransferFrom(src, address(this), amount);

        wrappedBasic = underlyingComet.userBasic(address(this));
        int104 wrapperPostPrinc = wrappedBasic.principal;
        accrueAccountRewards(dst);
        // safe to cast because amount is positive
        _mint(dst, uint104(wrapperPostPrinc - wrapperPrePrinc));
    }

    /// @param amount {Comet} The amount of cTokenV3 to withdraw
    function withdraw(uint256 amount) external {
        _withdraw(msg.sender, msg.sender, msg.sender, amount);
    }

    /// @param dst The address to withdraw cTokenv3 to
    /// @param amount {Comet} The amount of cTokenv3 to withdraw
    function withdrawTo(address dst, uint256 amount) external {
        _withdraw(msg.sender, msg.sender, dst, amount);
    }

    /// @param src The address to withdraw from
    /// @param dst The address to withdraw cTokenv3 to
    /// @param amount {Comet} The amount of cTokenv3 to withdraw
    function withdrawFrom(
        address src,
        address dst,
        uint256 amount
    ) external {
        _withdraw(msg.sender, src, dst, amount);
    }

    /// Internally called to run the withdraw logic
    /// Withdraws `amount` cTokenv3 from `src` account in the wrapper and sends to `dst`
    /// @dev Rounds conservatively so as not to over-withdraw from the wrapper
    /// @param operator The address calling the contract (msg.sender)
    /// @param src The address to withdraw from
    /// @param dst The address to withdraw cTokenv3 to
    /// @param amount {Comet} The amount of cTokenv3 to withdraw
    function _withdraw(
        address operator,
        address src,
        address dst,
        uint256 amount
    ) internal {
        if (!hasPermission(src, operator)) revert Unauthorized();
        // {Comet}
        uint256 srcBalUnderlying = underlyingBalanceOf(src);
        if (srcBalUnderlying < amount) amount = srcBalUnderlying;
        if (amount == 0) revert BadAmount();

        underlyingComet.accrueAccount(address(this));
        underlyingComet.accrueAccount(src);

        uint256 srcBalPre = balanceOf(src);
        CometInterface.UserBasic memory wrappedBasic = underlyingComet.userBasic(address(this));
        int104 wrapperPrePrinc = wrappedBasic.principal;

        // conservative rounding in favor of the wrapper
        IERC20(address(underlyingComet)).safeTransfer(dst, (amount / 10) * 10);

        wrappedBasic = underlyingComet.userBasic(address(this));
        int104 wrapperPostPrinc = wrappedBasic.principal;

        // safe to cast because principal can't go negative, wrapper is not borrowing
        uint256 burnAmt = uint256(uint104(wrapperPrePrinc - wrapperPostPrinc));
        // occasionally comet will withdraw 1-10 wei more than we asked for.
        // this is ok because 9 times out of 10 we are rounding in favor of the wrapper.
        // safe because we have already capped the comet withdraw amount to src underlying bal.
        // untested:
        //      difficult to trigger, depends on comet rules regarding rounding
        if (srcBalPre <= burnAmt) burnAmt = srcBalPre;

        accrueAccountRewards(src);
        _burn(src, safe104(burnAmt));
    }

    /// Internally called to run transfer logic.
    /// Accrues rewards for `src` and `dst` before transferring value.
    function _beforeTokenTransfer(
        address src,
        address dst,
        uint256 amount
    ) internal virtual override {
        underlyingComet.accrueAccount(address(this));

        super._beforeTokenTransfer(src, dst, amount);

        accrueAccountRewards(src);
        accrueAccountRewards(dst);
    }

    function claimRewards() external {
        claimTo(msg.sender, msg.sender);
    }

    /// @param src The account to claim from
    /// @param dst The address to send claimed rewards to
    function claimTo(address src, address dst) public {
        if (!hasPermission(src, msg.sender)) revert Unauthorized();

        accrueAccount(src);
        uint256 claimed = rewardsClaimed[src];
        uint256 accrued = (baseTrackingAccrued[src] * rescaleFactor * multiplier) / 1e18;
        uint256 owed;
        if (accrued > claimed) {
            owed = accrued - claimed;
            rewardsClaimed[src] = accrued;

            rewardsAddr.claimTo(address(underlyingComet), address(this), address(this), true);

            uint256 bal = rewardERC20.balanceOf(address(this));
            if (owed > bal) owed = bal;
            rewardERC20.safeTransfer(dst, owed);
        }
        emit RewardsClaimed(rewardERC20, owed);
    }

    /// Accure the cTokenv3 account of the wrapper
    function accrue() public {
        underlyingComet.accrueAccount(address(this));
    }

    /// @param account The address to accrue, first in cTokenv3, then locally
    function accrueAccount(address account) public {
        underlyingComet.accrueAccount(address(this));
        accrueAccountRewards(account);
    }

    /// Get the balance of cTokenv3 that is represented by the `accounts` wrapper value.
    /// @param account The address to calculate the cTokenv3 balance of
    /// @return {Comet} The cTokenv3 balance that `account` holds in the wrapper
    function underlyingBalanceOf(address account) public view returns (uint256) {
        uint256 balance = balanceOf(account);
        if (balance == 0) {
            return 0;
        }
        return convertStaticToDynamic(safe104(balance));
    }

    /// @return The exchange rate {comet/wComet}
    function exchangeRate() public view returns (uint256) {
        (uint64 baseSupplyIndex, ) = getUpdatedSupplyIndicies();
        return presentValueSupply(baseSupplyIndex, safe104(10**underlyingComet.decimals()));
    }

    /// @param amount The value of {wComet} to convert to {Comet}
    /// @return {Comet} The amount of cTokenv3 represented by `amount of {wComet}
    function convertStaticToDynamic(uint104 amount) public view returns (uint256) {
        (uint64 baseSupplyIndex, ) = getUpdatedSupplyIndicies();
        return presentValueSupply(baseSupplyIndex, amount);
    }

    /// @param amount The value of {Comet} to convert to {wComet}
    /// @return {wComet} The amount of wrapped token represented by `amount` of {Comet}
    function convertDynamicToStatic(uint256 amount) public view returns (uint104) {
        (uint64 baseSupplyIndex, ) = getUpdatedSupplyIndicies();
        return principalValueSupply(baseSupplyIndex, amount);
    }

    /// @param account The address to view the owed rewards of
    /// @return {reward} The amount of reward tokens owed to `account`
    function getRewardOwed(address account) external view returns (uint256) {
        (, uint64 trackingSupplyIndex) = getUpdatedSupplyIndicies();

        uint256 indexDelta = uint256(trackingSupplyIndex - baseTrackingIndex[account]);
        uint256 newBaseTrackingAccrued = baseTrackingAccrued[account] +
            (safe104(balanceOf(account)) * indexDelta) /
            trackingIndexScale /
            accrualDescaleFactor;

        uint256 claimed = rewardsClaimed[account];
        uint256 accrued = (newBaseTrackingAccrued * rescaleFactor * multiplier) / 1e18;
        uint256 owed = accrued > claimed ? accrued - claimed : 0;

        return owed;
    }

    /// Internally called to get saved indicies
    /// @return baseSupplyIndex_ {1} The saved baseSupplyIndex
    /// @return trackingSupplyIndex_ {1} The saved trackingSupplyIndex
    function getSupplyIndices()
        internal
        view
        returns (uint64 baseSupplyIndex_, uint64 trackingSupplyIndex_)
    {
        TotalsBasic memory totals = underlyingComet.totalsBasic();
        baseSupplyIndex_ = totals.baseSupplyIndex;
        trackingSupplyIndex_ = totals.trackingSupplyIndex;
    }

    /// Internally called to update the account indicies and accrued rewards for a given address
    /// @param account The UserBasic struct for a target address
    function accrueAccountRewards(address account) internal {
        uint256 accountBal = balanceOf(account);
        (, uint64 trackingSupplyIndex) = getSupplyIndices();
        uint256 indexDelta = uint256(trackingSupplyIndex - baseTrackingIndex[account]);

        baseTrackingAccrued[account] +=
            (safe104(accountBal) * indexDelta) /
            trackingIndexScale /
            accrualDescaleFactor;
        baseTrackingIndex[account] = trackingSupplyIndex;
    }

    /// Internally called to get the updated supply indicies
    /// @return {1} The current baseSupplyIndex
    /// @return {1} The current trackingSupplyIndex
    function getUpdatedSupplyIndicies() internal view returns (uint64, uint64) {
        TotalsBasic memory totals = underlyingComet.totalsBasic();
        uint40 timeDelta = uint40(block.timestamp) - totals.lastAccrualTime;
        uint64 baseSupplyIndex_ = totals.baseSupplyIndex;
        uint64 trackingSupplyIndex_ = totals.trackingSupplyIndex;
        if (timeDelta != 0) {
            uint256 baseTrackingSupplySpeed = underlyingComet.baseTrackingSupplySpeed();
            uint256 utilization = underlyingComet.getUtilization();
            uint256 supplyRate = underlyingComet.getSupplyRate(utilization);
            baseSupplyIndex_ += safe64(mulFactor(baseSupplyIndex_, supplyRate * timeDelta));
            trackingSupplyIndex_ += safe64(
                divBaseWei(baseTrackingSupplySpeed * timeDelta, totals.totalSupplyBase)
            );
        }
        return (baseSupplyIndex_, trackingSupplyIndex_);
    }
}
CometHelpers.sol 54 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

contract CometHelpers {
    uint64 internal constant BASE_INDEX_SCALE = 1e15;
    uint256 public constant EXP_SCALE = 1e18;
    uint256 public constant BASE_SCALE = 1e6;

    error InvalidUInt64();
    error InvalidUInt104();
    error InvalidInt256();
    error NegativeNumber();

    function safe64(uint256 n) internal pure returns (uint64) {
        // untested:
        //     comet code, overflow is hard to cover
        if (n > type(uint64).max) revert InvalidUInt64();
        return uint64(n);
    }

    function presentValueSupply(uint64 baseSupplyIndex_, uint104 principalValue_)
        internal
        pure
        returns (uint256)
    {
        return (uint256(principalValue_) * baseSupplyIndex_) / BASE_INDEX_SCALE;
    }

    function principalValueSupply(uint64 baseSupplyIndex_, uint256 presentValue_)
        internal
        pure
        returns (uint104)
    {
        return safe104((presentValue_ * BASE_INDEX_SCALE) / baseSupplyIndex_);
    }

    function safe104(uint256 n) internal pure returns (uint104) {
        // untested:
        //     comet code, overflow is hard to cover
        if (n > type(uint104).max) revert InvalidUInt104();
        return uint104(n);
    }

    /**
     * @dev Multiply a number by a factor
     */
    function mulFactor(uint256 n, uint256 factor) internal pure returns (uint256) {
        return (n * factor) / EXP_SCALE;
    }

    function divBaseWei(uint256 n, uint256 baseWei) internal pure returns (uint256) {
        return (n * BASE_SCALE) / baseWei;
    }
}
ICFiatV3Wrapper.sol 61 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./vendor/CometInterface.sol";
import "./IWrappedERC20.sol";
import "../../../interfaces/IRewardable.sol";

interface ICFiatV3Wrapper is IWrappedERC20, IRewardable {
    struct UserBasic {
        uint104 principal;
        uint64 baseTrackingIndex;
        uint64 baseTrackingAccrued;
        uint256 rewardsClaimed;
    }

    function deposit(uint256 amount) external;

    function depositTo(address account, uint256 amount) external;

    function depositFrom(
        address from,
        address dst,
        uint256 amount
    ) external;

    function withdraw(uint256 amount) external;

    function withdrawTo(address to, uint256 amount) external;

    function withdrawFrom(
        address src,
        address to,
        uint256 amount
    ) external;

    function claimTo(address src, address to) external;

    function accrue() external;

    function accrueAccount(address account) external;

    function underlyingBalanceOf(address account) external view returns (uint256);

    function getRewardOwed(address account) external view returns (uint256);

    function exchangeRate() external view returns (uint256);

    function convertStaticToDynamic(uint104 amount) external view returns (uint256);

    function convertDynamicToStatic(uint256 amount) external view returns (uint104);

    function baseTrackingAccrued(address account) external view returns (uint256);

    function baseTrackingIndex(address account) external view returns (uint64);

    function underlyingComet() external view returns (CometInterface);

    function rewardERC20() external view returns (IERC20);
}
IWrappedERC20.sol 12 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IWrappedERC20 is IERC20Metadata {
    function allow(address account, bool isAllowed_) external;

    function hasPermission(address owner, address manager) external view returns (bool);

    function isAllowed(address first, address second) external returns (bool);
}
CometExtInterface.sol 106 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

struct TotalsBasic {
    uint64 baseSupplyIndex;
    uint64 baseBorrowIndex;
    uint64 trackingSupplyIndex;
    uint64 trackingBorrowIndex;
    uint104 totalSupplyBase;
    uint104 totalBorrowBase;
    uint40 lastAccrualTime;
    uint8 pauseFlags;
}

/**
 * @title Compound's Comet Ext Interface
 * @notice An efficient monolithic money market protocol
 * @author Compound
 */
abstract contract CometExtInterface {
    error BadAmount();
    error BadNonce();
    error BadSignatory();
    error InvalidValueS();
    error InvalidValueV();
    error SignatureExpired();

    function allow(address manager, bool isAllowed) external virtual;

    function allowBySig(
        address owner,
        address manager,
        bool isAllowed,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external virtual;

    function collateralBalanceOf(address account, address asset)
        external
        view
        virtual
        returns (uint128);

    function baseTrackingAccrued(address account) external view virtual returns (uint64);

    function baseAccrualScale() external view virtual returns (uint64);

    function baseIndexScale() external view virtual returns (uint64);

    function factorScale() external view virtual returns (uint64);

    function priceScale() external view virtual returns (uint64);

    function maxAssets() external view virtual returns (uint8);

    function totalsBasic() external view virtual returns (TotalsBasic memory);

    function version() external view virtual returns (string memory);

    /**
     * ===== ERC20 interfaces =====
     * Does not include the following functions/events, which are defined in `CometMainInterface`
     * instead:
     * - function decimals() virtual external view returns (uint8)
     * - function totalSupply() virtual external view returns (uint256)
     * - function transfer(address dst, uint amount) virtual external returns (bool)
     * - function transferFrom(address src, address dst, uint amount) virtual external returns
        (bool)
     * - function balanceOf(address owner) virtual external view returns (uint256)
     * - event Transfer(address indexed from, address indexed to, uint256 amount)
     */
    function name() external view virtual returns (string memory);

    function symbol() external view virtual returns (string memory);

    /**
     * @notice Approve `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender`
     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @param amount The number of tokens that are approved (-1 means infinite)
     * @return Whether or not the approval succeeded
     */
    function approve(address spender, uint256 amount) external virtual returns (bool);

    /**
     * @notice Get the current allowance from `owner` for `spender`
     * @param owner The address of the account which owns the tokens to be spent
     * @param spender The address of the account which may transfer tokens
     * @return The number of tokens allowed to be spent (-1 means infinite)
     */
    function allowance(address owner, address spender) external view virtual returns (uint256);

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

    /**
     * @notice Determine if the manager has permission to act on behalf of the owner
     * @param owner The owner account
     * @param manager The manager account
     * @return Whether or not the manager has permission
     */
    function hasPermission(address owner, address manager) external view virtual returns (bool);
}
CometInterface.sol 20 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

import "./CometMainInterface.sol";
import "./CometExtInterface.sol";

/**
 * @title Compound's Comet Interface
 * @notice An efficient monolithic money market protocol
 * @author Compound
 */
abstract contract CometInterface is CometMainInterface, CometExtInterface {
    struct UserBasic {
        int104 principal;
        uint64 baseTrackingIndex;
        uint64 baseTrackingAccrued;
    }

    function userBasic(address account) external view virtual returns (UserBasic memory);
}
CometMainInterface.sol 288 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;

struct AssetInfo {
    uint8 offset;
    address asset;
    address priceFeed;
    uint64 scale;
    uint64 borrowCollateralFactor;
    uint64 liquidateCollateralFactor;
    uint64 liquidationFactor;
    uint128 supplyCap;
}

/**
 * @title Compound's Comet Main Interface (without Ext)
 * @notice An efficient monolithic money market protocol
 * @author Compound
 */
abstract contract CometMainInterface {
    error Absurd();
    error AlreadyInitialized();
    error BadAsset();
    error BadDecimals();
    error BadDiscount();
    error BadMinimum();
    error BadPrice();
    error BorrowTooSmall();
    error BorrowCFTooLarge();
    error InsufficientReserves();
    error LiquidateCFTooLarge();
    error NoSelfTransfer();
    error NotCollateralized();
    error NotForSale();
    error NotLiquidatable();
    error Paused();
    error SupplyCapExceeded();
    error TimestampTooLarge();
    error TooManyAssets();
    error TooMuchSlippage();
    error TransferInFailed();
    error TransferOutFailed();
    error Unauthorized();

    event Supply(address indexed from, address indexed dst, uint256 amount);
    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Withdraw(address indexed src, address indexed to, uint256 amount);

    event SupplyCollateral(
        address indexed from,
        address indexed dst,
        address indexed asset,
        uint256 amount
    );
    event TransferCollateral(
        address indexed from,
        address indexed to,
        address indexed asset,
        uint256 amount
    );
    event WithdrawCollateral(
        address indexed src,
        address indexed to,
        address indexed asset,
        uint256 amount
    );

    /// @notice Event emitted when a borrow position is absorbed by the protocol
    event AbsorbDebt(
        address indexed absorber,
        address indexed borrower,
        uint256 basePaidOut,
        uint256 usdValue
    );

    /// @notice Event emitted when a user's collateral is absorbed by the protocol
    event AbsorbCollateral(
        address indexed absorber,
        address indexed borrower,
        address indexed asset,
        uint256 collateralAbsorbed,
        uint256 usdValue
    );

    /// @notice Event emitted when a collateral asset is purchased from the protocol
    event BuyCollateral(
        address indexed buyer,
        address indexed asset,
        uint256 baseAmount,
        uint256 collateralAmount
    );

    /// @notice Event emitted when an action is paused/unpaused
    event PauseAction(
        bool supplyPaused,
        bool transferPaused,
        bool withdrawPaused,
        bool absorbPaused,
        bool buyPaused
    );

    /// @notice Event emitted when reserves are withdrawn by the governor
    event WithdrawReserves(address indexed to, uint256 amount);

    function supply(address asset, uint256 amount) external virtual;

    function supplyTo(
        address dst,
        address asset,
        uint256 amount
    ) external virtual;

    function supplyFrom(
        address from,
        address dst,
        address asset,
        uint256 amount
    ) external virtual;

    function transfer(address dst, uint256 amount) external virtual returns (bool);

    function transferFrom(
        address src,
        address dst,
        uint256 amount
    ) external virtual returns (bool);

    function transferAsset(
        address dst,
        address asset,
        uint256 amount
    ) external virtual;

    function transferAssetFrom(
        address src,
        address dst,
        address asset,
        uint256 amount
    ) external virtual;

    function withdraw(address asset, uint256 amount) external virtual;

    function withdrawTo(
        address to,
        address asset,
        uint256 amount
    ) external virtual;

    function withdrawFrom(
        address src,
        address to,
        address asset,
        uint256 amount
    ) external virtual;

    function approveThis(
        address manager,
        address asset,
        uint256 amount
    ) external virtual;

    function withdrawReserves(address to, uint256 amount) external virtual;

    function absorb(address absorber, address[] calldata accounts) external virtual;

    function buyCollateral(
        address asset,
        uint256 minAmount,
        uint256 baseAmount,
        address recipient
    ) external virtual;

    function quoteCollateral(address asset, uint256 baseAmount)
        public
        view
        virtual
        returns (uint256);

    function getAssetInfo(uint8 i) public view virtual returns (AssetInfo memory);

    function getAssetInfoByAddress(address asset) public view virtual returns (AssetInfo memory);

    function getReserves() public view virtual returns (int256);

    function getPrice(address priceFeed) public view virtual returns (uint256);

    function isBorrowCollateralized(address account) public view virtual returns (bool);

    function isLiquidatable(address account) public view virtual returns (bool);

    function totalSupply() external view virtual returns (uint256);

    function totalBorrow() external view virtual returns (uint256);

    function balanceOf(address owner) public view virtual returns (uint256);

    function borrowBalanceOf(address account) public view virtual returns (uint256);

    function pause(
        bool supplyPaused,
        bool transferPaused,
        bool withdrawPaused,
        bool absorbPaused,
        bool buyPaused
    ) external virtual;

    function isSupplyPaused() public view virtual returns (bool);

    function isTransferPaused() public view virtual returns (bool);

    function isWithdrawPaused() public view virtual returns (bool);

    function isAbsorbPaused() public view virtual returns (bool);

    function isBuyPaused() public view virtual returns (bool);

    function accrueAccount(address account) external virtual;

    function getSupplyRate(uint256 utilization) public view virtual returns (uint64);

    function getBorrowRate(uint256 utilization) public view virtual returns (uint64);

    function getUtilization() public view virtual returns (uint256);

    function governor() external view virtual returns (address);

    function pauseGuardian() external view virtual returns (address);

    function baseToken() external view virtual returns (address);

    function baseTokenPriceFeed() external view virtual returns (address);

    function extensionDelegate() external view virtual returns (address);

    /// @dev uint64
    function supplyKink() external view virtual returns (uint256);

    /// @dev uint64
    function supplyPerSecondInterestRateSlopeLow() external view virtual returns (uint256);

    /// @dev uint64
    function supplyPerSecondInterestRateSlopeHigh() external view virtual returns (uint256);

    /// @dev uint64
    function supplyPerSecondInterestRateBase() external view virtual returns (uint256);

    /// @dev uint64
    function borrowKink() external view virtual returns (uint256);

    /// @dev uint64
    function borrowPerSecondInterestRateSlopeLow() external view virtual returns (uint256);

    /// @dev uint64
    function borrowPerSecondInterestRateSlopeHigh() external view virtual returns (uint256);

    /// @dev uint64
    function borrowPerSecondInterestRateBase() external view virtual returns (uint256);

    /// @dev uint64
    function storeFrontPriceFactor() external view virtual returns (uint256);

    /// @dev uint64
    function baseScale() external view virtual returns (uint256);

    /// @dev uint64
    function trackingIndexScale() external view virtual returns (uint256);

    /// @dev uint64
    function baseTrackingSupplySpeed() external view virtual returns (uint256);

    /// @dev uint64
    function baseTrackingBorrowSpeed() external view virtual returns (uint256);

    /// @dev uint104
    function baseMinForRewards() external view virtual returns (uint256);

    /// @dev uint104
    function baseBorrowMin() external view virtual returns (uint256);

    /// @dev uint104
    function targetReserves() external view virtual returns (uint256);

    function numAssets() external view virtual returns (uint8);

    function decimals() external view virtual returns (uint8);

    function initializeStorage() external virtual;
}
ICometRewards.sol 33 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

interface ICometRewards {
    struct RewardConfig {
        address token;
        uint64 rescaleFactor;
        bool shouldUpscale;
        uint256 multiplier;
    }

    struct RewardOwed {
        address token;
        uint256 owed;
    }

    function rewardConfig(address) external view returns (RewardConfig memory);

    function claim(
        address comet,
        address src,
        bool shouldAccrue
    ) external;

    function getRewardOwed(address comet, address account) external returns (RewardOwed memory);

    function claimTo(
        address comet,
        address src,
        address to,
        bool shouldAccrue
    ) external;
}
WrappedERC20.sol 315 lines
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.19;

import "./IWrappedERC20.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This is a "soft-fork" of Open Zeppelin's ERC20 contract but with some notable
 * changes including:
 *
 *   - The allowance system is changed so that users are either allowed or not.
 *   There are no approved/allowed amounts. `approve` function still exists to
 *   adhere to the ERC-20 interface.
 *
 *   - Adds `allow` for easier authorization and is an easier-to-use alternative
 *   to `approve`.
 *
 *   - All hooks are removed except for `_beforeTokenTransfer` in `_transfer`.
 *   This is done to save on gas.
 *
 *   - All reverts use custom errors instead of strings. Another gas-optimization.
 *
 *   - Adds `hasPermission` which works the same as `allowance` and checks whether
 *   a user is authorized to make balance transfers.
 *
 *   - Some state variables are removed in anticipation of this contract
 *   being inherited by the cUSDCv3 wrapper
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract WrappedERC20 is IWrappedERC20 {
    error BadAmount();
    error Unauthorized();
    error ZeroAddress();
    error ExceedsBalance(uint256 amount);

    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => bool)) public isAllowed;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        _transfer(msg.sender, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender)
        public
        view
        virtual
        override
        returns (uint256)
    {
        return hasPermission(owner, spender) ? type(uint256).max : 0;
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        if (spender == address(0)) revert ZeroAddress();
        if (amount == type(uint256).max) {
            _allow(msg.sender, spender, true);
        } else if (amount == 0) {
            _allow(msg.sender, spender, false);
        } else {
            revert BadAmount();
        }
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must be authorized to transfer ``from``'s tokens
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual override returns (bool) {
        if (!hasPermission(from, msg.sender)) revert Unauthorized();
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {
        if (from == address(0)) revert ZeroAddress();
        if (to == address(0)) revert ZeroAddress();

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        if (amount > fromBalance) revert ExceedsBalance(amount);
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        if (account == address(0)) revert ZeroAddress();

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        // untestable:
        //      previously validated, account will not be address(0)
        if (account == address(0)) revert ZeroAddress();

        uint256 accountBalance = _balances[account];
        // untestable:
        //      ammount previously capped to the account balance
        if (amount > accountBalance) revert ExceedsBalance(amount);
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

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

    /**
     * @dev Allow or disallow another address to withdraw, or transfer from the sender.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `manager` cannot be the zero address.
     */
    function allow(address account, bool isAllowed_) external {
        _allow(msg.sender, account, isAllowed_);
    }

    /**
     * @dev Gives `manager` control over the  `owner` s tokens.
     *
     * This internal function is equivalent to `allow`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `manager` cannot be the zero address.
     */
    function _allow(
        address owner,
        address manager,
        bool isAllowed_
    ) internal {
        if (owner == address(0)) revert ZeroAddress();
        if (manager == address(0)) revert ZeroAddress();

        isAllowed[owner][manager] = isAllowed_;
        emit Approval(owner, manager, isAllowed_ ? type(uint256).max : 0);
    }

    /**
     * @dev Determine if the `manager` has permission to act on behalf of the `owner`.
     */
    function hasPermission(address owner, address manager) public view returns (bool) {
        return owner == manager || isAllowed[owner][manager];
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This does not include
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     */
    // solhint-disable no-empty-blocks
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}

Read Contract

BASE_SCALE 0x1f986445 → uint256
EXP_SCALE 0xbbba205d → uint256
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
baseTrackingAccrued 0xab9ba7f4 → uint256
baseTrackingIndex 0x5f2d5f6e → uint64
convertDynamicToStatic 0x43631bfe → uint104
convertStaticToDynamic 0x790add03 → uint256
decimals 0x313ce567 → uint8
exchangeRate 0x3ba0b9a9 → uint256
getRewardOwed 0x2a846398 → uint256
hasPermission 0xcde68041 → bool
isAllowed 0xa1654379 → bool
multiplier 0x1b3ed722 → uint256
name 0x06fdde03 → string
rescaleFactor 0xa628ef09 → uint256
rewardERC20 0xd10b5a5b → address
rewardsAddr 0xb15f67b3 → address
rewardsClaimed 0xd3f730fd → uint256
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
trackingIndexScale 0xaba7f15e → uint256
underlyingBalanceOf 0x935a8b84 → uint256
underlyingComet 0x97008d6c → address

Write Contract 14 functions

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

accrue 0xf8ba4cff
No parameters
accrueAccount 0xbfe69c8d
address account
allow 0x110496e5
address account
bool isAllowed_
approve 0x095ea7b3
address spender
uint256 amount
returns: bool
claimRewards 0x372500ab
No parameters
claimTo 0x34a1ca89
address src
address dst
deposit 0xb6b55f25
uint256 amount
depositFrom 0x68a9674d
address src
address dst
uint256 amount
depositTo 0xffaad6a5
address dst
uint256 amount
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool
withdraw 0x2e1a7d4d
uint256 amount
withdrawFrom 0x9555a942
address src
address dst
uint256 amount
withdrawTo 0x205c2878
address dst
uint256 amount

Recent Transactions

No transactions found for this address