Cryo Explorer Ethereum Mainnet

Address Contract Partially Verified

Address 0xAFB7d6d1e9BcA5B675aDC9b4f52F0CDfDdec9654
Balance 0 ETH
Nonce 1
Code Size 24308 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

24308 bytes
0x6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314612c80575080630892cd8b14612c1d578063095ea7b314612bce578063156e29f6146121ab57806318160ddd1461214f5780631dd19cb414611fb657806323b872dd14611e28578063252c09d714611da657806329910b1114611a475780632c8ce6bc146119895780632f13b60c1461192e578063313ce567146118d357806337d45e3a146116c25780635b709f17146113135780635cbadbe41461127b57806370a082311461121257806372069264146111d7578063794052f314611124578063883bdbfd14610e875780639262187b14610c0c57806395d89b4114610ab9578063a9059cbb146109e5578063a9f8d181146109aa578063c3fb90d614610926578063c45a0155146108b8578063c4f59f9b14610828578063d2c725e0146107e7578063dd62ed3e1461076e578063e184c9be14610716578063e4f8b2e9146106b2578063ea64a82014610629578063f6b911bc146101c45763ffa1ad7414610187575f80fd5b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405160068152f35b5f80fd5b346101c0576101d236612e02565b9160058054906101e860028360f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff809316178155610238336130d3565b30156105a55761024730614aed565b305f526020926003845260405f2054878110610521578790305f52600386520360405f20558087116101c0578254818816828216038281116104f457827fff00000000000000000000000000000000000000000000000000000000000000911691161783555f6040518881527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef863092a36102e130614bb0565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87116101c05786156104ca578382019561031d875189613bd8565b9561032e6040850197885190613df0565b9761034561033d86518c613bd8565b895190613df0565b978915806104c2575b6104985761035d8b8251614ce7565b905261036a888651614ce7565b8552610377898251614ce7565b90525f88126101c0575f87126101c0577f4cf25bc1d991c17529c25213d3cc0cda295eeaad5f13f361969b12ea48015f908860409a61040a7f01000000000000000000000000000000000000000000000000000000000000009773ffffffffffffffffffffffffffffffffffffffff8087169685308903610465575b50508c81891698308a03610432575b505050613527565b8b51908152602081019190915260408101899052606090a38254161790558351928352820152f35b61045d927f0000000000000000000000009bf45ab47747f4b4dd09b3c2c73953484b4eb37516613755565b8e8c81610402565b61049191837f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e16613755565b8f856103f3565b60046040517fccbf5468000000000000000000000000000000000000000000000000000000008152fd5b50881561034e565b60046040517f25daaec3000000000000000000000000000000000000000000000000000000008152fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b608485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05773ffffffffffffffffffffffffffffffffffffffff610675612dbc565b165f908152600260209081526040918290205482516fffffffffffffffffffffffffffffffff8216815260809190911c91810191909152f35b0390f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405169ffffffffffffffffffff7f000000000000000000000000000000000000000000000000002359c5efe693d2168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760206040517f0000000000000000000000000000000000000000000000000000000069cdb1808152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576107a5612dbc565b6107ad612ddf565b9073ffffffffffffffffffffffffffffffffffffffff8091165f52600460205260405f2091165f52602052602060405f2054604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600260055460f81c14604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05761085e613c16565b604051809160208083016020845282518091526020604085019301915f5b82811061088b57505050500390f35b835173ffffffffffffffffffffffffffffffffffffffff168552869550938101939281019260010161087c565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006d247b1c044fa1e22e6b04fa9f71baf99eb29a9f168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760c0600a54600b5461ffff906040519280600f0b845260801d600f0b60208401526bffffffffffffffffffffffff81166040840152818160601c166060840152818160701c16608084015260801c1660a0820152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600154604051908152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0577f0100000000000000000000000000000000000000000000000000000000000000610a3d612dbc565b610aa660055491610a5460028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055560243590336138d2565b6005541617600555602060405160018152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576040515f9060075460018160011c9060018316928315610c02575b6020938484108114610bd557838652908115610b975750600114610b3d575b6106ae84610b3181880382612f4c565b60405191829182612d58565b60075f9081529294507fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6885b828410610b8457505050816106ae93610b319282010193610b21565b8054858501870152928501928101610b68565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b8201019150610b31816106ae610b21565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610b02565b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057610c44612dbc565b90600554906002610c5b60028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80941617600555610cac84614aed565b610cb584614bb0565b610cbd613c16565b93610cc885516146c5565b915f9573ffffffffffffffffffffffffffffffffffffffff96878416905b8251811015610e1a5788610cfa8285614714565b51165f525f875260405f20825f52875260405f205490608091821c610d1f8289614714565b52610d2a8188614714565b51610d3a575b6001915001610ce6565b89610d458286614714565b51165f525f885260405f20835f52885260405f20916fffffffffffffffffffffffffffffffff92838154169055610d7c8289614714565b51908382116101c0578b610d908488614714565b51165f52868a528360405f209216908254901c039283116104f457600192610df391906fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b610e158a610e018387614714565b511687610e0e848b614714565b5191614c41565b610d30565b6106ae867f01000000000000000000000000000000000000000000000000000000000000008a857f78d61a0c27b13f43911095f9f356f14daa3cd8b125eea1aa22421245e90e813d60405180610e708782612fa5565b0390a2600554161760055560405191829182612fa5565b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05767ffffffffffffffff6004358181116101c057366023820112156101c0578060040135610ee381612f8d565b91610ef16040519384612f4c565b8183528483016024819360051b830101913683116101c057602401905b8282106111085750505063ffffffff91600b5461ffff906040519485937f1dd421ce00000000000000000000000000000000000000000000000000000000855260c4850190600c6004870152824216602487015260c060448701525180915260e4850195915f905b8a8383106110eb578a885f81808d8c8c6bffffffffffffffffffffffff81166064850152818160601c16608485015260701c1660a483015203817393be4f30119b22383875218cb7ca1c102a9ec85b5af49182156110e0575f92611036575b505090604051918183928301818452825180915281604085019301915f5b82811061100257505050500390f35b83517affffffffffffffffffffffffffffffffffffffffffffffffffffff1685528695509381019392810192600101610ff3565b9091503d805f843e6110488184612f4c565b82019183818403126101c05780519182116101c057019080601f830112156101c057815161107581612f8d565b926110836040519485612f4c565b818452848085019260051b8201019283116101c0578401905b8282106110ad575050508280610fd5565b81517affffffffffffffffffffffffffffffffffffffffffffffffffffff811681036101c057815290840190840161109c565b6040513d5f823e3d90fd5b845182168952978801978997509093019260019190910190610f76565b813563ffffffff811681036101c0578152908601908601610f0e565b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057610120611166611161612dbc565b6130d3565b6040519080518252602081015160208301526040810151604083015273ffffffffffffffffffffffffffffffffffffffff60608201511660608301526080810151608083015260a081015160a083015260c081015160c083015260e081015160e08301526101008091015190820152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020600854604051908152f35b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602061127361124e612dbc565b73ffffffffffffffffffffffffffffffffffffffff165f52600360205260405f205490565b604051908152f35b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576112b2612dbc565b6112ba612ddf565b73ffffffffffffffffffffffffffffffffffffffff9182165f908152602081815260408083209390941682529182528290205482516fffffffffffffffffffffffffffffffff8216815260809190911c91810191909152f35b346101c05761132136612e62565b91929060055461133760028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555427f0000000000000000000000000000000000000000000000000000000069cdb1801115611698576113b0336130d3565b926113da7f00000000000000000000000031f9e6692e87d81ff8d64de1f475fce6880a030f613491565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9081881190816101c05761141f96611415428b84846141bf565b9891999099613b87565b995f8b126101c0575f8a126101c0575f89126101c05773ffffffffffffffffffffffffffffffffffffffff91828816978d308a03611665575b505061148e8a847f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e169460608701511685613755565b61149784613527565b816115ea575b505060206114aa826136f7565b9201918251905f82126101c057106115a15750506114da670de0b6b3a7640000916114d5888a613045565b613f76565b0415611577576101c05785116101c0576040957f829000a5bc6a12d46e30cdcecd7c56b1efd88f6d7d059da6734a04f3764557c4857f01000000000000000000000000000000000000000000000000000000000000009561156061153d8a613b87565b8b5195865260208601526040850192909252606084015233929081906080820190565b0390a3600554161760055582519182526020820152f35b60046040517f880ccd2c000000000000000000000000000000000000000000000000000000008152fd5b6115aa906136f7565b9051905f82126101c057604491604051917ff62951c700000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5f955090868c116101c0576115fe8c613b87565b333b156101c0578d61163f5f9360405195869485947ffa483e7200000000000000000000000000000000000000000000000000000000865260048601613052565b038183335af180156110e057611656575b8061149d565b61165f90612eef565b8b611650565b61169191857f0000000000000000000000009bf45ab47747f4b4dd09b3c2c73953484b4eb37516613755565b8d8d611458565b60046040517fb2094b59000000000000000000000000000000000000000000000000000000008152fd5b346101c0576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576004359061ffff908183168093036101c0576005549161171860028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055580600b5460801c16604051947f587cdc06000000000000000000000000000000000000000000000000000000008652600c6004870152816024870152604486015282856064817393be4f30119b22383875218cb7ca1c102a9ec85b5af49182156110e0575f9261187d575b7f010000000000000000000000000000000000000000000000000000000000000095508216808203611804575b60058054861687179055005b7fac49e518f90a358f652e4400164f05a5d8f7e35e7747279bc3a93dbf584e125a936040937fffffffffffffffffffffffffffff0000ffffffffffffffffffffffffffffffff71ffff00000000000000000000000000000000600b549260801b16911617600b558351928352820152a1828080806117f8565b91508285813d83116118cc575b6118948183612f4c565b810103126101c0576118c67f0100000000000000000000000000000000000000000000000000000000000000956130a3565b916117cb565b503d61188a565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057602060405160ff7f0000000000000000000000000000000000000000000000000000000000000012168152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576020604051427f0000000000000000000000000000000000000000000000000000000069cdb18011158152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057606060405173ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e168252807f0000000000000000000000009bf45ab47747f4b4dd09b3c2c73953484b4eb3751660208301527f00000000000000000000000031f9e6692e87d81ff8d64de1f475fce6880a030f166040820152f35b346101c057611a5536612e62565b909192600554611a6b60028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555427f0000000000000000000000000000000000000000000000000000000069cdb180111561169857611ae4336130d3565b91611b0e7f00000000000000000000000031f9e6692e87d81ff8d64de1f475fce6880a030f613491565b957f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9687821190816101c057611b4e42611b4785613b87565b83896141bf565b97919990985f8b126101c0575f8a126101c0575f89126101c05773ffffffffffffffffffffffffffffffffffffffff91828816978c308a03611d73575b5050611bc08a84606087015116857f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e16613755565b611bc984613527565b81611cf6575b50507f0000000000000000000000009bf45ab47747f4b4dd09b3c2c73953484b4eb37516611bfc816136f7565b8251905f82126101c05710611cad575050611c24670de0b6b3a7640000916114d5888a613045565b0415611577576101c057611c3790613b87565b9585116101c0576040957f829000a5bc6a12d46e30cdcecd7c56b1efd88f6d7d059da6734a04f3764557c46115607f0100000000000000000000000000000000000000000000000000000000000000958951918291898b3397859094939260609260808301968352602083015260408201520152565b611cb6906136f7565b9051905f82126101c057604491604051917f2c49ea0f00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5f955090611d0387613b87565b908d8d116101c057333b156101c0575f91611d4d8e9260405195869485947ffa483e7200000000000000000000000000000000000000000000000000000000865260048601613052565b038183335af180156110e057611d64575b80611bcf565b611d6d90612eef565b8b611d5e565b611d9f91857f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e16613755565b8d8c611b8b565b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760043561ffff8110156101c057606090600c01546040519063ffffffff811682527affffffffffffffffffffffffffffffffffffffffffffffffffffff8160201c16602083015260f81c15156040820152f35b346101c057611e3636612e02565b60055491611e4a60028460f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094161760055573ffffffffffffffffffffffffffffffffffffffff84165f52600460205260405f20335f5260205260405f20547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103611f14575b5090610aa6917f0100000000000000000000000000000000000000000000000000000000000000946138d2565b9190818310611f58577f010000000000000000000000000000000000000000000000000000000000000094611f4f83610aa695033383613320565b94509091611ee7565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057600554611ff760028260f81c1415612fe0565b7f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80921617600555612048336130d3565b9073ffffffffffffffffffffffffffffffffffffffff91827f0000000000000000000000009bf45ab47747f4b4dd09b3c2c73953484b4eb375169261208c846136f7565b82515f81126101c05761209e91613045565b90807f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e16906120cc826136f7565b956020850151965f88126101c0577f01000000000000000000000000000000000000000000000000000000000000009761210591613045565b9380612137575b50508261211f5760058054861687179055005b606061212e9401511690613755565b828080806117f8565b612148918360608801511690613755565b868061210c565b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05760207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416604051908152f35b346101c05760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0576121e2612dbc565b602435604435917f02000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055461223d60028260f81c1415612fe0565b1617600555427f0000000000000000000000000000000000000000000000000000000069cdb180111561169857612273336130d3565b9061229d7f00000000000000000000000031f9e6692e87d81ff8d64de1f475fce6880a030f613491565b917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff938481116101c0578486116101c0575f9381158015612bc6575b6104ca5760a08301954287511115611698576040840195865180155f14612b295750506123068884613bd8565b905f82126101c0575f6003831115612b1a5750816001908060011c600181018091116104f45791905b848310612aff575050505b81116101c0577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1881019081136001166104f457956103e89297955b5f8813801590612af5575b8015612aeb575b61049857612399896020870151613edc565b60208601526123a9878651613edc565b85526123bf6123b8858a613edc565b8251613edc565b90525f83126101c0575f87126101c0575f88126101c0575f86126101c05782612719575b50505073ffffffffffffffffffffffffffffffffffffffff8083169283156126bb5761240e81614aed565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86116101c057600554907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831601917effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116104f4577fff000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff61253a9416911617600555845f52600360205260405f2061250788825461351a565b9055845f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60206040518b8152a3614bb0565b61254382613527565b807f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e1661256f816136f7565b6020840151905f82126101c0571061266e57507f0000000000000000000000009bf45ab47747f4b4dd09b3c2c73953484b4eb375166125ad816136f7565b8251905f82126101c05710611cad575050906106ae917fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb60405180612605858989846040919493926060820195825260208201520152565b0390a27f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6005541617600555604051938493846040919493926060820195825260208201520152565b602061267a84926136f7565b910151905f82126101c057604491604051917ff62951c700000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b42815111156116985761274061273761277593602087015190613ef7565b91429051613045565b907f0000000000000000000000000000000000000000000000000e500228cc01d1579061276d8387613f89565b90865161401a565b610100830152612783614940565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057600554907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831601917effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116104f45760205f927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef927fff000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6001971691161760055584845260038252604084206128a082825461351a565b9055604051908152a33060011415806128bb575b80806123e3565b6128c4906155be565b60015f52600360205260405f20546040517fe268b3a4000000000000000000000000000000000000000000000000000000008152600160048201526040816024815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000004f30a9d41b80ecc5b94306ab4364951ae3170210165af19081156110e0575f905f92612a92575b506fffffffffffffffffffffffffffffffff16826028810204602814831517156104f4576064602884020491816129fa575b505060207f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d54916001938082105f146129f357505b6129d6816129d1600854875f526009865260405f205490613045565b61351a565b600855835f52600982528060405f2055604051908152a2856128b4565b90506129b5565b90612a3d6fffffffffffffffffffffffffffffffff612a4293167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416613f76565b614010565b90603c820291808304603c14901517156104f457600192612a8a60209260647f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d5495049061351a565b925092612981565b9150506040813d604011612ae3575b81612aae60409383612f4c565b810103126101c0576fffffffffffffffffffffffffffffffff612adc6020612ad584615ea1565b9301615ea1565b919061294f565b3d9150612aa1565b505f871315612387565b505f891315612380565b90919350612b11846129d18184614010565b821c919061232f565b911561233a576001915061233a565b89989250612b3d90612b459295989a613bd8565b855190613df0565b612b50895188613bd8565b96612b616020870198895190613df0565b9081831215612b9b575050612b7c81612b9592999851613bd8565b612b90612b8b8b518093613edc565b613e72565b613df0565b97612375565b985098965050612bc0612bb18998998651613bd8565b612b90612b8b89518093613edc565b95612375565b5086156122d9565b346101c05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c057612c12612c08612dbc565b6024359033613320565b602060405160018152f35b346101c05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c05773ffffffffffffffffffffffffffffffffffffffff612c69612dbc565b165f526009602052602060405f2054604051908152f35b346101c0575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101c0575f9060065460018160011c9060018316928315612d4e575b6020938484108114610bd557838652908115610b975750600114612cf4576106ae84610b3181880382612f4c565b60065f9081529294507ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f5b828410612d3b57505050816106ae93610b319282010193610b21565b8054858501870152928501928101612d1f565b91607f1691612cc6565b6020808252825181830181905293925f5b858110612da8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b818101830151848201604001528201612d69565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101c05773ffffffffffffffffffffffffffffffffffffffff9060043582811681036101c0579160243590811681036101c0579060443590565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126101c05760043573ffffffffffffffffffffffffffffffffffffffff811681036101c057916024359160443567ffffffffffffffff928382116101c057806023830112156101c05781600401359384116101c057602484830101116101c0576024019190565b67ffffffffffffffff8111612f0357604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff821117612f0357604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612f0357604052565b67ffffffffffffffff8111612f035760051b60200190565b60209060206040818301928281528551809452019301915f5b828110612fcc575050505090565b835185529381019392810192600101612fbe565b15612fe757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b919082039182116104f457565b9293806080957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe094601f948752602087015260606040870152816060870152868601375f8582860101520116010190565b519061ffff821682036101c057565b519073ffffffffffffffffffffffffffffffffffffffff821682036101c057565b906040805192610120840184811067ffffffffffffffff821117612f03578252606084015f815260808501905f825260a08601925f845260c08701915f835260e088015f81526101008901965f885289600a5480600f0b825260801d600f0b60208201527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82116101c05782015273ffffffffffffffffffffffffffffffffffffffff91828251957f5c098c11000000000000000000000000000000000000000000000000000000008752306004880152166024860152606085604481867f0000000000000000000000006d247b1c044fa1e22e6b04fa9f71baf99eb29a9f165afa8015613316575f925f965f926132ae575b505060ff16905216905269ffffffffffffffffffff908082166132a957507f000000000000000000000000000000000000000000000000002359c5efe693d25b1690527f00000000000000000000000000000000000000000000000167a2e260c3986b0390527f0000000000000000000000000000000000000000000000000000000069cdb18090526bffffffffffffffffffffffff600b54169052565b61324b565b93509550506060823d60601161330e575b816132cc60609383612f4c565b810103126101c0576132dd826130b2565b9460208301519269ffffffffffffffffffff841684036101c05701519460ff861686036101c057919460ff5f61320b565b3d91506132bf565b82513d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff80911691821561340e571691821561338a5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591835f526004825260405f20855f5282528060405f2055604051908152a3565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fd5b602073ffffffffffffffffffffffffffffffffffffffff60045f9360405194859384927f1d52edc4000000000000000000000000000000000000000000000000000000008452165af19081156110e0575f916134eb575090565b90506020813d602011613512575b8161350660209383612f4c565b810103126101c0575190565b3d91506134f9565b919082018092116104f457565b610100810180516bffffffffffffffffffffffff8082116101c05761355960206135518651614170565b950151614170565b93600b549161ffff956040968751947f4af74b68000000000000000000000000000000000000000000000000000000008652600c6004870152818160601c16602487015263ffffffff421660448701528381166064870152818160701c16608487015260801c1660a4850152868460c4817393be4f30119b22383875218cb7ca1c102a9ec85b5af49182156136ed575f935f93613697575b6dffff0000000000000000000000009495507fffffffffffffffffffffffffffffffff00000000000000000000000000000000809360801b16906fffffffffffffffffffffffffffffffff1617600a556fffff0000000000000000000000000000600b549360701b1695169116179160601b161717600b555190519081527f5c0e21d57bb4cf91d8fe238d6f92e2685a695371b19209afcce6217b478f83e160204292a2565b925092508684813d83116136e6575b6136b08183612f4c565b810103126101c0576dffff000000000000000000000000926136dd60206136d6876130a3565b96016130a3565b928594506135f1565b503d6136a6565b87513d5f823e3d90fd5b602073ffffffffffffffffffffffffffffffffffffffff916024604051809481937f70a08231000000000000000000000000000000000000000000000000000000008352306004840152165afa9081156110e0575f916134eb575090565b906040519060208201937fa9059cbb00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff8092166024840152604483015260448252608082019282841067ffffffffffffffff851117612f0357613815945f9283928660405216936137d786612f30565b602086527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656460a0820152519082855af161380f614be4565b9161599a565b80519081159182156138af575b50501561382b57565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b81925090602091810103126101c0576020015180151581036101c0575f80613822565b91909173ffffffffffffffffffffffffffffffffffffffff808216908115613b03578416928315613a7f57838214613a215761390e8584614b41565b815f52600360205260405f205481811061399d577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208387948694855f52600384520360405f2055845f5260405f2061396982825461351a565b9055604051908152a3300361398e575b5030036139835750565b61398c9061578c565b565b6139979061578c565b5f613979565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f45524332303a207472616e7366657220746f2073656c660000000000000000006044820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b90815f03918083057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14901517156104f457565b90670de0b6b3a7640000918281029281840514901517156104f457565b81810292915f82127f80000000000000000000000000000000000000000000000000000000000000008214166104f45781840514901517156104f457565b6040517fc4f59f9b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff5f826004817f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e85165afa9182156110e0575f92613d55575b507f000000000000000000000000808507121b80c02388fad14726482e061b8da82790613cb68284614755565b613d505782516001808201948583116104f4577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0613d0c613cf688612f8d565b97613d04604051998a612f4c565b808952612f8d565b013660208801375f5b838110613d3057505050613d299084614714565b9116905290565b8085613d3d859385614714565b5116613d49828a614714565b5201613d15565b505090565b9091503d805f833e613d678183612f4c565b81019060209081818403126101c05780519067ffffffffffffffff82116101c057019180601f840112156101c0578251613da081612f8d565b93613dae6040519586612f4c565b818552838086019260051b8201019283116101c0578301905b828210613dd95750505050905f613c89565b838091613de5846130b2565b815201910190613dc7565b8115613e45577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82147f80000000000000000000000000000000000000000000000000000000000000008214166104f4570590565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019182136001166104f457565b90670de0b6b3a76400005f83820393128184128116918413901516176104f457565b81810392915f1380158285131691841216176104f457565b9190915f83820193841291129080158216911516176104f457565b613f37670de0b6b3a7640000915f84125f14613f6b576114d57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff946147a5565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613f6891613bd8565b90565b6114d56001946147a5565b818102929181159184041417156104f457565b60800151906301e13380918281029281840514901517156104f4577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613fd491613df0565b905f821315613fdf57565b602482604051907f1ca418760000000000000000000000000000000000000000000000000000000082526004820152fd5b8115613e45570490565b5f81126141125761403761403161403c9383613edc565b91613bbb565b6147f6565b670d529ae9e86000008082136140db575061406b929161403761406161406693614cf8565b613bbb565b613edc565b670de0b6b3a764000081126140aa5761408390614800565b905f82126101c0576301e13380918281029281840414901517156104f457613f6891614010565b602490604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b60449250604051917ffc68d09e00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600860248201527f6e656761746976650000000000000000000000000000000000000000000000006044820152fd5b807fffffffffffffffffffffffffffffffff800000000000000000000000000000001315806141a5575b156101c057600f0b90565b506f7fffffffffffffffffffffffffffffff81131561419a565b939060a085019484865111156116985780518481131561468e5750604051916080830183811067ffffffffffffffff821117612f03576040525f83525f60208401525f60408401525f6060840152858751111561169857614221868851613045565b61422b8184613f89565b845261423b602084015183613ef7565b80602086015283519081158015614686575b61464f57505082516101008401516020860151916301e1338061427285895194613f76565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c0576142a390615ac7565b92670de0b6b3a7640000841261461e57926142e06301e1338095936140376140616142db866140376140316142f59b6142e69a613edc565b614cf8565b90613ec4565b604087015260c0850151613f76565b047f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c05761432690615ac7565b6060840152815160208401519061435585519161403761403160408901519561434f8c85614ce7565b93613edc565b670d529ae9e86000008082136140db575061437a929161403761406161406693614cf8565b94670de0b6b3a764000086126145ed5761439f61439a8761403784613bbb565b613b87565b606085015190965f8313156145cd57816140376143bb92613bbb565b670de0b6b3a764000081126140aa57506143e66143e0670de0b6b3a764000092613ea2565b88613bd8565b05915b60e08401517f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c05783606461442561442c9383613bd8565b0598613ec4565b945f861280156145a35715614598576144657fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff966147a5565b670de0b6b3a7640000810290808204670de0b6b3a764000014901517156104f457826144909161351a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019081116104f457826144c491614010565b957f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87116101c0576145476145629561453061452861451e61451761450f6101009d61455598613bd8565b945b89615093565b9d88615093565b9c9d839f51613045565b968951614ce7565b88526145418b60208a015192613edc565b90614ce7565b806020880152865193613ef7565b604082519201519261401a565b91829101521561456e57565b60046040517faa69ae1e000000000000000000000000000000000000000000000000000000008152fd5b6144656001966147a5565b506145556145476145629561453061452861451e6145176145c76101009d8a615093565b94614511565b5061439a81612b906145e16145e794613ea2565b8a613bd8565b916143e9565b602486604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b602484604051907fca78c8a40000000000000000000000000000000000000000000000000000000082526004820152fd5b60449250604051917fb1c4aefb00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b50801561424d565b60449085604051917f8615426c00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b906146cf82612f8d565b6146dc6040519182612f4c565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061470a8294612f8d565b0190602036910137565b80518210156147285760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b908151915f5b83811061476a57505050505f90565b73ffffffffffffffffffffffffffffffffffffffff8061478a8385614714565b51169084161461479c5760010161475b565b50505050600190565b5f8113156147b05790565b7f800000000000000000000000000000000000000000000000000000000000000081146104f4575f0390565b8015613e45576ec097ce7bc90715b34b9f10000000000590565b8115613e45570590565b5f8113156148e25780670c7d713b49da000012806148d1575b156148c857670de0b6b3a7640000906ec097ce7bc90715b34b9f10000000009061486c908302828101907fffffffffffffffffffffffffffffffffff3f68318436f8ea4cb460f0000000000183026147f6565b9080828002059181838202058284820205838582020591848684020593858786020595808888020597880205600f900596600d900595600b900594600990059360079005926005900591600390050101010101010160011b0590565b613f6890614d49565b50670f43fc2c04ee00008112614819565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f6f7574206f6620626f756e6473000000000000000000000000000000000000006044820152fd5b614948615124565b815115614ae957600191308314159182614963575b50505050565b61496d84936155be565b825f52602060098152604090815f2054935f955b61498e575b50505061495d565b8051861015614ae45773ffffffffffffffffffffffffffffffffffffffff6149b68783614714565b5116956149c38186614714565b5190875f525f8452845f20895f5284526fffffffffffffffffffffffffffffffff9182865f2054168015614add575b8a8282148015614ad5575b614ac95750670de0b6b3a7640000614a21614a1b614a3c9385613045565b8b613f76565b048a5f525f8752875f208c5f528752875f205460801c61351a565b908381116101c0578382116101c0578a9984614ac2938c968a5194614a6086612f30565b16845216878301525f525f8652865f20845f528652865f20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b0195614981565b99505050879150614ac2565b5082156149fd565b50896149f2565b614986565b5050565b614af5615124565b90805115614b3c5773ffffffffffffffffffffffffffffffffffffffff83168015159081614b31575b50614b2857505050565b61398c926155f2565b90503014155f614b1e565b505050565b614b49615124565b9181511561495d5773ffffffffffffffffffffffffffffffffffffffff9083838383168015159081614ba5575b50614b94575b50505083168015159081614b315750614b2857505050565b614b9d926155f2565b5f8383614b7c565b90503014155f614b76565b73ffffffffffffffffffffffffffffffffffffffff81168015159081614bd9575b506139835750565b90503014155f614bd1565b3d15614c3c573d9067ffffffffffffffff8211612f035760405191614c3160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184612f4c565b82523d5f602084013e565b606090565b9190918115614b3c5773ffffffffffffffffffffffffffffffffffffffff169182614cde575f809350809281925af1614c78614be4565b5015614c8057565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f6574682073656e64206661696c656400000000000000000000000000000000006044820152fd5b61398c92613755565b9080821261411257613f6891613ec4565b670de0b6b3a76400008114614d1f57614d1a81614037614031613f6894613ea2565b614800565b60046040517fa9c8b14d000000000000000000000000000000000000000000000000000000008152fd5b670de0b6b3a7640000811261507d576064905f7e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c0000000000000821215615052575b73011798004d755d3c8bc8e03204cf44619e000000821215615031575b820290808302906e01855144814a7ff805980ff0084000908183121561500e575b50506b02df0ab5a80a22c61ab5a70080821215614fee575b50693f1fce3da636ea5cf85080821215614fce575b50690127fa27722cc06cc5e280821215614fae575b5068280e60114edb805d0380821215614f8e575b50680ebc5fb4174612111080821215614f77575b506808f00f760a4b2db55d80821215614f57575b506806f5f177578893793780821215614f37575b506806248f33704b28660380821215614f18575b506805c548670b9510e7ac80821215614ef9575b50614eb868056bc75e2d6310000091827ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf000008183019201026147f6565b9080828002059181838202058284820205916003600560076009600b888a89020598808b8b02059a8b0205059805960594059205010101010160011b010590565b68056bc75e2d631000006756bc75e2d63100009202059101905f614e7c565b68056bc75e2d6310000067ad78ebc5ac6200009202059101905f614e68565b68056bc75e2d6310000068015af1d78b58c400009202059101905f614e54565b68056bc75e2d631000006802b5e3af16b18800009202059101905f614e40565b68056bc75e2d63100000809202059101905f614e2c565b68056bc75e2d63100000680ad78ebc5ac62000009202059101905f614e18565b68056bc75e2d631000006815af1d78b58c4000009202059101905f614e04565b68056bc75e2d63100000682b5e3af16b188000009202059101905f614def565b68056bc75e2d631000006856bc75e2d6310000009202059101905f614dda565b68ad78ebc5ac62000000925069021e19e0c9bab240000002059101905f80614dc2565b906b1425982cf597cd205cef73806803782dace9d900000091059101614da1565b50770195e54c5dd42177f53a27172fa9ec63026282700000000090056806f05b59d3b2000000614d84565b61508961508e916147dc565b614d49565b5f0390565b5f821215615119576150c57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff926147a5565b90670de0b6b3a7640000918281029281840414901517156104f4576150e991614010565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101c057613f6891613bd8565b6150c56001926147a5565b61512c613c16565b9061513782516146c5565b918051925f93156155b857600154431461555357436001556008546040517fef5cfb8c0000000000000000000000000000000000000000000000000000000081523060048201525f816024818373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c9bfebc79a722c05dc34bd2a227ef2db19fd1b8e165af180156110e0576154c9575b5073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000047d74516b33ed5d70dde7119a40839f6fcc24e5716803b156101c0575f80916004604051809481937f58f3d1630000000000000000000000000000000000000000000000000000000083525af180156110e0576154b6575b50845b83518110156154ae5773ffffffffffffffffffffffffffffffffffffffff61526e8286614714565b5116908187526002602052604087206040519061528a82612f30565b54906fffffffffffffffffffffffffffffffff8216815260208260801c9101526fffffffffffffffffffffffffffffffff81169073ffffffffffffffffffffffffffffffffffffffff6152dd8489614714565b51168061542157506152f4475b8260801c90613045565b6fffffffffffffffffffffffffffffffff821615615418575b856153e4575b6fffffffffffffffffffffffffffffffff83116101c0576153369160801c61351a565b926fffffffffffffffffffffffffffffffff84116101c0576001936153d3916fffffffffffffffffffffffffffffffff6040519261537384612f30565b81861684521660208301528a52600260205260408a20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b6153dd8286614714565b5201615246565b91670de0b6b3a7640000908184029184830414841517156104f45761540c8761541293614010565b9061351a565b91615313565b6001925061530d565b6020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa80156154a3578a9061546f575b6152f491506152ea565b506020813d60201161549b575b8161548960209383612f4c565b810103126101c0576152f49051615465565b3d915061547c565b6040513d8c823e3d90fd5b505091909250565b6154c1919550612eef565b5f935f615243565b3d805f833e6154d88183612f4c565b810160209182818303126101c05780519067ffffffffffffffff82116101c0570181601f820112156101c057805190838061551284612f8d565b61551f6040519182612f4c565b848152019260051b8201019283116101c0578301905b828210615544575050506151c8565b81518152908301908301615535565b9092505f5b83518110156155b4578073ffffffffffffffffffffffffffffffffffffffff61558360019387614714565b51165f5260026020526fffffffffffffffffffffffffffffffff60405f2054166155ad8285614714565b5201615558565b5090565b92509190565b156155c557565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9291909273ffffffffffffffffffffffffffffffffffffffff8091169081151580615782575b615621906155be565b815f5260209160098352604091825f2054965f5b815181101561577757826156498284614714565b5116906156568189614714565b5191805f525f8852865f20865f5288526fffffffffffffffffffffffffffffffff928b84895f205416801561576f575b8281148015615767575b61575a576156b46156cf926156ae670de0b6b3a76400009386613045565b90613f76565b04835f525f8b52895f20895f528b52895f205460801c61351a565b918482116101c0578483116101c05784615754936001968b51946156f286612f30565b168452168a8301525f525f8952875f20875f528952875f20815160209092015160801b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff92909216919091179055565b01615635565b5050505060019150615754565b508215615690565b506001615686565b505050505050509050565b5030821415615618565b73ffffffffffffffffffffffffffffffffffffffff5f6157bb8284169384151580615990575b61124e906155be565b6040809360248251809581937fe268b3a40000000000000000000000000000000000000000000000000000000083528960048401527f0000000000000000000000004f30a9d41b80ecc5b94306ab4364951ae3170210165af1918215615986575f905f93615949575b506fffffffffffffffffffffffffffffffff8091169060288302838104602814841517156104f4576064900493826158c5575b505050916020917f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d54938082105f146158bd5750905b6158a7826129d1600854885f5260098752845f205490613045565b600855845f526009835281815f205551908152a2565b90509061588c565b6158fb9291612a3d91969496167effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60055416613f76565b92603c840293808504603c14901517156104f45761594060209360647f15db480654d6b3ac10d36677c6f855f2c8b1322d2c53afc972b502a130371d5496049061351a565b92819294615857565b809350848092503d831161597f575b6159628183612f4c565b810103126101c0576159786020612ad584615ea1565b915f615824565b503d615958565b83513d5f823e3d90fd5b50308514156157b2565b91929015615a1557508151156159ae575090565b3b156159b75790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b825190915015615a285750805190602001fd5b615a5e906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301612d58565b0390fd5b15615a6957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f496e76616c6964206578706f6e656e74000000000000000000000000000000006044820152fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffdc702bd3a30fc000081121580615e8e575b615afa90615a62565b5f8112615e7a576064906806f05b59d3b20000008112615e17577ffffffffffffffffffffffffffffffffffffffffffffffff90fa4a62c4e0000000168056bc75e2d6310000082770195e54c5dd42177f53a27172fa9ec630262827000000000925b02819068ad78ebc5ac62000000811215615dde575b6856bc75e2d631000000811215615da4575b682b5e3af16b18800000811215615d6c575b6815af1d78b58c400000811215615d34575b680ad78ebc5ac6200000811215615cfd575b82811215615cc6575b6802b5e3af16b1880000811215615c8f575b68015af1d78b58c40000811215615c58575b60028382800205058360038184840205056004828583020505600583868302050560068487830205056007858883020505906008868984020505926009878a8602050594600a888b8802050596600b898c8a02050599600c8a8d8d0205059b0101010101010101010101010205020590565b6806f5f17757889379377ffffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c000084920192020590615be6565b6808f00f760a4b2db55d7ffffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e78000084920192020590615bd4565b680ebc5fb417461211107ffffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf0000084920192020590615bc2565b68280e60114edb805d037ffffffffffffffffffffffffffffffffffffffffffffffff5287143a539e0000084920192020590615bb9565b690127fa27722cc06cc5e27fffffffffffffffffffffffffffffffffffffffffffffffea50e2874a73c0000084920192020590615ba7565b693f1fce3da636ea5cf8507fffffffffffffffffffffffffffffffffffffffffffffffd4a1c50e94e780000084920192020590615b95565b6b02df0ab5a80a22c61ab5a7007fffffffffffffffffffffffffffffffffffffffffffffffa9438a1d29cf00000084920192020590615b83565b6e01855144814a7ff805980ff008400091507fffffffffffffffffffffffffffffffffffffffffffffff5287143a539e00000001615b71565b6803782dace9d90000008112615e67577ffffffffffffffffffffffffffffffffffffffffffffffffc87d25316270000000168056bc75e2d63100000826b1425982cf597cd205cef738092615b5c565b68056bc75e2d6310000082600192615b5c565b615e85905f03615ac7565b613f68906147dc565b5068070c1cc73b00c80000811315615af1565b51906fffffffffffffffffffffffffffffffff821682036101c05756fea2646970667358221220057ba17c06d00bc766723abb82a4a8e82f42e80ca1820c4ccbe78d47ab45bba264736f6c63430008180033

Verified Source Code Partial Match

Compiler: v0.8.24+commit.e11b9ed9 EVM: shanghai Optimization: Yes (1000000 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 60 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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.
 */
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].
     */
    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);
        }
    }
}
Context.sol 24 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
PendleERC20.sol 331 lines
// SPDX-License-Identifier: GPL-3.0-or-later
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

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

/**
 * @dev Pendle's ERC20 implementation, modified from @openzeppelin implementation
 * Changes are:
 * - comes with built-in reentrancy protection, storage-packed with totalSupply variable
 * - delete increaseAllowance / decreaseAllowance
 * - add nonReentrancy protection to transfer / transferFrom functions
 * - allow decimals to be passed in
 * - block self-transfer by default
 */
// solhint-disable
contract PendleERC20 is Context, IERC20, IERC20Metadata {
    uint8 private constant _NOT_ENTERED = 1;
    uint8 private constant _ENTERED = 2;

    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint248 private _totalSupply;
    uint8 private _status;

    string private _name;
    string private _symbol;
    uint8 public immutable decimals;

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    function reentrancyGuardEntered() external view virtual returns (bool) {
        return _reentrancyGuardEntered();
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }

    /**
     * @dev Sets the values for {name}, {symbol} and {decimals}.
     *
     * All three of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_, uint8 decimals_) {
        _name = name_;
        _symbol = symbol_;
        decimals = decimals_;
        _status = _NOT_ENTERED;
    }

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

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

    /**
     * @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) external virtual override nonReentrant returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @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) external virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        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 have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external virtual override nonReentrant returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * 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 {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");
        require(from != to, "ERC20: transfer to self");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
        }
        _balances[to] += amount;

        emit Transfer(from, to, amount);

        _afterTokenTransfer(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 {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

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

        _afterTokenTransfer(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 {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= toUint248(amount);

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

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, 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.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * 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.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    function toUint248(uint256 x) internal virtual returns (uint248) {
        require(x <= type(uint248).max); // signed, lim = bit-1
        return uint248(x);
    }
}
ArrayLib.sol 150 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

library ArrayLib {
    function sum(uint256[] memory input) internal pure returns (uint256) {
        uint256 value = 0;
        for (uint256 i = 0; i < input.length; ) {
            value += input[i];
            unchecked {
                i++;
            }
        }
        return value;
    }

    /// @notice return index of the element if found, else return uint256.max
    function find(address[] memory array, address element) internal pure returns (uint256 index) {
        uint256 length = array.length;
        for (uint256 i = 0; i < length; ) {
            if (array[i] == element) return i;
            unchecked {
                i++;
            }
        }
        return type(uint256).max;
    }

    function append(address[] memory inp, address element) internal pure returns (address[] memory out) {
        uint256 length = inp.length;
        out = new address[](length + 1);
        for (uint256 i = 0; i < length; ) {
            out[i] = inp[i];
            unchecked {
                i++;
            }
        }
        out[length] = element;
    }

    function appendHead(address[] memory inp, address element) internal pure returns (address[] memory out) {
        uint256 length = inp.length;
        out = new address[](length + 1);
        out[0] = element;
        for (uint256 i = 1; i <= length; ) {
            out[i] = inp[i - 1];
            unchecked {
                i++;
            }
        }
    }

    /**
     * @dev This function assumes a and b each contains unidentical elements
     * @param a array of addresses a
     * @param b array of addresses b
     * @return out Concatenation of a and b containing unidentical elements
     */
    function merge(address[] memory a, address[] memory b) internal pure returns (address[] memory out) {
        unchecked {
            uint256 countUnidenticalB = 0;
            bool[] memory isUnidentical = new bool[](b.length);
            for (uint256 i = 0; i < b.length; ++i) {
                if (!contains(a, b[i])) {
                    countUnidenticalB++;
                    isUnidentical[i] = true;
                }
            }

            out = new address[](a.length + countUnidenticalB);
            for (uint256 i = 0; i < a.length; ++i) {
                out[i] = a[i];
            }
            uint256 id = a.length;
            for (uint256 i = 0; i < b.length; ++i) {
                if (isUnidentical[i]) {
                    out[id++] = b[i];
                }
            }
        }
    }

    // various version of contains
    function contains(address[] memory array, address element) internal pure returns (bool) {
        uint256 length = array.length;
        for (uint256 i = 0; i < length; ) {
            if (array[i] == element) return true;
            unchecked {
                i++;
            }
        }
        return false;
    }

    function contains(bytes4[] memory array, bytes4 element) internal pure returns (bool) {
        uint256 length = array.length;
        for (uint256 i = 0; i < length; ) {
            if (array[i] == element) return true;
            unchecked {
                i++;
            }
        }
        return false;
    }

    function create(address a) internal pure returns (address[] memory res) {
        res = new address[](1);
        res[0] = a;
    }

    function create(address a, address b) internal pure returns (address[] memory res) {
        res = new address[](2);
        res[0] = a;
        res[1] = b;
    }

    function create(address a, address b, address c) internal pure returns (address[] memory res) {
        res = new address[](3);
        res[0] = a;
        res[1] = b;
        res[2] = c;
    }

    function create(address a, address b, address c, address d) internal pure returns (address[] memory res) {
        res = new address[](4);
        res[0] = a;
        res[1] = b;
        res[2] = c;
        res[3] = d;
    }

    function create(
        address a,
        address b,
        address c,
        address d,
        address e
    ) internal pure returns (address[] memory res) {
        res = new address[](5);
        res[0] = a;
        res[1] = b;
        res[2] = c;
        res[3] = d;
        res[4] = e;
    }

    function create(uint256 a) internal pure returns (uint256[] memory res) {
        res = new uint256[](1);
        res[0] = a;
    }
}
Errors.sol 190 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

library Errors {
    // BulkSeller
    error BulkInsufficientSyForTrade(uint256 currentAmount, uint256 requiredAmount);
    error BulkInsufficientTokenForTrade(uint256 currentAmount, uint256 requiredAmount);
    error BulkInSufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
    error BulkInSufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
    error BulkInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
    error BulkNotMaintainer();
    error BulkNotAdmin();
    error BulkSellerAlreadyExisted(address token, address SY, address bulk);
    error BulkSellerInvalidToken(address token, address SY);
    error BulkBadRateTokenToSy(uint256 actualRate, uint256 currentRate, uint256 eps);
    error BulkBadRateSyToToken(uint256 actualRate, uint256 currentRate, uint256 eps);

    // APPROX
    error ApproxFail();
    error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps);
    error ApproxBinarySearchInputInvalid(
        uint256 approxGuessMin,
        uint256 approxGuessMax,
        uint256 minGuessMin,
        uint256 maxGuessMax
    );

    // MARKET + MARKET MATH CORE
    error MarketExpired();
    error MarketZeroAmountsInput();
    error MarketZeroAmountsOutput();
    error MarketZeroLnImpliedRate();
    error MarketZeroNetLPFee();
    error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount);
    error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance);
    error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
    error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset);
    error MarketExchangeRateBelowOne(int256 exchangeRate);
    error MarketProportionMustNotEqualOne();
    error MarketRateScalarBelowZero(int256 rateScalar);
    error MarketScalarRootBelowZero(int256 scalarRoot);
    error MarketProportionTooHigh(int256 proportion, int256 maxProportion);

    error OracleUninitialized();
    error OracleTargetTooOld(uint32 target, uint32 oldest);
    error OracleZeroCardinality();

    error MarketFactoryExpiredPt();
    error MarketFactoryInvalidPt();
    error MarketFactoryMarketExists();

    error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot);
    error MarketFactoryOverriddenFeeTooHigh(uint80 overriddenFee, uint256 marketLnFeeRateRoot);
    error MarketFactoryReserveFeePercentTooHigh(uint8 reserveFeePercent, uint8 maxReserveFeePercent);
    error MarketFactoryZeroTreasury();
    error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor);
    error MFNotPendleMarket(address addr);

    // ROUTER
    error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut);
    error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
    error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut);
    error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut);
    error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut);
    error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
    error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay);
    error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay);
    error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed);

    error RouterTimeRangeZero();
    error RouterCallbackNotPendleMarket(address caller);
    error RouterInvalidAction(bytes4 selector);
    error RouterInvalidFacet(address facet);

    error RouterKyberSwapDataZero();

    error SimulationResults(bool success, bytes res);

    // YIELD CONTRACT
    error YCExpired();
    error YCNotExpired();
    error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy);
    error YCNothingToRedeem();
    error YCPostExpiryDataNotSet();
    error YCNoFloatingSy();

    // YieldFactory
    error YCFactoryInvalidExpiry();
    error YCFactoryYieldContractExisted();
    error YCFactoryZeroExpiryDivisor();
    error YCFactoryZeroTreasury();
    error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate);
    error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate);

    // SY
    error SYInvalidTokenIn(address token);
    error SYInvalidTokenOut(address token);
    error SYZeroDeposit();
    error SYZeroRedeem();
    error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut);
    error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);

    // SY-specific
    error SYQiTokenMintFailed(uint256 errCode);
    error SYQiTokenRedeemFailed(uint256 errCode);
    error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1);
    error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax);

    error SYCurveInvalidPid();
    error SYCurve3crvPoolNotFound();

    error SYApeDepositAmountTooSmall(uint256 amountDeposited);
    error SYBalancerInvalidPid();
    error SYInvalidRewardToken(address token);

    error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable);

    error SYBalancerReentrancy();

    error NotFromTrustedRemote(uint16 srcChainId, bytes path);

    error ApxETHNotEnoughBuffer();

    // Liquidity Mining
    error VCInvalidCap(uint256 cap);
    error VCInactivePool(address pool);
    error VCPoolAlreadyActive(address pool);
    error VCZeroVePendle(address user);
    error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight);
    error VCEpochNotFinalized(uint256 wTime);
    error VCPoolAlreadyAddAndRemoved(address pool);

    error VEInvalidNewExpiry(uint256 newExpiry);
    error VEExceededMaxLockTime();
    error VEInsufficientLockTime();
    error VENotAllowedReduceExpiry();
    error VEZeroAmountLocked();
    error VEPositionNotExpired();
    error VEZeroPosition();
    error VEZeroSlope(uint128 bias, uint128 slope);
    error VEReceiveOldSupply(uint256 msgTime);

    error GCNotPendleMarket(address caller);
    error GCNotVotingController(address caller);

    error InvalidWTime(uint256 wTime);
    error ExpiryInThePast(uint256 expiry);
    error ChainNotSupported(uint256 chainId);

    error FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount);
    error FDEpochLengthMismatch();
    error FDInvalidPool(address pool);
    error FDPoolAlreadyExists(address pool);
    error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch);
    error FDInvalidStartEpoch(uint256 startEpoch);
    error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime);
    error FDFutureFunding(uint256 lastFunded, uint256 currentWTime);

    error BDInvalidEpoch(uint256 epoch, uint256 startTime);

    // Cross-Chain
    error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path);
    error MsgNotFromReceiveEndpoint(address sender);
    error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee);
    error ApproxDstExecutionGasNotSet();
    error InvalidRetryData();

    // GENERIC MSG
    error ArrayLengthMismatch();
    error ArrayEmpty();
    error ArrayOutOfBounds();
    error ZeroAddress();
    error FailedToSendEther();
    error InvalidMerkleProof();

    error OnlyLayerZeroEndpoint();
    error OnlyYT();
    error OnlyYCFactory();
    error OnlyWhitelisted();

    // Swap Aggregator
    error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual);
    error UnsupportedSelector(uint256 aggregatorType, bytes4 selector);

    // Cross Chain Oracle App
    error FeedNotInitialized();
    error ExchangeRateCallFailed();
    error InvalidDestinationEid();
    error InvalidMsgType();
}
LogExpMath.sol 495 lines
// SPDX-License-Identifier: GPL-3.0-or-later
// Permission is hereby granted, free of charge, 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.

// 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;

/* solhint-disable */

/**
 * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).
 *
 * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural
 * exponentiation and logarithm (where the base is Euler's number).
 *
 * @author Fernando Martinelli - @fernandomartinelli
 * @author Sergio Yuhjtman - @sergioyuhjtman
 * @author Daniel Fernandez - @dmf7z
 */
library LogExpMath {
    // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying
    // two numbers, and multiply by ONE when dividing them.

    // All arguments and return values are 18 decimal fixed point numbers.
    int256 constant ONE_18 = 1e18;

    // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the
    // case of ln36, 36 decimals.
    int256 constant ONE_20 = 1e20;
    int256 constant ONE_36 = 1e36;

    // The domain of natural exponentiation is bound by the word size and number of decimals used.
    //
    // Because internally the result will be stored using 20 decimals, the largest possible result is
    // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.
    // The smallest possible result is 10^(-18), which makes largest negative argument
    // ln(10^(-18)) = -41.446531673892822312.
    // We use 130.0 and -41.0 to have some safety margin.
    int256 constant MAX_NATURAL_EXPONENT = 130e18;
    int256 constant MIN_NATURAL_EXPONENT = -41e18;

    // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point
    // 256 bit integer.
    int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;
    int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;

    uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20);

    // 18 decimal constants
    int256 constant x0 = 128000000000000000000; // 2ˆ7
    int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals)
    int256 constant x1 = 64000000000000000000; // 2ˆ6
    int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals)

    // 20 decimal constants
    int256 constant x2 = 3200000000000000000000; // 2ˆ5
    int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2)
    int256 constant x3 = 1600000000000000000000; // 2ˆ4
    int256 constant a3 = 888611052050787263676000000; // eˆ(x3)
    int256 constant x4 = 800000000000000000000; // 2ˆ3
    int256 constant a4 = 298095798704172827474000; // eˆ(x4)
    int256 constant x5 = 400000000000000000000; // 2ˆ2
    int256 constant a5 = 5459815003314423907810; // eˆ(x5)
    int256 constant x6 = 200000000000000000000; // 2ˆ1
    int256 constant a6 = 738905609893065022723; // eˆ(x6)
    int256 constant x7 = 100000000000000000000; // 2ˆ0
    int256 constant a7 = 271828182845904523536; // eˆ(x7)
    int256 constant x8 = 50000000000000000000; // 2ˆ-1
    int256 constant a8 = 164872127070012814685; // eˆ(x8)
    int256 constant x9 = 25000000000000000000; // 2ˆ-2
    int256 constant a9 = 128402541668774148407; // eˆ(x9)
    int256 constant x10 = 12500000000000000000; // 2ˆ-3
    int256 constant a10 = 113314845306682631683; // eˆ(x10)
    int256 constant x11 = 6250000000000000000; // 2ˆ-4
    int256 constant a11 = 106449445891785942956; // eˆ(x11)

    /**
     * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.
     *
     * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function exp(int256 x) internal pure returns (int256) {
        unchecked {
            require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, "Invalid exponent");

            if (x < 0) {
                // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it
                // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT).
                // Fixed point division requires multiplying by ONE_18.
                return ((ONE_18 * ONE_18) / exp(-x));
            }

            // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,
            // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7
            // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the
            // decomposition.
            // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this
            // decomposition, which will be lower than the smallest x_n.
            // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.
            // We mutate x by subtracting x_n, making it the remainder of the decomposition.

            // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause
            // intermediate overflows. Instead we store them as plain integers, with 0 decimals.
            // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the
            // decomposition.

            // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct
            // it and compute the accumulated product.

            int256 firstAN;
            if (x >= x0) {
                x -= x0;
                firstAN = a0;
            } else if (x >= x1) {
                x -= x1;
                firstAN = a1;
            } else {
                firstAN = 1; // One with no decimal places
            }

            // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the
            // smaller terms.
            x *= 100;

            // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point
            // one. Recall that fixed point multiplication requires dividing by ONE_20.
            int256 product = ONE_20;

            if (x >= x2) {
                x -= x2;
                product = (product * a2) / ONE_20;
            }
            if (x >= x3) {
                x -= x3;
                product = (product * a3) / ONE_20;
            }
            if (x >= x4) {
                x -= x4;
                product = (product * a4) / ONE_20;
            }
            if (x >= x5) {
                x -= x5;
                product = (product * a5) / ONE_20;
            }
            if (x >= x6) {
                x -= x6;
                product = (product * a6) / ONE_20;
            }
            if (x >= x7) {
                x -= x7;
                product = (product * a7) / ONE_20;
            }
            if (x >= x8) {
                x -= x8;
                product = (product * a8) / ONE_20;
            }
            if (x >= x9) {
                x -= x9;
                product = (product * a9) / ONE_20;
            }

            // x10 and x11 are unnecessary here since we have high enough precision already.

            // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series
            // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).

            int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.
            int256 term; // Each term in the sum, where the nth term is (x^n / n!).

            // The first term is simply x.
            term = x;
            seriesSum += term;

            // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,
            // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.

            term = ((term * x) / ONE_20) / 2;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 3;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 4;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 5;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 6;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 7;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 8;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 9;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 10;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 11;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 12;
            seriesSum += term;

            // 12 Taylor terms are sufficient for 18 decimal precision.

            // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor
            // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply
            // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),
            // and then drop two digits to return an 18 decimal value.

            return (((product * seriesSum) / ONE_20) * firstAN) / 100;
        }
    }

    /**
     * @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
     */
    function ln(int256 a) internal pure returns (int256) {
        unchecked {
            // The real natural logarithm is not defined for negative numbers or zero.
            require(a > 0, "out of bounds");
            if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) {
                return _ln_36(a) / ONE_18;
            } else {
                return _ln(a);
            }
        }
    }

    /**
     * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.
     *
     * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function pow(uint256 x, uint256 y) internal pure returns (uint256) {
        unchecked {
            if (y == 0) {
                // We solve the 0^0 indetermination by making it equal one.
                return uint256(ONE_18);
            }

            if (x == 0) {
                return 0;
            }

            // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to
            // arrive at that r`esult. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means
            // x^y = exp(y * ln(x)).

            // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.
            require(x < 2 ** 255, "x out of bounds");
            int256 x_int256 = int256(x);

            // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In
            // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.

            // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.
            require(y < MILD_EXPONENT_BOUND, "y out of bounds");
            int256 y_int256 = int256(y);

            int256 logx_times_y;
            if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {
                int256 ln_36_x = _ln_36(x_int256);

                // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just
                // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal
                // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the
                // (downscaled) last 18 decimals.
                logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18);
            } else {
                logx_times_y = _ln(x_int256) * y_int256;
            }
            logx_times_y /= ONE_18;

            // Finally, we compute exp(y * ln(x)) to arrive at x^y
            require(
                MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT,
                "product out of bounds"
            );

            return uint256(exp(logx_times_y));
        }
    }

    /**
     * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
     */
    function _ln(int256 a) private pure returns (int256) {
        unchecked {
            if (a < ONE_18) {
                // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less
                // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call.
                // Fixed point division requires multiplying by ONE_18.
                return (-_ln((ONE_18 * ONE_18) / a));
            }

            // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which
            // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,
            // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot
            // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.
            // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this
            // decomposition, which will be lower than the smallest a_n.
            // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.
            // We mutate a by subtracting a_n, making it the remainder of the decomposition.

            // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point
            // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by
            // ONE_18 to convert them to fixed point.
            // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide
            // by it and compute the accumulated sum.

            int256 sum = 0;
            if (a >= a0 * ONE_18) {
                a /= a0; // Integer, not fixed point division
                sum += x0;
            }

            if (a >= a1 * ONE_18) {
                a /= a1; // Integer, not fixed point division
                sum += x1;
            }

            // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.
            sum *= 100;
            a *= 100;

            // Because further a_n are  20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.

            if (a >= a2) {
                a = (a * ONE_20) / a2;
                sum += x2;
            }

            if (a >= a3) {
                a = (a * ONE_20) / a3;
                sum += x3;
            }

            if (a >= a4) {
                a = (a * ONE_20) / a4;
                sum += x4;
            }

            if (a >= a5) {
                a = (a * ONE_20) / a5;
                sum += x5;
            }

            if (a >= a6) {
                a = (a * ONE_20) / a6;
                sum += x6;
            }

            if (a >= a7) {
                a = (a * ONE_20) / a7;
                sum += x7;
            }

            if (a >= a8) {
                a = (a * ONE_20) / a8;
                sum += x8;
            }

            if (a >= a9) {
                a = (a * ONE_20) / a9;
                sum += x9;
            }

            if (a >= a10) {
                a = (a * ONE_20) / a10;
                sum += x10;
            }

            if (a >= a11) {
                a = (a * ONE_20) / a11;
                sum += x11;
            }

            // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series
            // that converges rapidly for values of `a` close to one - the same one used in ln_36.
            // Let z = (a - 1) / (a + 1).
            // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

            // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires
            // division by ONE_20.
            int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);
            int256 z_squared = (z * z) / ONE_20;

            // num is the numerator of the series: the z^(2 * n + 1) term
            int256 num = z;

            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
            int256 seriesSum = num;

            // In each step, the numerator is multiplied by z^2
            num = (num * z_squared) / ONE_20;
            seriesSum += num / 3;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 5;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 7;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 9;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 11;

            // 6 Taylor terms are sufficient for 36 decimal precision.

            // Finally, we multiply by 2 (non fixed point) to compute ln(remainder)
            seriesSum *= 2;

            // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both
            // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal
            // value.

            return (sum + seriesSum) / 100;
        }
    }

    /**
     * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,
     * for x close to one.
     *
     * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.
     */
    function _ln_36(int256 x) private pure returns (int256) {
        unchecked {
            // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits
            // worthwhile.

            // First, we transform x to a 36 digit fixed point value.
            x *= ONE_18;

            // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).
            // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

            // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires
            // division by ONE_36.
            int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);
            int256 z_squared = (z * z) / ONE_36;

            // num is the numerator of the series: the z^(2 * n + 1) term
            int256 num = z;

            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
            int256 seriesSum = num;

            // In each step, the numerator is multiplied by z^2
            num = (num * z_squared) / ONE_36;
            seriesSum += num / 3;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 5;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 7;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 9;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 11;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 13;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 15;

            // 8 Taylor terms are sufficient for 36 decimal precision.

            // All that remains is multiplying by 2 (non fixed point).
            return seriesSum * 2;
        }
    }
}
PMath.sol 225 lines
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.8.0;

/* solhint-disable private-vars-leading-underscore, reason-string */

library PMath {
    uint256 internal constant ONE = 1e18; // 18 decimal places
    int256 internal constant IONE = 1e18; // 18 decimal places

    function subMax0(uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            return (a >= b ? a - b : 0);
        }
    }

    function subNoNeg(int256 a, int256 b) internal pure returns (int256) {
        require(a >= b, "negative");
        return a - b; // no unchecked since if b is very negative, a - b might overflow
    }

    function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 product = a * b;
        unchecked {
            return product / ONE;
        }
    }

    function mulDown(int256 a, int256 b) internal pure returns (int256) {
        int256 product = a * b;
        unchecked {
            return product / IONE;
        }
    }

    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 aInflated = a * ONE;
        unchecked {
            return aInflated / b;
        }
    }

    function divDown(int256 a, int256 b) internal pure returns (int256) {
        int256 aInflated = a * IONE;
        unchecked {
            return aInflated / b;
        }
    }

    function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a + b - 1) / b;
    }

    function rawDivUp(int256 a, int256 b) internal pure returns (int256) {
        return (a + b - 1) / b;
    }

    function tweakUp(uint256 a, uint256 factor) internal pure returns (uint256) {
        return mulDown(a, ONE + factor);
    }

    function tweakDown(uint256 a, uint256 factor) internal pure returns (uint256) {
        return mulDown(a, ONE - factor);
    }

    /// @return res = min(a + b, bound)
    /// @dev This function should handle arithmetic operation and bound check without overflow/underflow
    function addWithUpperBound(uint256 a, uint256 b, uint256 bound) internal pure returns (uint256 res) {
        unchecked {
            if (type(uint256).max - b < a) res = bound;
            else res = min(bound, a + b);
        }
    }

    /// @return res = max(a - b, bound)
    /// @dev This function should handle arithmetic operation and bound check without overflow/underflow
    function subWithLowerBound(uint256 a, uint256 b, uint256 bound) internal pure returns (uint256 res) {
        unchecked {
            if (b > a) res = bound;
            else res = max(a - b, bound);
        }
    }

    function clamp(uint256 x, uint256 lower, uint256 upper) internal pure returns (uint256 res) {
        res = x;
        if (x < lower) res = lower;
        else if (x > upper) res = upper;
    }

    // @author Uniswap
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function square(uint256 x) internal pure returns (uint256) {
        return x * x;
    }

    function squareDown(uint256 x) internal pure returns (uint256) {
        return mulDown(x, x);
    }

    function abs(int256 x) internal pure returns (uint256) {
        return uint256(x > 0 ? x : -x);
    }

    function neg(int256 x) internal pure returns (int256) {
        return x * (-1);
    }

    function neg(uint256 x) internal pure returns (int256) {
        return Int(x) * (-1);
    }

    function max(uint256 x, uint256 y) internal pure returns (uint256) {
        return (x > y ? x : y);
    }

    function max(int256 x, int256 y) internal pure returns (int256) {
        return (x > y ? x : y);
    }

    function min(uint256 x, uint256 y) internal pure returns (uint256) {
        return (x < y ? x : y);
    }

    function min(int256 x, int256 y) internal pure returns (int256) {
        return (x < y ? x : y);
    }

    /*///////////////////////////////////////////////////////////////
                               SIGNED CASTS
    //////////////////////////////////////////////////////////////*/

    function Int(uint256 x) internal pure returns (int256) {
        require(x <= uint256(type(int256).max));
        return int256(x);
    }

    function Int128(int256 x) internal pure returns (int128) {
        require(type(int128).min <= x && x <= type(int128).max);
        return int128(x);
    }

    function Int128(uint256 x) internal pure returns (int128) {
        return Int128(Int(x));
    }

    /*///////////////////////////////////////////////////////////////
                               UNSIGNED CASTS
    //////////////////////////////////////////////////////////////*/

    function Uint(int256 x) internal pure returns (uint256) {
        require(x >= 0);
        return uint256(x);
    }

    function Uint32(uint256 x) internal pure returns (uint32) {
        require(x <= type(uint32).max);
        return uint32(x);
    }

    function Uint64(uint256 x) internal pure returns (uint64) {
        require(x <= type(uint64).max);
        return uint64(x);
    }

    function Uint112(uint256 x) internal pure returns (uint112) {
        require(x <= type(uint112).max);
        return uint112(x);
    }

    function Uint96(uint256 x) internal pure returns (uint96) {
        require(x <= type(uint96).max);
        return uint96(x);
    }

    function Uint128(uint256 x) internal pure returns (uint128) {
        require(x <= type(uint128).max);
        return uint128(x);
    }

    function Uint192(uint256 x) internal pure returns (uint192) {
        require(x <= type(uint192).max);
        return uint192(x);
    }

    function Uint80(uint256 x) internal pure returns (uint80) {
        require(x <= type(uint80).max);
        return uint80(x);
    }

    function isAApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
        return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps);
    }

    function isAGreaterApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
        return a >= b && a <= mulDown(b, ONE + eps);
    }

    function isASmallerApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
        return a <= b && a >= mulDown(b, ONE - eps);
    }
}
MiniHelpers.sol 16 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

library MiniHelpers {
    function isCurrentlyExpired(uint256 expiry) internal view returns (bool) {
        return (expiry <= block.timestamp);
    }

    function isExpired(uint256 expiry, uint256 blockTime) internal pure returns (bool) {
        return (expiry <= blockTime);
    }

    function isTimeInThePast(uint256 timestamp) internal view returns (bool) {
        return (timestamp <= block.timestamp); // same definition as isCurrentlyExpired
    }
}
StringLib.sol 772 lines
/*
 * @title String & slice utility library for Solidity contracts.
 * @author Nick Johnson <[email protected]>
 *
 * @dev Functionality in this library is largely implemented using an
 *      abstraction called a 'slice'. A slice represents a part of a string -
 *      anything from the entire string to a single character, or even no
 *      characters at all (a 0-length slice). Since a slice only has to specify
 *      an offset and a length, copying and manipulating slices is a lot less
 *      expensive than copying and manipulating the strings they reference.
 *
 *      To further reduce gas costs, most functions on slice that need to return
 *      a slice modify the original one instead of allocating a new one; for
 *      instance, `s.split(".")` will return the text up to the first '.',
 *      modifying s to only contain the remainder of the string after the '.'.
 *      In situations where you do not want to modify the original slice, you
 *      can make a copy first with `.copy()`, for example:
 *      `s.copy().split(".")`. Try and avoid using this idiom in loops; since
 *      Solidity has no memory management, it will result in allocating many
 *      short-lived slices that are later discarded.
 *
 *      Functions that return two slices come in two versions: a non-allocating
 *      version that takes the second slice as an argument, modifying it in
 *      place, and an allocating version that allocates and returns the second
 *      slice; see `nextRune` for example.
 *
 *      Functions that have to copy string data will return strings rather than
 *      slices; these can be cast back to slices for further processing if
 *      required.
 *
 *      For convenience, some functions are provided with non-modifying
 *      variants that create a new slice and return both; for instance,
 *      `s.splitNew('.')` leaves s unmodified, and returns two values
 *      corresponding to the left and right parts of the string.
 */

pragma solidity ^0.8.0;

library StringLib {
    struct slice {
        uint256 _len;
        uint256 _ptr;
    }

    function memcpy(uint256 dest, uint256 src, uint256 len) private pure {
        // Copy word-length chunks while possible
        for (; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        uint256 mask = type(uint256).max;
        if (len > 0) {
            mask = 256 ** (32 - len) - 1;
        }
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }
    }

    /*
     * @dev Returns a slice containing the entire string.
     * @param self The string to make a slice from.
     * @return A newly allocated slice containing the entire string.
     */
    function toSlice(string memory self) internal pure returns (slice memory) {
        uint256 ptr;
        assembly {
            ptr := add(self, 0x20)
        }
        return slice(bytes(self).length, ptr);
    }

    /*
     * @dev Returns the length of a null-terminated bytes32 string.
     * @param self The value to find the length of.
     * @return The length of the string, from 0 to 32.
     */
    function len(bytes32 self) internal pure returns (uint256) {
        uint256 ret;
        if (self == 0) return 0;
        if (uint256(self) & type(uint128).max == 0) {
            ret += 16;
            self = bytes32(uint256(self) / 0x100000000000000000000000000000000);
        }
        if (uint256(self) & type(uint64).max == 0) {
            ret += 8;
            self = bytes32(uint256(self) / 0x10000000000000000);
        }
        if (uint256(self) & type(uint32).max == 0) {
            ret += 4;
            self = bytes32(uint256(self) / 0x100000000);
        }
        if (uint256(self) & type(uint16).max == 0) {
            ret += 2;
            self = bytes32(uint256(self) / 0x10000);
        }
        if (uint256(self) & type(uint8).max == 0) {
            ret += 1;
        }
        return 32 - ret;
    }

    /*
     * @dev Returns a slice containing the entire bytes32, interpreted as a
     *      null-terminated utf-8 string.
     * @param self The bytes32 value to convert to a slice.
     * @return A new slice containing the value of the input argument up to the
     *         first null.
     */
    function toSliceB32(bytes32 self) internal pure returns (slice memory ret) {
        // Allocate space for `self` in memory, copy it there, and point ret at it
        assembly {
            let ptr := mload(0x40)
            mstore(0x40, add(ptr, 0x20))
            mstore(ptr, self)
            mstore(add(ret, 0x20), ptr)
        }
        ret._len = len(self);
    }

    /*
     * @dev Returns a new slice containing the same data as the current slice.
     * @param self The slice to copy.
     * @return A new slice containing the same data as `self`.
     */
    function copy(slice memory self) internal pure returns (slice memory) {
        return slice(self._len, self._ptr);
    }

    /*
     * @dev Copies a slice to a new string.
     * @param self The slice to copy.
     * @return A newly allocated string containing the slice's text.
     */
    function toString(slice memory self) internal pure returns (string memory) {
        string memory ret = new string(self._len);
        uint256 retptr;
        assembly {
            retptr := add(ret, 32)
        }

        memcpy(retptr, self._ptr, self._len);
        return ret;
    }

    /*
     * @dev Returns the length in runes of the slice. Note that this operation
     *      takes time proportional to the length of the slice; avoid using it
     *      in loops, and call `slice.empty()` if you only need to know whether
     *      the slice is empty or not.
     * @param self The slice to operate on.
     * @return The length of the slice in runes.
     */
    function len(slice memory self) internal pure returns (uint256 l) {
        // Starting at ptr-31 means the LSB will be the byte we care about
        uint256 ptr = self._ptr - 31;
        uint256 end = ptr + self._len;
        for (l = 0; ptr < end; l++) {
            uint8 b;
            assembly {
                b := and(mload(ptr), 0xFF)
            }
            if (b < 0x80) {
                ptr += 1;
            } else if (b < 0xE0) {
                ptr += 2;
            } else if (b < 0xF0) {
                ptr += 3;
            } else if (b < 0xF8) {
                ptr += 4;
            } else if (b < 0xFC) {
                ptr += 5;
            } else {
                ptr += 6;
            }
        }
    }

    /*
     * @dev Returns true if the slice is empty (has a length of 0).
     * @param self The slice to operate on.
     * @return True if the slice is empty, False otherwise.
     */
    function empty(slice memory self) internal pure returns (bool) {
        return self._len == 0;
    }

    /*
     * @dev Returns a positive number if `other` comes lexicographically after
     *      `self`, a negative number if it comes before, or zero if the
     *      contents of the two slices are equal. Comparison is done per-rune,
     *      on unicode codepoints.
     * @param self The first slice to compare.
     * @param other The second slice to compare.
     * @return The result of the comparison.
     */
    function compare(slice memory self, slice memory other) internal pure returns (int256) {
        uint256 shortest = self._len;
        if (other._len < self._len) shortest = other._len;

        uint256 selfptr = self._ptr;
        uint256 otherptr = other._ptr;
        for (uint256 idx = 0; idx < shortest; idx += 32) {
            uint256 a;
            uint256 b;
            assembly {
                a := mload(selfptr)
                b := mload(otherptr)
            }
            if (a != b) {
                // Mask out irrelevant bytes and check again
                uint256 mask = type(uint256).max; // 0xffff...
                if (shortest < 32) {
                    mask = ~(2 ** (8 * (32 - shortest + idx)) - 1);
                }
                unchecked {
                    uint256 diff = (a & mask) - (b & mask);
                    if (diff != 0) return int256(diff);
                }
            }
            selfptr += 32;
            otherptr += 32;
        }
        return int256(self._len) - int256(other._len);
    }

    /*
     * @dev Returns true if the two slices contain the same text.
     * @param self The first slice to compare.
     * @param self The second slice to compare.
     * @return True if the slices are equal, false otherwise.
     */
    function equals(slice memory self, slice memory other) internal pure returns (bool) {
        return compare(self, other) == 0;
    }

    /*
     * @dev Extracts the first rune in the slice into `rune`, advancing the
     *      slice to point to the next rune and returning `self`.
     * @param self The slice to operate on.
     * @param rune The slice that will contain the first rune.
     * @return `rune`.
     */
    function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) {
        rune._ptr = self._ptr;

        if (self._len == 0) {
            rune._len = 0;
            return rune;
        }

        uint256 l;
        uint256 b;
        // Load the first byte of the rune into the LSBs of b
        assembly {
            b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF)
        }
        if (b < 0x80) {
            l = 1;
        } else if (b < 0xE0) {
            l = 2;
        } else if (b < 0xF0) {
            l = 3;
        } else {
            l = 4;
        }

        // Check for truncated codepoints
        if (l > self._len) {
            rune._len = self._len;
            self._ptr += self._len;
            self._len = 0;
            return rune;
        }

        self._ptr += l;
        self._len -= l;
        rune._len = l;
        return rune;
    }

    /*
     * @dev Returns the first rune in the slice, advancing the slice to point
     *      to the next rune.
     * @param self The slice to operate on.
     * @return A slice containing only the first rune from `self`.
     */
    function nextRune(slice memory self) internal pure returns (slice memory ret) {
        nextRune(self, ret);
    }

    /*
     * @dev Returns the number of the first codepoint in the slice.
     * @param self The slice to operate on.
     * @return The number of the first codepoint in the slice.
     */
    function ord(slice memory self) internal pure returns (uint256 ret) {
        if (self._len == 0) {
            return 0;
        }

        uint256 word;
        uint256 length;
        uint256 divisor = 2 ** 248;

        // Load the rune into the MSBs of b
        assembly {
            word := mload(mload(add(self, 32)))
        }
        uint256 b = word / divisor;
        if (b < 0x80) {
            ret = b;
            length = 1;
        } else if (b < 0xE0) {
            ret = b & 0x1F;
            length = 2;
        } else if (b < 0xF0) {
            ret = b & 0x0F;
            length = 3;
        } else {
            ret = b & 0x07;
            length = 4;
        }

        // Check for truncated codepoints
        if (length > self._len) {
            return 0;
        }

        for (uint256 i = 1; i < length; i++) {
            divisor = divisor / 256;
            b = (word / divisor) & 0xFF;
            if (b & 0xC0 != 0x80) {
                // Invalid UTF-8 sequence
                return 0;
            }
            ret = (ret * 64) | (b & 0x3F);
        }

        return ret;
    }

    /*
     * @dev Returns the keccak-256 hash of the slice.
     * @param self The slice to hash.
     * @return The hash of the slice.
     */
    function keccak(slice memory self) internal pure returns (bytes32 ret) {
        assembly {
            ret := keccak256(mload(add(self, 32)), mload(self))
        }
    }

    /*
     * @dev Returns true if `self` starts with `needle`.
     * @param self The slice to operate on.
     * @param needle The slice to search for.
     * @return True if the slice starts with the provided text, false otherwise.
     */
    function startsWith(slice memory self, slice memory needle) internal pure returns (bool) {
        if (self._len < needle._len) {
            return false;
        }

        if (self._ptr == needle._ptr) {
            return true;
        }

        bool equal;
        assembly {
            let length := mload(needle)
            let selfptr := mload(add(self, 0x20))
            let needleptr := mload(add(needle, 0x20))
            equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
        }
        return equal;
    }

    /*
     * @dev If `self` starts with `needle`, `needle` is removed from the
     *      beginning of `self`. Otherwise, `self` is unmodified.
     * @param self The slice to operate on.
     * @param needle The slice to search for.
     * @return `self`
     */
    function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) {
        if (self._len < needle._len) {
            return self;
        }

        bool equal = true;
        if (self._ptr != needle._ptr) {
            assembly {
                let length := mload(needle)
                let selfptr := mload(add(self, 0x20))
                let needleptr := mload(add(needle, 0x20))
                equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
            }
        }

        if (equal) {
            self._len -= needle._len;
            self._ptr += needle._len;
        }

        return self;
    }

    /*
     * @dev Returns true if the slice ends with `needle`.
     * @param self The slice to operate on.
     * @param needle The slice to search for.
     * @return True if the slice starts with the provided text, false otherwise.
     */
    function endsWith(slice memory self, slice memory needle) internal pure returns (bool) {
        if (self._len < needle._len) {
            return false;
        }

        uint256 selfptr = self._ptr + self._len - needle._len;

        if (selfptr == needle._ptr) {
            return true;
        }

        bool equal;
        assembly {
            let length := mload(needle)
            let needleptr := mload(add(needle, 0x20))
            equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
        }

        return equal;
    }

    /*
     * @dev If `self` ends with `needle`, `needle` is removed from the
     *      end of `self`. Otherwise, `self` is unmodified.
     * @param self The slice to operate on.
     * @param needle The slice to search for.
     * @return `self`
     */
    function until(slice memory self, slice memory needle) internal pure returns (slice memory) {
        if (self._len < needle._len) {
            return self;
        }

        uint256 selfptr = self._ptr + self._len - needle._len;
        bool equal = true;
        if (selfptr != needle._ptr) {
            assembly {
                let length := mload(needle)
                let needleptr := mload(add(needle, 0x20))
                equal := eq(keccak256(selfptr, length), keccak256(needleptr, length))
            }
        }

        if (equal) {
            self._len -= needle._len;
        }

        return self;
    }

    // Returns the memory address of the first byte of the first occurrence of
    // `needle` in `self`, or the first byte after `self` if not found.
    function findPtr(
        uint256 selflen,
        uint256 selfptr,
        uint256 needlelen,
        uint256 needleptr
    ) private pure returns (uint256) {
        uint256 ptr = selfptr;
        uint256 idx;

        if (needlelen <= selflen) {
            if (needlelen <= 32) {
                bytes32 mask;
                if (needlelen > 0) {
                    mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1));
                }

                bytes32 needledata;
                assembly {
                    needledata := and(mload(needleptr), mask)
                }

                uint256 end = selfptr + selflen - needlelen;
                bytes32 ptrdata;
                assembly {
                    ptrdata := and(mload(ptr), mask)
                }

                while (ptrdata != needledata) {
                    if (ptr >= end) return selfptr + selflen;
                    ptr++;
                    assembly {
                        ptrdata := and(mload(ptr), mask)
                    }
                }
                return ptr;
            } else {
                // For long needles, use hashing
                bytes32 hash;
                assembly {
                    hash := keccak256(needleptr, needlelen)
                }

                for (idx = 0; idx <= selflen - needlelen; idx++) {
                    bytes32 testHash;
                    assembly {
                        testHash := keccak256(ptr, needlelen)
                    }
                    if (hash == testHash) return ptr;
                    ptr += 1;
                }
            }
        }
        return selfptr + selflen;
    }

    // Returns the memory address of the first byte after the last occurrence of
    // `needle` in `self`, or the address of `self` if not found.
    function rfindPtr(
        uint256 selflen,
        uint256 selfptr,
        uint256 needlelen,
        uint256 needleptr
    ) private pure returns (uint256) {
        uint256 ptr;

        if (needlelen <= selflen) {
            if (needlelen <= 32) {
                bytes32 mask;
                if (needlelen > 0) {
                    mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1));
                }

                bytes32 needledata;
                assembly {
                    needledata := and(mload(needleptr), mask)
                }

                ptr = selfptr + selflen - needlelen;
                bytes32 ptrdata;
                assembly {
                    ptrdata := and(mload(ptr), mask)
                }

                while (ptrdata != needledata) {
                    if (ptr <= selfptr) return selfptr;
                    ptr--;
                    assembly {
                        ptrdata := and(mload(ptr), mask)
                    }
                }
                return ptr + needlelen;
            } else {
                // For long needles, use hashing
                bytes32 hash;
                assembly {
                    hash := keccak256(needleptr, needlelen)
                }
                ptr = selfptr + (selflen - needlelen);
                while (ptr >= selfptr) {
                    bytes32 testHash;
                    assembly {
                        testHash := keccak256(ptr, needlelen)
                    }
                    if (hash == testHash) return ptr + needlelen;
                    ptr -= 1;
                }
            }
        }
        return selfptr;
    }

    /*
     * @dev Modifies `self` to contain everything from the first occurrence of
     *      `needle` to the end of the slice. `self` is set to the empty slice
     *      if `needle` is not found.
     * @param self The slice to search and modify.
     * @param needle The text to search for.
     * @return `self`.
     */
    function find(slice memory self, slice memory needle) internal pure returns (slice memory) {
        uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
        self._len -= ptr - self._ptr;
        self._ptr = ptr;
        return self;
    }

    /*
     * @dev Modifies `self` to contain the part of the string from the start of
     *      `self` to the end of the first occurrence of `needle`. If `needle`
     *      is not found, `self` is set to the empty slice.
     * @param self The slice to search and modify.
     * @param needle The text to search for.
     * @return `self`.
     */
    function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) {
        uint256 ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr);
        self._len = ptr - self._ptr;
        return self;
    }

    /*
     * @dev Splits the slice, setting `self` to everything after the first
     *      occurrence of `needle`, and `token` to everything before it. If
     *      `needle` does not occur in `self`, `self` is set to the empty slice,
     *      and `token` is set to the entirety of `self`.
     * @param self The slice to split.
     * @param needle The text to search for in `self`.
     * @param token An output parameter to which the first token is written.
     * @return `token`.
     */
    function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) {
        uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr);
        token._ptr = self._ptr;
        token._len = ptr - self._ptr;
        if (ptr == self._ptr + self._len) {
            // Not found
            self._len = 0;
        } else {
            self._len -= token._len + needle._len;
            self._ptr = ptr + needle._len;
        }
        return token;
    }

    /*
     * @dev Splits the slice, setting `self` to everything after the first
     *      occurrence of `needle`, and returning everything before it. If
     *      `needle` does not occur in `self`, `self` is set to the empty slice,
     *      and the entirety of `self` is returned.
     * @param self The slice to split.
     * @param needle The text to search for in `self`.
     * @return The part of `self` up to the first occurrence of `delim`.
     */
    function split(slice memory self, slice memory needle) internal pure returns (slice memory token) {
        split(self, needle, token);
    }

    /*
     * @dev Splits the slice, setting `self` to everything before the last
     *      occurrence of `needle`, and `token` to everything after it. If
     *      `needle` does not occur in `self`, `self` is set to the empty slice,
     *      and `token` is set to the entirety of `self`.
     * @param self The slice to split.
     * @param needle The text to search for in `self`.
     * @param token An output parameter to which the first token is written.
     * @return `token`.
     */
    function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) {
        uint256 ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr);
        token._ptr = ptr;
        token._len = self._len - (ptr - self._ptr);
        if (ptr == self._ptr) {
            // Not found
            self._len = 0;
        } else {
            self._len -= token._len + needle._len;
        }
        return token;
    }

    /*
     * @dev Splits the slice, setting `self` to everything before the last
     *      occurrence of `needle`, and returning everything after it. If
     *      `needle` does not occur in `self`, `self` is set to the empty slice,
     *      and the entirety of `self` is returned.
     * @param self The slice to split.
     * @param needle The text to search for in `self`.
     * @return The part of `self` after the last occurrence of `delim`.
     */
    function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) {
        rsplit(self, needle, token);
    }

    /*
     * @dev Counts the number of nonoverlapping occurrences of `needle` in `self`.
     * @param self The slice to search.
     * @param needle The text to search for in `self`.
     * @return The number of occurrences of `needle` found in `self`.
     */
    function count(slice memory self, slice memory needle) internal pure returns (uint256 cnt) {
        uint256 ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len;
        while (ptr <= self._ptr + self._len) {
            cnt++;
            ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len;
        }
    }

    /*
     * @dev Returns True if `self` contains `needle`.
     * @param self The slice to search.
     * @param needle The text to search for in `self`.
     * @return True if `needle` is found in `self`, false otherwise.
     */
    function contains(slice memory self, slice memory needle) internal pure returns (bool) {
        return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr;
    }

    /*
     * @dev Returns a newly allocated string containing the concatenation of
     *      `self` and `other`.
     * @param self The first slice to concatenate.
     * @param other The second slice to concatenate.
     * @return The concatenation of the two strings.
     */
    function concat(slice memory self, slice memory other) internal pure returns (string memory) {
        string memory ret = new string(self._len + other._len);
        uint256 retptr;
        assembly {
            retptr := add(ret, 32)
        }
        memcpy(retptr, self._ptr, self._len);
        memcpy(retptr + self._len, other._ptr, other._len);
        return ret;
    }

    /*
     * @dev Joins an array of slices, using `self` as a delimiter, returning a
     *      newly allocated string.
     * @param self The delimiter to use.
     * @param parts A list of slices to join.
     * @return A newly allocated string containing all the slices in `parts`,
     *         joined with `self`.
     */
    function join(slice memory self, slice[] memory parts) internal pure returns (string memory) {
        if (parts.length == 0) return "";

        uint256 length = self._len * (parts.length - 1);
        for (uint256 i = 0; i < parts.length; i++) length += parts[i]._len;

        string memory ret = new string(length);
        uint256 retptr;
        assembly {
            retptr := add(ret, 32)
        }

        for (uint256 i = 0; i < parts.length; i++) {
            memcpy(retptr, parts[i]._ptr, parts[i]._len);
            retptr += parts[i]._len;
            if (i < parts.length - 1) {
                memcpy(retptr, self._ptr, self._len);
                retptr += self._len;
            }
        }

        return ret;
    }

    function stripPrefix(string memory _str, string memory _prefix) internal pure returns (string memory) {
        slice memory s = toSlice(_str);
        slice memory d = toSlice(_prefix);
        return toString(beyond(s, d));
    }

    function stripPrefixSlice(string memory _str, string memory _delim) internal pure returns (slice memory) {
        slice memory s = toSlice(_str);
        slice memory d = toSlice(_delim);
        return beyond(s, d);
    }
}
TokenHelper.sol 71 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../../interfaces/IWETH.sol";

abstract contract TokenHelper {
    using SafeERC20 for IERC20;

    address internal constant NATIVE = address(0);
    uint256 internal constant LOWER_BOUND_APPROVAL = type(uint96).max / 2; // some tokens use 96 bits for approval

    function _transferIn(address token, address from, uint256 amount) internal {
        if (token == NATIVE) require(msg.value == amount, "eth mismatch");
        else if (amount != 0) IERC20(token).safeTransferFrom(from, address(this), amount);
    }

    function _transferFrom(IERC20 token, address from, address to, uint256 amount) internal {
        if (amount != 0) token.safeTransferFrom(from, to, amount);
    }

    function _transferOut(address token, address to, uint256 amount) internal {
        if (amount == 0) return;
        if (token == NATIVE) {
            (bool success, ) = to.call{value: amount}("");
            require(success, "eth send failed");
        } else {
            IERC20(token).safeTransfer(to, amount);
        }
    }

    function _transferOut(address[] memory tokens, address to, uint256[] memory amounts) internal {
        uint256 numTokens = tokens.length;
        require(numTokens == amounts.length, "length mismatch");
        for (uint256 i = 0; i < numTokens; ) {
            _transferOut(tokens[i], to, amounts[i]);
            unchecked {
                i++;
            }
        }
    }

    function _selfBalance(address token) internal view returns (uint256) {
        return (token == NATIVE) ? address(this).balance : IERC20(token).balanceOf(address(this));
    }

    function _selfBalance(IERC20 token) internal view returns (uint256) {
        return token.balanceOf(address(this));
    }

    /// @notice Approves the stipulated contract to spend the given allowance in the given token
    /// @dev PLS PAY ATTENTION to tokens that requires the approval to be set to 0 before changing it
    function _safeApprove(address token, address to, uint256 value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), "Safe Approve");
    }

    function _safeApproveInf(address token, address to) internal {
        if (token == NATIVE) return;
        if (IERC20(token).allowance(address(this), to) < LOWER_BOUND_APPROVAL) {
            _safeApprove(token, to, 0);
            _safeApprove(token, to, type(uint256).max);
        }
    }

    function _wrap_unwrap_ETH(address tokenIn, address tokenOut, uint256 netTokenIn) internal {
        if (tokenIn == NATIVE) IWETH(tokenOut).deposit{value: netTokenIn}();
        else IWETH(tokenIn).withdraw(netTokenIn);
    }
}
MarketMathCore.sol 417 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "../libraries/math/PMath.sol";
import "../libraries/math/LogExpMath.sol";

import "../StandardizedYield/PYIndex.sol";
import "../libraries/MiniHelpers.sol";
import "../libraries/Errors.sol";

struct MarketState {
    int256 totalPt;
    int256 totalSy;
    int256 totalLp;
    address treasury;
    /// immutable variables ///
    int256 scalarRoot;
    uint256 expiry;
    /// fee data ///
    uint256 lnFeeRateRoot;
    uint256 reserveFeePercent; // base 100
    /// last trade data ///
    uint256 lastLnImpliedRate;
}

// params that are expensive to compute, therefore we pre-compute them
struct MarketPreCompute {
    int256 rateScalar;
    int256 totalAsset;
    int256 rateAnchor;
    int256 feeRate;
}

// solhint-disable ordering
library MarketMathCore {
    using PMath for uint256;
    using PMath for int256;
    using LogExpMath for int256;
    using PYIndexLib for PYIndex;

    int256 internal constant MINIMUM_LIQUIDITY = 10 ** 3;
    int256 internal constant PERCENTAGE_DECIMALS = 100;
    uint256 internal constant DAY = 86400;
    uint256 internal constant IMPLIED_RATE_TIME = 365 * DAY;

    int256 internal constant MAX_MARKET_PROPORTION = (1e18 * 96) / 100;

    using PMath for uint256;
    using PMath for int256;

    /*///////////////////////////////////////////////////////////////
                UINT FUNCTIONS TO PROXY TO CORE FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function addLiquidity(
        MarketState memory market,
        uint256 syDesired,
        uint256 ptDesired,
        uint256 blockTime
    ) internal pure returns (uint256 lpToReserve, uint256 lpToAccount, uint256 syUsed, uint256 ptUsed) {
        (int256 _lpToReserve, int256 _lpToAccount, int256 _syUsed, int256 _ptUsed) = addLiquidityCore(
            market,
            syDesired.Int(),
            ptDesired.Int(),
            blockTime
        );

        lpToReserve = _lpToReserve.Uint();
        lpToAccount = _lpToAccount.Uint();
        syUsed = _syUsed.Uint();
        ptUsed = _ptUsed.Uint();
    }

    function removeLiquidity(
        MarketState memory market,
        uint256 lpToRemove
    ) internal pure returns (uint256 netSyToAccount, uint256 netPtToAccount) {
        (int256 _syToAccount, int256 _ptToAccount) = removeLiquidityCore(market, lpToRemove.Int());

        netSyToAccount = _syToAccount.Uint();
        netPtToAccount = _ptToAccount.Uint();
    }

    function swapExactPtForSy(
        MarketState memory market,
        PYIndex index,
        uint256 exactPtToMarket,
        uint256 blockTime
    ) internal pure returns (uint256 netSyToAccount, uint256 netSyFee, uint256 netSyToReserve) {
        (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(
            market,
            index,
            exactPtToMarket.neg(),
            blockTime
        );

        netSyToAccount = _netSyToAccount.Uint();
        netSyFee = _netSyFee.Uint();
        netSyToReserve = _netSyToReserve.Uint();
    }

    function swapSyForExactPt(
        MarketState memory market,
        PYIndex index,
        uint256 exactPtToAccount,
        uint256 blockTime
    ) internal pure returns (uint256 netSyToMarket, uint256 netSyFee, uint256 netSyToReserve) {
        (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(
            market,
            index,
            exactPtToAccount.Int(),
            blockTime
        );

        netSyToMarket = _netSyToAccount.neg().Uint();
        netSyFee = _netSyFee.Uint();
        netSyToReserve = _netSyToReserve.Uint();
    }

    /*///////////////////////////////////////////////////////////////
                    CORE FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function addLiquidityCore(
        MarketState memory market,
        int256 syDesired,
        int256 ptDesired,
        uint256 blockTime
    ) internal pure returns (int256 lpToReserve, int256 lpToAccount, int256 syUsed, int256 ptUsed) {
        /// ------------------------------------------------------------
        /// CHECKS
        /// ------------------------------------------------------------
        if (syDesired == 0 || ptDesired == 0) revert Errors.MarketZeroAmountsInput();
        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        if (market.totalLp == 0) {
            lpToAccount = PMath.sqrt((syDesired * ptDesired).Uint()).Int() - MINIMUM_LIQUIDITY;
            lpToReserve = MINIMUM_LIQUIDITY;
            syUsed = syDesired;
            ptUsed = ptDesired;
        } else {
            int256 netLpByPt = (ptDesired * market.totalLp) / market.totalPt;
            int256 netLpBySy = (syDesired * market.totalLp) / market.totalSy;
            if (netLpByPt < netLpBySy) {
                lpToAccount = netLpByPt;
                ptUsed = ptDesired;
                syUsed = (market.totalSy * lpToAccount).rawDivUp(market.totalLp);
            } else {
                lpToAccount = netLpBySy;
                syUsed = syDesired;
                ptUsed = (market.totalPt * lpToAccount).rawDivUp(market.totalLp);
            }
        }

        if (lpToAccount <= 0 || syUsed <= 0 || ptUsed <= 0) revert Errors.MarketZeroAmountsOutput();

        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        market.totalSy += syUsed;
        market.totalPt += ptUsed;
        market.totalLp += lpToAccount + lpToReserve;
    }

    function removeLiquidityCore(
        MarketState memory market,
        int256 lpToRemove
    ) internal pure returns (int256 netSyToAccount, int256 netPtToAccount) {
        /// ------------------------------------------------------------
        /// CHECKS
        /// ------------------------------------------------------------
        if (lpToRemove == 0) revert Errors.MarketZeroAmountsInput();

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        netSyToAccount = (lpToRemove * market.totalSy) / market.totalLp;
        netPtToAccount = (lpToRemove * market.totalPt) / market.totalLp;

        if (netSyToAccount == 0 && netPtToAccount == 0) revert Errors.MarketZeroAmountsOutput();

        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        market.totalLp = market.totalLp.subNoNeg(lpToRemove);
        market.totalPt = market.totalPt.subNoNeg(netPtToAccount);
        market.totalSy = market.totalSy.subNoNeg(netSyToAccount);
    }

    function executeTradeCore(
        MarketState memory market,
        PYIndex index,
        int256 netPtToAccount,
        uint256 blockTime
    ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {
        /// ------------------------------------------------------------
        /// CHECKS
        /// ------------------------------------------------------------
        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
        if (market.totalPt <= netPtToAccount)
            revert Errors.MarketInsufficientPtForTrade(market.totalPt, netPtToAccount);

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        MarketPreCompute memory comp = getMarketPreCompute(market, index, blockTime);

        (netSyToAccount, netSyFee, netSyToReserve) = calcTrade(market, comp, index, netPtToAccount);

        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        _setNewMarketStateTrade(market, comp, index, netPtToAccount, netSyToAccount, netSyToReserve, blockTime);
    }

    function getMarketPreCompute(
        MarketState memory market,
        PYIndex index,
        uint256 blockTime
    ) internal pure returns (MarketPreCompute memory res) {
        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();

        uint256 timeToExpiry = market.expiry - blockTime;

        res.rateScalar = _getRateScalar(market, timeToExpiry);
        res.totalAsset = index.syToAsset(market.totalSy);

        if (market.totalPt == 0 || res.totalAsset == 0)
            revert Errors.MarketZeroTotalPtOrTotalAsset(market.totalPt, res.totalAsset);

        res.rateAnchor = _getRateAnchor(
            market.totalPt,
            market.lastLnImpliedRate,
            res.totalAsset,
            res.rateScalar,
            timeToExpiry
        );
        res.feeRate = _getExchangeRateFromImpliedRate(market.lnFeeRateRoot, timeToExpiry);
    }

    function calcTrade(
        MarketState memory market,
        MarketPreCompute memory comp,
        PYIndex index,
        int256 netPtToAccount
    ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {
        int256 preFeeExchangeRate = _getExchangeRate(
            market.totalPt,
            comp.totalAsset,
            comp.rateScalar,
            comp.rateAnchor,
            netPtToAccount
        );

        int256 preFeeAssetToAccount = netPtToAccount.divDown(preFeeExchangeRate).neg();
        int256 fee = comp.feeRate;

        if (netPtToAccount > 0) {
            int256 postFeeExchangeRate = preFeeExchangeRate.divDown(fee);
            if (postFeeExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(postFeeExchangeRate);

            fee = preFeeAssetToAccount.mulDown(PMath.IONE - fee);
        } else {
            fee = ((preFeeAssetToAccount * (PMath.IONE - fee)) / fee).neg();
        }

        int256 netAssetToReserve = (fee * market.reserveFeePercent.Int()) / PERCENTAGE_DECIMALS;
        int256 netAssetToAccount = preFeeAssetToAccount - fee;

        netSyToAccount = netAssetToAccount < 0
            ? index.assetToSyUp(netAssetToAccount)
            : index.assetToSy(netAssetToAccount);
        netSyFee = index.assetToSy(fee);
        netSyToReserve = index.assetToSy(netAssetToReserve);
    }

    function _setNewMarketStateTrade(
        MarketState memory market,
        MarketPreCompute memory comp,
        PYIndex index,
        int256 netPtToAccount,
        int256 netSyToAccount,
        int256 netSyToReserve,
        uint256 blockTime
    ) internal pure {
        uint256 timeToExpiry = market.expiry - blockTime;

        market.totalPt = market.totalPt.subNoNeg(netPtToAccount);
        market.totalSy = market.totalSy.subNoNeg(netSyToAccount + netSyToReserve);

        market.lastLnImpliedRate = _getLnImpliedRate(
            market.totalPt,
            index.syToAsset(market.totalSy),
            comp.rateScalar,
            comp.rateAnchor,
            timeToExpiry
        );

        if (market.lastLnImpliedRate == 0) revert Errors.MarketZeroLnImpliedRate();
    }

    function _getRateAnchor(
        int256 totalPt,
        uint256 lastLnImpliedRate,
        int256 totalAsset,
        int256 rateScalar,
        uint256 timeToExpiry
    ) internal pure returns (int256 rateAnchor) {
        int256 newExchangeRate = _getExchangeRateFromImpliedRate(lastLnImpliedRate, timeToExpiry);

        if (newExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(newExchangeRate);

        {
            int256 proportion = totalPt.divDown(totalPt + totalAsset);

            int256 lnProportion = _logProportion(proportion);

            rateAnchor = newExchangeRate - lnProportion.divDown(rateScalar);
        }
    }

    /// @notice Calculates the current market implied rate.
    /// @return lnImpliedRate the implied rate
    function _getLnImpliedRate(
        int256 totalPt,
        int256 totalAsset,
        int256 rateScalar,
        int256 rateAnchor,
        uint256 timeToExpiry
    ) internal pure returns (uint256 lnImpliedRate) {
        // This will check for exchange rates < PMath.IONE
        int256 exchangeRate = _getExchangeRate(totalPt, totalAsset, rateScalar, rateAnchor, 0);

        // exchangeRate >= 1 so its ln >= 0
        uint256 lnRate = exchangeRate.ln().Uint();

        lnImpliedRate = (lnRate * IMPLIED_RATE_TIME) / timeToExpiry;
    }

    /// @notice Converts an implied rate to an exchange rate given a time to expiry. The
    /// formula is E = e^rt
    function _getExchangeRateFromImpliedRate(
        uint256 lnImpliedRate,
        uint256 timeToExpiry
    ) internal pure returns (int256 exchangeRate) {
        uint256 rt = (lnImpliedRate * timeToExpiry) / IMPLIED_RATE_TIME;

        exchangeRate = LogExpMath.exp(rt.Int());
    }

    function _getExchangeRate(
        int256 totalPt,
        int256 totalAsset,
        int256 rateScalar,
        int256 rateAnchor,
        int256 netPtToAccount
    ) internal pure returns (int256 exchangeRate) {
        int256 numerator = totalPt.subNoNeg(netPtToAccount);

        int256 proportion = (numerator.divDown(totalPt + totalAsset));

        if (proportion > MAX_MARKET_PROPORTION)
            revert Errors.MarketProportionTooHigh(proportion, MAX_MARKET_PROPORTION);

        int256 lnProportion = _logProportion(proportion);

        exchangeRate = lnProportion.divDown(rateScalar) + rateAnchor;

        if (exchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(exchangeRate);
    }

    function _logProportion(int256 proportion) internal pure returns (int256 res) {
        if (proportion == PMath.IONE) revert Errors.MarketProportionMustNotEqualOne();

        int256 logitP = proportion.divDown(PMath.IONE - proportion);

        res = logitP.ln();
    }

    function _getRateScalar(MarketState memory market, uint256 timeToExpiry) internal pure returns (int256 rateScalar) {
        rateScalar = (market.scalarRoot * IMPLIED_RATE_TIME.Int()) / timeToExpiry.Int();
        if (rateScalar <= 0) revert Errors.MarketRateScalarBelowZero(rateScalar);
    }

    function setInitialLnImpliedRate(
        MarketState memory market,
        PYIndex index,
        int256 initialAnchor,
        uint256 blockTime
    ) internal pure {
        /// ------------------------------------------------------------
        /// CHECKS
        /// ------------------------------------------------------------
        if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();

        /// ------------------------------------------------------------
        /// MATH
        /// ------------------------------------------------------------
        int256 totalAsset = index.syToAsset(market.totalSy);
        uint256 timeToExpiry = market.expiry - blockTime;
        int256 rateScalar = _getRateScalar(market, timeToExpiry);

        /// ------------------------------------------------------------
        /// WRITE
        /// ------------------------------------------------------------
        market.lastLnImpliedRate = _getLnImpliedRate(
            market.totalPt,
            totalAsset,
            rateScalar,
            initialAnchor,
            timeToExpiry
        );
    }
}
OracleLib.sol 199 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "../libraries/Errors.sol";

/// Adapted from UniswapV3's Oracle

library OracleLib {
    struct Observation {
        uint32 blockTimestamp;
        uint216 lnImpliedRateCumulative;
        bool initialized;
        // 1 SLOT = 256 bits
    }

    function transform(
        Observation memory last,
        uint32 blockTimestamp,
        uint96 lnImpliedRate
    ) public pure returns (Observation memory) {
        return
            Observation({
                blockTimestamp: blockTimestamp,
                lnImpliedRateCumulative: last.lnImpliedRateCumulative +
                    uint216(lnImpliedRate) *
                    (blockTimestamp - last.blockTimestamp),
                initialized: true
            });
    }

    function initialize(
        Observation[65535] storage self,
        uint32 time
    ) public returns (uint16 cardinality, uint16 cardinalityNext) {
        self[0] = Observation({blockTimestamp: time, lnImpliedRateCumulative: 0, initialized: true});
        return (1, 1);
    }

    function write(
        Observation[65535] storage self,
        uint16 index,
        uint32 blockTimestamp,
        uint96 lnImpliedRate,
        uint16 cardinality,
        uint16 cardinalityNext
    ) public returns (uint16 indexUpdated, uint16 cardinalityUpdated) {
        Observation memory last = self[index];

        // early return if we've already written an observation this block
        if (last.blockTimestamp == blockTimestamp) return (index, cardinality);

        // if the conditions are right, we can bump the cardinality
        if (cardinalityNext > cardinality && index == (cardinality - 1)) {
            cardinalityUpdated = cardinalityNext;
        } else {
            cardinalityUpdated = cardinality;
        }

        indexUpdated = (index + 1) % cardinalityUpdated;
        self[indexUpdated] = transform(last, blockTimestamp, lnImpliedRate);
    }

    function grow(Observation[65535] storage self, uint16 current, uint16 next) public returns (uint16) {
        if (current == 0) revert Errors.OracleUninitialized();
        // no-op if the passed next value isn't greater than the current next value
        if (next <= current) return current;
        // store in each slot to prevent fresh SSTOREs in swaps
        // this data will not be used because the initialized boolean is still false
        for (uint16 i = current; i != next; ) {
            self[i].blockTimestamp = 1;
            unchecked {
                ++i;
            }
        }
        return next;
    }

    function binarySearch(
        Observation[65535] storage self,
        uint32 target,
        uint16 index,
        uint16 cardinality
    ) public view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
        uint256 l = (index + 1) % cardinality; // oldest observation
        uint256 r = l + cardinality - 1; // newest observation
        uint256 i;
        while (true) {
            i = (l + r) / 2;

            beforeOrAt = self[i % cardinality];

            // we've landed on an uninitialized observation, keep searching higher (more recently)
            if (!beforeOrAt.initialized) {
                l = i + 1;
                continue;
            }

            atOrAfter = self[(i + 1) % cardinality];

            bool targetAtOrAfter = beforeOrAt.blockTimestamp <= target;

            // check if we've found the answer!
            if (targetAtOrAfter && target <= atOrAfter.blockTimestamp) break;

            if (!targetAtOrAfter) r = i - 1;
            else l = i + 1;
        }
    }

    function getSurroundingObservations(
        Observation[65535] storage self,
        uint32 target,
        uint96 lnImpliedRate,
        uint16 index,
        uint16 cardinality
    ) public view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
        // optimistically set before to the newest observation
        beforeOrAt = self[index];

        // if the target is chronologically at or after the newest observation, we can early return
        if (beforeOrAt.blockTimestamp <= target) {
            if (beforeOrAt.blockTimestamp == target) {
                // if newest observation equals target, we're in the same block, so we can ignore atOrAfter
                return (beforeOrAt, atOrAfter);
            } else {
                // otherwise, we need to transform
                return (beforeOrAt, transform(beforeOrAt, target, lnImpliedRate));
            }
        }

        // now, set beforeOrAt to the oldest observation
        beforeOrAt = self[(index + 1) % cardinality];
        if (!beforeOrAt.initialized) beforeOrAt = self[0];

        // ensure that the target is chronologically at or after the oldest observation
        if (target < beforeOrAt.blockTimestamp) revert Errors.OracleTargetTooOld(target, beforeOrAt.blockTimestamp);

        // if we've reached this point, we have to binary search
        return binarySearch(self, target, index, cardinality);
    }

    function observeSingle(
        Observation[65535] storage self,
        uint32 time,
        uint32 secondsAgo,
        uint96 lnImpliedRate,
        uint16 index,
        uint16 cardinality
    ) public view returns (uint216 lnImpliedRateCumulative) {
        if (secondsAgo == 0) {
            Observation memory last = self[index];
            if (last.blockTimestamp != time) {
                return transform(last, time, lnImpliedRate).lnImpliedRateCumulative;
            }
            return last.lnImpliedRateCumulative;
        }

        uint32 target = time - secondsAgo;

        (Observation memory beforeOrAt, Observation memory atOrAfter) = getSurroundingObservations(
            self,
            target,
            lnImpliedRate,
            index,
            cardinality
        );

        if (target == beforeOrAt.blockTimestamp) {
            // we're at the left boundary
            return beforeOrAt.lnImpliedRateCumulative;
        } else if (target == atOrAfter.blockTimestamp) {
            // we're at the right boundary
            return atOrAfter.lnImpliedRateCumulative;
        } else {
            // we're in the middle
            return (beforeOrAt.lnImpliedRateCumulative +
                uint216(
                    (uint256(atOrAfter.lnImpliedRateCumulative - beforeOrAt.lnImpliedRateCumulative) *
                        (target - beforeOrAt.blockTimestamp)) / (atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp)
                ));
        }
    }

    function observe(
        Observation[65535] storage self,
        uint32 time,
        uint32[] memory secondsAgos,
        uint96 lnImpliedRate,
        uint16 index,
        uint16 cardinality
    ) public view returns (uint216[] memory lnImpliedRateCumulative) {
        if (cardinality == 0) revert Errors.OracleZeroCardinality();

        lnImpliedRateCumulative = new uint216[](secondsAgos.length);
        for (uint256 i = 0; i < lnImpliedRateCumulative.length; ++i) {
            lnImpliedRateCumulative[i] = observeSingle(self, time, secondsAgos[i], lnImpliedRate, index, cardinality);
        }
    }
}
PendleGauge.sol 118 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "../../interfaces/IPGauge.sol";
import "../../interfaces/IPVeToken.sol";
import "../../interfaces/IPGaugeController.sol";
import "../../interfaces/IStandardizedYield.sol";

import "../RewardManager/RewardManager.sol";

/**
Invariants to maintain:
- before any changes to active balance, updateAndDistributeRewards() must be called
 */
abstract contract PendleGauge is RewardManager, IPGauge {
    using PMath for uint256;
    using SafeERC20 for IERC20;
    using ArrayLib for address[];

    address private immutable SY;

    uint256 internal constant TOKENLESS_PRODUCTION = 40;

    address internal immutable PENDLE;
    IPVeToken internal immutable vePENDLE;
    address internal immutable gaugeController;

    uint256 public totalActiveSupply;
    mapping(address => uint256) public activeBalance;

    constructor(address _SY, address _vePendle, address _gaugeController) {
        SY = _SY;
        vePENDLE = IPVeToken(_vePendle);
        gaugeController = _gaugeController;
        PENDLE = IPGaugeController(gaugeController).pendle();
    }

    /**
     * @dev Since rewardShares is based on activeBalance, user's activeBalance must be updated AFTER
        rewards is updated
     * @dev It's intended to have user's activeBalance updated when rewards is redeemed
     */
    function _redeemRewards(address user) internal virtual returns (uint256[] memory rewardsOut) {
        _updateAndDistributeRewards(user);
        _updateUserActiveBalance(user);
        rewardsOut = _doTransferOutRewards(user, user);
        emit RedeemRewards(user, rewardsOut);
    }

    function _updateUserActiveBalance(address user) internal virtual {
        _updateUserActiveBalanceForTwo(user, address(0));
    }

    function _updateUserActiveBalanceForTwo(address user1, address user2) internal virtual {
        if (user1 != address(0) && user1 != address(this)) _updateUserActiveBalancePrivate(user1);
        if (user2 != address(0) && user2 != address(this)) _updateUserActiveBalancePrivate(user2);
    }

    /**
     * @dev should only be callable from `_updateUserActiveBalanceForTwo` to guarantee user != address(0) && user != address(this)
     */
    function _updateUserActiveBalancePrivate(address user) private {
        assert(user != address(0) && user != address(this));

        uint256 lpBalance = _stakedBalance(user);
        uint256 veBoostedLpBalance = _calcVeBoostedLpBalance(user, lpBalance);

        uint256 newActiveBalance = PMath.min(veBoostedLpBalance, lpBalance);

        totalActiveSupply = totalActiveSupply - activeBalance[user] + newActiveBalance;
        activeBalance[user] = newActiveBalance;

        emit UpdateActiveBalance(user, newActiveBalance);
    }

    function _calcVeBoostedLpBalance(address user, uint256 lpBalance) internal virtual returns (uint256) {
        (uint256 vePendleSupply, uint256 vePendleBalance) = vePENDLE.totalSupplyAndBalanceCurrent(user);
        // Inspired by Curve's Gauge
        uint256 veBoostedLpBalance = (lpBalance * TOKENLESS_PRODUCTION) / 100;
        if (vePendleSupply > 0) {
            veBoostedLpBalance +=
                (((_totalStaked() * vePendleBalance) / vePendleSupply) * (100 - TOKENLESS_PRODUCTION)) /
                100;
        }
        return veBoostedLpBalance;
    }

    function _redeemExternalReward() internal virtual override {
        IStandardizedYield(SY).claimRewards(address(this));
        IPGaugeController(gaugeController).redeemMarketReward();
    }

    function _stakedBalance(address user) internal view virtual returns (uint256);

    function _totalStaked() internal view virtual returns (uint256);

    function _rewardSharesTotal() internal view virtual override returns (uint256) {
        return totalActiveSupply;
    }

    function _rewardSharesUser(address user) internal view virtual override returns (uint256) {
        return activeBalance[user];
    }

    function _getRewardTokens() internal view virtual override returns (address[] memory) {
        address[] memory SYRewards = IStandardizedYield(SY).getRewardTokens();
        if (SYRewards.contains(PENDLE)) return SYRewards;
        return SYRewards.append(PENDLE);
    }

    function _beforeTokenTransfer(address from, address to, uint256) internal virtual {
        _updateAndDistributeRewardsForTwo(from, to);
    }

    function _afterTokenTransfer(address from, address to, uint256) internal virtual {
        _updateUserActiveBalanceForTwo(from, to);
    }
}
PendleMarketV6.sol 395 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;

import "../../interfaces/IPMarket.sol";
import "../../interfaces/IPMarketFactory.sol";
import "../../interfaces/IPMarketSwapCallback.sol";

import "../erc20/PendleERC20.sol";
import "./PendleGauge.sol";
import "./OracleLib.sol";
import "../libraries/StringLib.sol";

/**
Invariance to maintain:
- Internal balances totalPt & totalSy not interfered by people transferring tokens in directly
- address(0) & address(this) should never have any rewards & activeBalance accounting done. This is
    guaranteed by address(0) & address(this) check in each updateForTwo function
*/
contract PendleMarketV6 is PendleERC20, PendleGauge, IPMarket {
    using PMath for uint256;
    using PMath for int256;
    using MarketMathCore for MarketState;
    using SafeERC20 for IERC20;
    using PYIndexLib for IPYieldToken;
    using PYIndexLib for PYIndex;
    using OracleLib for OracleLib.Observation[65535];
    using StringLib for string;
    using StringLib for StringLib.slice;

    struct MarketStorage {
        int128 totalPt;
        int128 totalSy;
        // 1 SLOT = 256 bits
        uint96 lastLnImpliedRate;
        uint16 observationIndex;
        uint16 observationCardinality;
        uint16 observationCardinalityNext;
        // 1 SLOT = 144 bits
    }

    string private constant PT_NAME_PREF = "PT ";
    string private constant PT_SYMBOL_PREF = "PT-";

    string private constant LP_NAME_PREF = "PLP ";
    string private constant LP_SYMBOL_PREF = "PLP-";

    uint256 public constant VERSION = 6;

    IPPrincipalToken internal immutable PT;
    IStandardizedYield internal immutable SY;
    IPYieldToken internal immutable YT;

    address public immutable factory;
    uint256 public immutable expiry;

    int256 internal immutable scalarRoot;
    int256 internal immutable initialAnchor;
    uint80 internal immutable lnFeeRateRoot;

    MarketStorage public _storage;

    OracleLib.Observation[65535] public observations;

    modifier notExpired() {
        if (isExpired()) revert Errors.MarketExpired();
        _;
    }

    constructor(
        address _PT,
        int256 _scalarRoot,
        int256 _initialAnchor,
        uint80 _lnFeeRateRoot,
        address _vePendle,
        address _gaugeController
    )
        PendleERC20(_getLPName(_PT), _getLPSymbol(_PT), 18)
        PendleGauge(IPPrincipalToken(_PT).SY(), _vePendle, _gaugeController)
    {
        PT = IPPrincipalToken(_PT);
        SY = IStandardizedYield(PT.SY());
        YT = IPYieldToken(PT.YT());

        (_storage.observationCardinality, _storage.observationCardinalityNext) = observations.initialize(
            uint32(block.timestamp)
        );

        if (_scalarRoot <= 0) revert Errors.MarketScalarRootBelowZero(_scalarRoot);

        scalarRoot = _scalarRoot;
        initialAnchor = _initialAnchor;
        lnFeeRateRoot = _lnFeeRateRoot;
        expiry = IPPrincipalToken(_PT).expiry();
        factory = msg.sender;
    }

    function _getLPName(address _PT) internal view returns (string memory) {
        return LP_NAME_PREF.toSlice().concat(IPPrincipalToken(_PT).name().stripPrefixSlice(PT_NAME_PREF));
    }

    function _getLPSymbol(address _PT) internal view returns (string memory) {
        return LP_SYMBOL_PREF.toSlice().concat(IPPrincipalToken(_PT).symbol().stripPrefixSlice(PT_SYMBOL_PREF));
    }

    /**
     * @notice PendleMarket allows users to provide in PT & SY in exchange for LPs, which
     * will grant LP holders more exchange fee over time
     * @dev will mint as much LP as possible such that the corresponding SY and PT used do
     * not exceed `netSyDesired` and `netPtDesired`, respectively
     * @dev PT and SY should be transferred to this contract prior to calling
     * @dev will revert if PT is expired
     */
    function mint(
        address receiver,
        uint256 netSyDesired,
        uint256 netPtDesired
    ) external nonReentrant notExpired returns (uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed) {
        MarketState memory market = readState(msg.sender);
        PYIndex index = YT.newIndex();

        uint256 lpToReserve;

        (lpToReserve, netLpOut, netSyUsed, netPtUsed) = market.addLiquidity(
            netSyDesired,
            netPtDesired,
            block.timestamp
        );

        // initializing the market
        if (lpToReserve != 0) {
            market.setInitialLnImpliedRate(index, initialAnchor, block.timestamp);
            _mint(address(1), lpToReserve);
        }

        _mint(receiver, netLpOut);

        _writeState(market);

        if (_selfBalance(SY) < market.totalSy.Uint())
            revert Errors.MarketInsufficientSyReceived(_selfBalance(SY), market.totalSy.Uint());
        if (_selfBalance(PT) < market.totalPt.Uint())
            revert Errors.MarketInsufficientPtReceived(_selfBalance(PT), market.totalPt.Uint());

        emit Mint(receiver, netLpOut, netSyUsed, netPtUsed);
    }

    /**
     * @notice LP Holders can burn their LP to receive back SY & PT proportionally
     * to their share of the market
     */
    function burn(
        address receiverSy,
        address receiverPt,
        uint256 netLpToBurn
    ) external nonReentrant returns (uint256 netSyOut, uint256 netPtOut) {
        MarketState memory market = readState(msg.sender);

        _burn(address(this), netLpToBurn);

        (netSyOut, netPtOut) = market.removeLiquidity(netLpToBurn);

        if (receiverSy != address(this)) IERC20(SY).safeTransfer(receiverSy, netSyOut);
        if (receiverPt != address(this)) IERC20(PT).safeTransfer(receiverPt, netPtOut);

        _writeState(market);

        emit Burn(receiverSy, receiverPt, netLpToBurn, netSyOut, netPtOut);
    }

    /**
     * @notice Pendle Market allows swaps between PT & SY it is holding. This function
     * aims to swap an exact amount of PT to SY.
     * @dev steps working of this contract
       - The outcome amount of SY will be precomputed by MarketMathLib
       - Release the calculated amount of SY to receiver
       - Callback to msg.sender if data.length > 0
       - Ensure exactPtIn amount of PT has been transferred to this address
     * @dev will revert if PT is expired
     * @param data bytes data to be sent in the callback (if any)
     */
    function swapExactPtForSy(
        address receiver,
        uint256 exactPtIn,
        bytes calldata data
    ) external nonReentrant notExpired returns (uint256 netSyOut, uint256 netSyFee) {
        MarketState memory market = readState(msg.sender);
        PYIndex index = YT.newIndex();

        uint256 netSyToReserve;
        (netSyOut, netSyFee, netSyToReserve) = market.swapExactPtForSy(index, exactPtIn, block.timestamp);

        if (receiver != address(this)) IERC20(SY).safeTransfer(receiver, netSyOut);
        IERC20(SY).safeTransfer(market.treasury, netSyToReserve);

        _writeState(market);

        if (data.length > 0) {
            IPMarketSwapCallback(msg.sender).swapCallback(exactPtIn.neg(), netSyOut.Int(), data);
        }

        if (_selfBalance(PT) < market.totalPt.Uint())
            revert Errors.MarketInsufficientPtReceived(_selfBalance(PT), market.totalPt.Uint());

        if (index.syToAsset(netSyFee - netSyToReserve) == 0) {
            revert Errors.MarketZeroNetLPFee();
        }

        emit Swap(msg.sender, receiver, exactPtIn.neg(), netSyOut.Int(), netSyFee, netSyToReserve);
    }

    /**
     * @notice Pendle Market allows swaps between PT & SY it is holding. This function
     * aims to swap SY for an exact amount of PT.
     * @dev steps working of this function
       - The exact outcome amount of PT will be transferred to receiver
       - Callback to msg.sender if data.length > 0
       - Ensure the calculated required amount of SY is transferred to this address
     * @dev will revert if PT is expired
     * @param data bytes data to be sent in the callback (if any)
     */
    function swapSyForExactPt(
        address receiver,
        uint256 exactPtOut,
        bytes calldata data
    ) external nonReentrant notExpired returns (uint256 netSyIn, uint256 netSyFee) {
        MarketState memory market = readState(msg.sender);

        PYIndex index = YT.newIndex();

        uint256 netSyToReserve;
        (netSyIn, netSyFee, netSyToReserve) = market.swapSyForExactPt(index, exactPtOut, block.timestamp);

        if (receiver != address(this)) IERC20(PT).safeTransfer(receiver, exactPtOut);
        IERC20(SY).safeTransfer(market.treasury, netSyToReserve);

        _writeState(market);

        if (data.length > 0) {
            IPMarketSwapCallback(msg.sender).swapCallback(exactPtOut.Int(), netSyIn.neg(), data);
        }

        // have received enough SY
        if (_selfBalance(SY) < market.totalSy.Uint())
            revert Errors.MarketInsufficientSyReceived(_selfBalance(SY), market.totalSy.Uint());

        if (index.syToAsset(netSyFee - netSyToReserve) == 0) {
            revert Errors.MarketZeroNetLPFee();
        }

        emit Swap(msg.sender, receiver, exactPtOut.Int(), netSyIn.neg(), netSyFee, netSyToReserve);
    }

    /// @notice forces balances to match reserves
    function skim() external nonReentrant {
        MarketState memory market = readState(msg.sender);
        uint256 excessPt = _selfBalance(PT) - market.totalPt.Uint();
        uint256 excessSy = _selfBalance(SY) - market.totalSy.Uint();
        if (excessPt != 0) IERC20(PT).safeTransfer(market.treasury, excessPt);
        if (excessSy != 0) IERC20(SY).safeTransfer(market.treasury, excessSy);
    }

    /**
     * @notice redeems the user's reward
     * @return amount of reward token redeemed, in the same order as `getRewardTokens()`
     */
    function redeemRewards(address user) external nonReentrant returns (uint256[] memory) {
        return _redeemRewards(user);
    }

    /// @notice returns the list of reward tokens
    function getRewardTokens() external view returns (address[] memory) {
        return _getRewardTokens();
    }

    /*///////////////////////////////////////////////////////////////
                                ORACLE
    //////////////////////////////////////////////////////////////*/

    function observe(uint32[] memory secondsAgos) external view returns (uint216[] memory lnImpliedRateCumulative) {
        return
            observations.observe(
                uint32(block.timestamp),
                secondsAgos,
                _storage.lastLnImpliedRate,
                _storage.observationIndex,
                _storage.observationCardinality
            );
    }

    function increaseObservationsCardinalityNext(uint16 cardinalityNext) external nonReentrant {
        uint16 cardinalityNextOld = _storage.observationCardinalityNext;
        uint16 cardinalityNextNew = observations.grow(cardinalityNextOld, cardinalityNext);
        if (cardinalityNextOld != cardinalityNextNew) {
            _storage.observationCardinalityNext = cardinalityNextNew;
            emit IncreaseObservationCardinalityNext(cardinalityNextOld, cardinalityNextNew);
        }
    }

    /*///////////////////////////////////////////////////////////////
                                READ/WRITE STATES
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice read the state of the market from storage into memory for gas-efficient manipulation
     */
    function readState(address router) public view returns (MarketState memory market) {
        market.totalPt = _storage.totalPt;
        market.totalSy = _storage.totalSy;
        market.totalLp = totalSupply().Int();

        uint80 overriddenFee;

        (market.treasury, overriddenFee, market.reserveFeePercent) = IPMarketFactory(factory).getMarketConfig(
            address(this),
            router
        );

        market.lnFeeRateRoot = overriddenFee == 0 ? lnFeeRateRoot : overriddenFee;
        market.scalarRoot = scalarRoot;
        market.expiry = expiry;

        market.lastLnImpliedRate = _storage.lastLnImpliedRate;
    }

    /// @notice write back the state of the market from memory to storage
    function _writeState(MarketState memory market) internal {
        uint96 lastLnImpliedRate96 = market.lastLnImpliedRate.Uint96();
        int128 totalPt128 = market.totalPt.Int128();
        int128 totalSy128 = market.totalSy.Int128();

        (uint16 observationIndex, uint16 observationCardinality) = observations.write(
            _storage.observationIndex,
            uint32(block.timestamp),
            _storage.lastLnImpliedRate,
            _storage.observationCardinality,
            _storage.observationCardinalityNext
        );

        _storage.totalPt = totalPt128;
        _storage.totalSy = totalSy128;
        _storage.lastLnImpliedRate = lastLnImpliedRate96;
        _storage.observationIndex = observationIndex;
        _storage.observationCardinality = observationCardinality;

        emit UpdateImpliedRate(block.timestamp, market.lastLnImpliedRate);
    }

    function getNonOverrideLnFeeRateRoot() external view returns (uint80) {
        return lnFeeRateRoot;
    }

    /*///////////////////////////////////////////////////////////////
                            TRIVIAL FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function readTokens() external view returns (IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT) {
        _SY = SY;
        _PT = PT;
        _YT = YT;
    }

    function isExpired() public view returns (bool) {
        return MiniHelpers.isCurrentlyExpired(expiry);
    }

    function reentrancyGuardEntered() external view override(PendleERC20, IPMarket) returns (bool) {
        return _reentrancyGuardEntered();
    }

    /*///////////////////////////////////////////////////////////////
                    PENDLE GAUGE - RELATED
    //////////////////////////////////////////////////////////////*/

    function _stakedBalance(address user) internal view override returns (uint256) {
        return balanceOf(user);
    }

    function _totalStaked() internal view override returns (uint256) {
        return totalSupply();
    }

    // solhint-disable-next-line ordering
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal override(PendleERC20, PendleGauge) {
        PendleGauge._beforeTokenTransfer(from, to, amount);
    }

    // solhint-disable-next-line ordering
    function _afterTokenTransfer(address from, address to, uint256 amount) internal override(PendleERC20, PendleGauge) {
        PendleGauge._afterTokenTransfer(from, to, amount);
    }
}
RewardManager.sol 82 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "./RewardManagerAbstract.sol";

/// NOTE: This RewardManager is used with SY & YTv2 & PendleMarket. For YTv1, it will use RewardManagerAbstract
/// NOTE: RewardManager must not have duplicated rewardTokens
abstract contract RewardManager is RewardManagerAbstract {
    using PMath for uint256;
    using ArrayLib for uint256[];

    uint256 public lastRewardBlock;

    mapping(address => RewardState) public rewardState;

    function _updateRewardIndex()
        internal
        virtual
        override
        returns (address[] memory tokens, uint256[] memory indexes)
    {
        tokens = _getRewardTokens();
        indexes = new uint256[](tokens.length);

        if (tokens.length == 0) return (tokens, indexes);

        if (lastRewardBlock != block.number) {
            // if we have not yet update the index for this block
            lastRewardBlock = block.number;

            uint256 totalShares = _rewardSharesTotal();

            _redeemExternalReward();

            for (uint256 i = 0; i < tokens.length; ++i) {
                address token = tokens[i];

                // the entire token balance of the contract must be the rewards of the contract

                RewardState memory _state = rewardState[token];
                (uint256 lastBalance, uint256 index) = (_state.lastBalance, _state.index);

                uint256 accrued = _selfBalance(tokens[i]) - lastBalance;

                if (index == 0) index = INITIAL_REWARD_INDEX;
                if (totalShares != 0) index += accrued.divDown(totalShares);

                rewardState[token] = RewardState({
                    index: index.Uint128(),
                    lastBalance: (lastBalance + accrued).Uint128()
                });
                indexes[i] = index;
            }
        } else {
            for (uint256 i = 0; i < tokens.length; i++) {
                indexes[i] = rewardState[tokens[i]].index;
            }
        }
    }

    /// @dev this function doesn't need redeemExternal since redeemExternal is bundled in updateRewardIndex
    /// @dev this function also has to update rewardState.lastBalance
    function _doTransferOutRewards(
        address user,
        address receiver
    ) internal virtual override returns (uint256[] memory rewardAmounts) {
        address[] memory tokens = _getRewardTokens();
        rewardAmounts = new uint256[](tokens.length);
        for (uint256 i = 0; i < tokens.length; i++) {
            rewardAmounts[i] = userReward[tokens[i]][user].accrued;
            if (rewardAmounts[i] != 0) {
                userReward[tokens[i]][user].accrued = 0;
                rewardState[tokens[i]].lastBalance -= rewardAmounts[i].Uint128();
                _transferOut(tokens[i], receiver, rewardAmounts[i]);
            }
        }
    }

    function _getRewardTokens() internal view virtual returns (address[] memory);

    function _rewardSharesTotal() internal view virtual returns (uint256);
}
RewardManagerAbstract.sol 78 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "../../interfaces/IRewardManager.sol";

import "../libraries/ArrayLib.sol";
import "../libraries/TokenHelper.sol";
import "../libraries/math/PMath.sol";

import "./RewardManagerAbstract.sol";

/// NOTE: RewardManager must not have duplicated rewardTokens
abstract contract RewardManagerAbstract is IRewardManager, TokenHelper {
    using PMath for uint256;

    uint256 internal constant INITIAL_REWARD_INDEX = 1;

    struct RewardState {
        uint128 index;
        uint128 lastBalance;
    }

    struct UserReward {
        uint128 index;
        uint128 accrued;
    }

    // [token] => [user] => (index,accrued)
    mapping(address => mapping(address => UserReward)) public userReward;

    function _updateAndDistributeRewards(address user) internal virtual {
        _updateAndDistributeRewardsForTwo(user, address(0));
    }

    function _updateAndDistributeRewardsForTwo(address user1, address user2) internal virtual {
        (address[] memory tokens, uint256[] memory indexes) = _updateRewardIndex();
        if (tokens.length == 0) return;

        if (user1 != address(0) && user1 != address(this)) _distributeRewardsPrivate(user1, tokens, indexes);
        if (user2 != address(0) && user2 != address(this)) _distributeRewardsPrivate(user2, tokens, indexes);
    }

    // should only be callable from `_updateAndDistributeRewardsForTwo` to guarantee user != address(0) && user != address(this)
    function _distributeRewardsPrivate(address user, address[] memory tokens, uint256[] memory indexes) private {
        assert(user != address(0) && user != address(this));

        uint256 userShares = _rewardSharesUser(user);

        for (uint256 i = 0; i < tokens.length; ++i) {
            address token = tokens[i];
            uint256 index = indexes[i];
            uint256 userIndex = userReward[token][user].index;

            if (userIndex == 0) {
                userIndex = INITIAL_REWARD_INDEX.Uint128();
            }

            if (userIndex == index || index == 0) continue;

            uint256 deltaIndex = index - userIndex;
            uint256 rewardDelta = userShares.mulDown(deltaIndex);
            uint256 rewardAccrued = userReward[token][user].accrued + rewardDelta;

            userReward[token][user] = UserReward({index: index.Uint128(), accrued: rewardAccrued.Uint128()});
        }
    }

    function _updateRewardIndex() internal virtual returns (address[] memory tokens, uint256[] memory indexes);

    function _redeemExternalReward() internal virtual;

    function _doTransferOutRewards(
        address user,
        address receiver
    ) internal virtual returns (uint256[] memory rewardAmounts);

    function _rewardSharesUser(address user) internal view virtual returns (uint256);
}
PYIndex.sol 50 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "../../interfaces/IPYieldToken.sol";
import "../../interfaces/IPPrincipalToken.sol";

import "./SYUtils.sol";
import "../libraries/math/PMath.sol";

type PYIndex is uint256;

library PYIndexLib {
    using PMath for uint256;
    using PMath for int256;

    function newIndex(IPYieldToken YT) internal returns (PYIndex) {
        return PYIndex.wrap(YT.pyIndexCurrent());
    }

    function syToAsset(PYIndex index, uint256 syAmount) internal pure returns (uint256) {
        return SYUtils.syToAsset(PYIndex.unwrap(index), syAmount);
    }

    function assetToSy(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {
        return SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount);
    }

    function assetToSyUp(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {
        return SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount);
    }

    function syToAssetUp(PYIndex index, uint256 syAmount) internal pure returns (uint256) {
        uint256 _index = PYIndex.unwrap(index);
        return SYUtils.syToAssetUp(_index, syAmount);
    }

    function syToAsset(PYIndex index, int256 syAmount) internal pure returns (int256) {
        int256 sign = syAmount < 0 ? int256(-1) : int256(1);
        return sign * (SYUtils.syToAsset(PYIndex.unwrap(index), syAmount.abs())).Int();
    }

    function assetToSy(PYIndex index, int256 assetAmount) internal pure returns (int256) {
        int256 sign = assetAmount < 0 ? int256(-1) : int256(1);
        return sign * (SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount.abs())).Int();
    }

    function assetToSyUp(PYIndex index, int256 assetAmount) internal pure returns (int256) {
        int256 sign = assetAmount < 0 ? int256(-1) : int256(1);
        return sign * (SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount.abs())).Int();
    }
}
SYUtils.sol 22 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

library SYUtils {
    uint256 internal constant ONE = 1e18;

    function syToAsset(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
        return (syAmount * exchangeRate) / ONE;
    }

    function syToAssetUp(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
        return (syAmount * exchangeRate + ONE - 1) / ONE;
    }

    function assetToSy(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
        return (assetAmount * ONE) / exchangeRate;
    }

    function assetToSyUp(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
        return (assetAmount * ONE + exchangeRate - 1) / exchangeRate;
    }
}
IPGauge.sol 12 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IPGauge {
    event RedeemRewards(address indexed user, uint256[] rewardsOut);

    event UpdateActiveBalance(address indexed user, uint256 newActiveBalance);

    function totalActiveSupply() external view returns (uint256);

    function activeBalance(address user) external view returns (uint256);
}
IPGaugeController.sol 20 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IPGaugeController {
    event MarketClaimReward(address indexed market, uint256 amount);

    event ReceiveVotingResults(uint128 indexed wTime, address[] markets, uint256[] pendleAmounts);

    event UpdateMarketReward(address indexed market, uint256 pendlePerSec, uint256 incentiveEndsAt);

    function fundPendle(uint256 amount) external;

    function withdrawPendle(uint256 amount) external;

    function pendle() external returns (address);

    function redeemMarketReward() external;

    function rewardData(address pool) external view returns (uint128 pendlePerSec, uint128, uint128, uint128);
}
IPInterestManagerYT.sol 8 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IPInterestManagerYT {
    event CollectInterestFee(uint256 amountInterestFee);

    function userInterest(address user) external view returns (uint128 lastPYIndex, uint128 accruedInterest);
}
IPMarket.sol 99 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

// import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IPPrincipalToken.sol";
import "./IPYieldToken.sol";
import "./IStandardizedYield.sol";
import "./IPGauge.sol";
import "../core/Market/MarketMathCore.sol";

interface IPMarket is IPGauge, IERC20Metadata {
    event Mint(address indexed receiver, uint256 netLpMinted, uint256 netSyUsed, uint256 netPtUsed);

    event Burn(
        address indexed receiverSy,
        address indexed receiverPt,
        uint256 netLpBurned,
        uint256 netSyOut,
        uint256 netPtOut
    );

    event Swap(
        address indexed caller,
        address indexed receiver,
        int256 netPtOut,
        int256 netSyOut,
        uint256 netSyFee,
        uint256 netSyToReserve
    );

    event UpdateImpliedRate(uint256 indexed timestamp, uint256 lnLastImpliedRate);

    event IncreaseObservationCardinalityNext(
        uint16 observationCardinalityNextOld,
        uint16 observationCardinalityNextNew
    );

    function mint(
        address receiver,
        uint256 netSyDesired,
        uint256 netPtDesired
    ) external returns (uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed);

    function burn(
        address receiverSy,
        address receiverPt,
        uint256 netLpToBurn
    ) external returns (uint256 netSyOut, uint256 netPtOut);

    function swapExactPtForSy(
        address receiver,
        uint256 exactPtIn,
        bytes calldata data
    ) external returns (uint256 netSyOut, uint256 netSyFee);

    function swapSyForExactPt(
        address receiver,
        uint256 exactPtOut,
        bytes calldata data
    ) external returns (uint256 netSyIn, uint256 netSyFee);

    function redeemRewards(address user) external returns (uint256[] memory);

    function readState(address router) external view returns (MarketState memory market);

    function observe(uint32[] memory secondsAgos) external view returns (uint216[] memory lnImpliedRateCumulative);

    function increaseObservationsCardinalityNext(uint16 cardinalityNext) external;

    function readTokens() external view returns (IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT);

    function getRewardTokens() external view returns (address[] memory);

    function isExpired() external view returns (bool);

    function expiry() external view returns (uint256);

    function observations(
        uint256 index
    ) external view returns (uint32 blockTimestamp, uint216 lnImpliedRateCumulative, bool initialized);

    function _storage()
        external
        view
        returns (
            int128 totalPt,
            int128 totalSy,
            uint96 lastLnImpliedRate,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext
        );

    function getNonOverrideLnFeeRateRoot() external view returns (uint80);

    function VERSION() external pure returns (uint256);

    function reentrancyGuardEntered() external view returns (bool);
}
IPMarketFactory.sol 35 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IPMarketFactory {
    event SetOverriddenFee(address indexed router, address indexed market, uint80 lnFeeRateRoot);

    event CreateNewMarket(
        address indexed market,
        address indexed PT,
        int256 scalarRoot,
        int256 initialAnchor,
        uint256 lnFeeRateRoot
    );

    event NewTreasuryAndFeeReserve(address indexed treasury, uint8 reserveFeePercent);

    function VERSION() external pure returns (uint256);

    function isValidMarket(address market) external view returns (bool);

    // If this is changed, change the readState function in market as well
    function getMarketConfig(
        address market,
        address router
    ) external view returns (address treasury, uint80 overriddenFee, uint8 reserveFeePercent);

    function createNewMarket(
        address PT,
        int256 scalarRoot,
        int256 initialAnchor,
        uint80 lnFeeRateRoot
    ) external returns (address market);

    function setOverriddenFee(address router, address market, uint80 newFee) external;
}
IPMarketSwapCallback.sol 6 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IPMarketSwapCallback {
    function swapCallback(int256 ptToAccount, int256 syToAccount, bytes calldata data) external;
}
IPPrincipalToken.sol 25 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IPPrincipalToken is IERC20Metadata {
    function burnByYT(address user, uint256 amount) external;

    function mintByYT(address user, uint256 amount) external;

    function initialize(address _YT) external;

    function SY() external view returns (address);

    function YT() external view returns (address);

    function factory() external view returns (address);

    function expiry() external view returns (uint256);

    function isExpired() external view returns (bool);

    function VERSION() external pure returns (uint256);

    function reentrancyGuardEntered() external view returns (bool);
}
IPVeToken.sol 19 lines
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.0;

interface IPVeToken {
    // ============= USER INFO =============

    function balanceOf(address user) external view returns (uint128);

    function positionData(address user) external view returns (uint128 amount, uint128 expiry);

    // ============= META DATA =============

    function totalSupplyStored() external view returns (uint128);

    function totalSupplyCurrent() external returns (uint128);

    function totalSupplyAndBalanceCurrent(address user) external returns (uint128, uint128);
}
IPYieldToken.sol 66 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IRewardManager.sol";
import "./IPInterestManagerYT.sol";

interface IPYieldToken is IERC20Metadata, IRewardManager, IPInterestManagerYT {
    event NewInterestIndex(uint256 indexed newIndex);

    event Mint(
        address indexed caller,
        address indexed receiverPT,
        address indexed receiverYT,
        uint256 amountSyToMint,
        uint256 amountPYOut
    );

    event Burn(address indexed caller, address indexed receiver, uint256 amountPYToRedeem, uint256 amountSyOut);

    event RedeemRewards(address indexed user, uint256[] amountRewardsOut);

    event RedeemInterest(address indexed user, uint256 interestOut);

    event CollectRewardFee(address indexed rewardToken, uint256 amountRewardFee);

    function mintPY(address receiverPT, address receiverYT) external returns (uint256 amountPYOut);

    function redeemPY(address receiver) external returns (uint256 amountSyOut);

    function redeemPYMulti(
        address[] calldata receivers,
        uint256[] calldata amountPYToRedeems
    ) external returns (uint256[] memory amountSyOuts);

    function redeemDueInterestAndRewards(
        address user,
        bool redeemInterest,
        bool redeemRewards
    ) external returns (uint256 interestOut, uint256[] memory rewardsOut);

    function rewardIndexesCurrent() external returns (uint256[] memory);

    function pyIndexCurrent() external returns (uint256);

    function pyIndexStored() external view returns (uint256);

    function getRewardTokens() external view returns (address[] memory);

    function SY() external view returns (address);

    function PT() external view returns (address);

    function factory() external view returns (address);

    function expiry() external view returns (uint256);

    function isExpired() external view returns (bool);

    function doCacheIndexSameBlock() external view returns (bool);

    function pyIndexLastUpdatedBlock() external view returns (uint128);

    function reentrancyGuardEntered() external view returns (bool);

    function VERSION() external pure returns (uint256);
}
IRewardManager.sol 6 lines
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IRewardManager {
    function userReward(address token, address user) external view returns (uint128 index, uint128 accrued);
}
IStandardizedYield.sol 167 lines
// SPDX-License-Identifier: GPL-3.0-or-later
/*
 * MIT License
 * ===========
 *
 * Permission is hereby granted, free of charge, 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.
 *
 * 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
 */

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

interface IStandardizedYield is IERC20Metadata {
    /// @dev Emitted when any base tokens is deposited to mint shares
    event Deposit(
        address indexed caller,
        address indexed receiver,
        address indexed tokenIn,
        uint256 amountDeposited,
        uint256 amountSyOut
    );

    /// @dev Emitted when any shares are redeemed for base tokens
    event Redeem(
        address indexed caller,
        address indexed receiver,
        address indexed tokenOut,
        uint256 amountSyToRedeem,
        uint256 amountTokenOut
    );

    /// @dev check `assetInfo()` for more information
    enum AssetType {
        TOKEN,
        LIQUIDITY
    }

    /// @dev Emitted when (`user`) claims their rewards
    event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts);

    /**
     * @notice mints an amount of shares by depositing a base token.
     * @param receiver shares recipient address
     * @param tokenIn address of the base tokens to mint shares
     * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`)
     * @param minSharesOut reverts if amount of shares minted is lower than this
     * @return amountSharesOut amount of shares minted
     * @dev Emits a {Deposit} event
     *
     * Requirements:
     * - (`tokenIn`) must be a valid base token.
     */
    function deposit(
        address receiver,
        address tokenIn,
        uint256 amountTokenToDeposit,
        uint256 minSharesOut
    ) external payable returns (uint256 amountSharesOut);

    /**
     * @notice redeems an amount of base tokens by burning some shares
     * @param receiver recipient address
     * @param amountSharesToRedeem amount of shares to be burned
     * @param tokenOut address of the base token to be redeemed
     * @param minTokenOut reverts if amount of base token redeemed is lower than this
     * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender`
     * @return amountTokenOut amount of base tokens redeemed
     * @dev Emits a {Redeem} event
     *
     * Requirements:
     * - (`tokenOut`) must be a valid base token.
     */
    function redeem(
        address receiver,
        uint256 amountSharesToRedeem,
        address tokenOut,
        uint256 minTokenOut,
        bool burnFromInternalBalance
    ) external returns (uint256 amountTokenOut);

    /**
     * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account
     * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy
     he can mint must be X * exchangeRate / 1e18
     * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication
     & division
     */
    function exchangeRate() external view returns (uint256 res);

    /**
     * @notice claims reward for (`user`)
     * @param user the user receiving their rewards
     * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
     * @dev
     * Emits a `ClaimRewards` event
     * See {getRewardTokens} for list of reward tokens
     */
    function claimRewards(address user) external returns (uint256[] memory rewardAmounts);

    /**
     * @notice get the amount of unclaimed rewards for (`user`)
     * @param user the user to check for
     * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
     */
    function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts);

    function rewardIndexesCurrent() external returns (uint256[] memory indexes);

    function rewardIndexesStored() external view returns (uint256[] memory indexes);

    /**
     * @notice returns the list of reward token addresses
     */
    function getRewardTokens() external view returns (address[] memory);

    /**
     * @notice returns the address of the underlying yield token
     */
    function yieldToken() external view returns (address);

    /**
     * @notice returns all tokens that can mint this SY
     */
    function getTokensIn() external view returns (address[] memory res);

    /**
     * @notice returns all tokens that can be redeemed by this SY
     */
    function getTokensOut() external view returns (address[] memory res);

    function isValidTokenIn(address token) external view returns (bool);

    function isValidTokenOut(address token) external view returns (bool);

    function previewDeposit(
        address tokenIn,
        uint256 amountTokenToDeposit
    ) external view returns (uint256 amountSharesOut);

    function previewRedeem(
        address tokenOut,
        uint256 amountSharesToRedeem
    ) external view returns (uint256 amountTokenOut);

    /**
     * @notice This function contains information to interpret what the asset is
     * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens,
        2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the chain)
     * @return assetAddress the address of the asset
     * @return assetDecimals the decimals of the asset
     */
    function assetInfo() external view returns (AssetType assetType, address assetAddress, uint8 assetDecimals);
}
IWETH.sol 34 lines
// SPDX-License-Identifier: GPL-3.0-or-later
/*
 * MIT License
 * ===========
 *
 * Permission is hereby granted, free of charge, 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.
 *
 * 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
 */
pragma solidity ^0.8.0;

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

interface IWETH is IERC20 {
    event Deposit(address indexed dst, uint256 wad);
    event Withdrawal(address indexed src, uint256 wad);

    function deposit() external payable;

    function withdraw(uint256 wad) external;
}

Read Contract

VERSION 0xffa1ad74 → uint256
_storage 0xc3fb90d6 → int128, int128, uint96, uint16, uint16, uint16
activeBalance 0x0892cd8b → uint256
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
decimals 0x313ce567 → uint8
expiry 0xe184c9be → uint256
factory 0xc45a0155 → address
getNonOverrideLnFeeRateRoot 0xe4f8b2e9 → uint80
getRewardTokens 0xc4f59f9b → address[]
isExpired 0x2f13b60c → bool
lastRewardBlock 0xa9f8d181 → uint256
name 0x06fdde03 → string
observations 0x252c09d7 → uint32, uint216, bool
observe 0x883bdbfd → uint216[]
readState 0x794052f3 → tuple
readTokens 0x2c8ce6bc → address, address, address
reentrancyGuardEntered 0xd2c725e0 → bool
rewardState 0xea64a820 → uint128, uint128
symbol 0x95d89b41 → string
totalActiveSupply 0x72069264 → uint256
totalSupply 0x18160ddd → uint256
userReward 0x5cbadbe4 → uint128, uint128

Write Contract 10 functions

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

approve 0x095ea7b3
address spender
uint256 amount
returns: bool
burn 0xf6b911bc
address receiverSy
address receiverPt
uint256 netLpToBurn
returns: uint256, uint256
increaseObservationsCardinalityNext 0x37d45e3a
uint16 cardinalityNext
mint 0x156e29f6
address receiver
uint256 netSyDesired
uint256 netPtDesired
returns: uint256, uint256, uint256
redeemRewards 0x9262187b
address user
returns: uint256[]
skim 0x1dd19cb4
No parameters
swapExactPtForSy 0x29910b11
address receiver
uint256 exactPtIn
bytes data
returns: uint256, uint256
swapSyForExactPt 0x5b709f17
address receiver
uint256 exactPtOut
bytes data
returns: uint256, uint256
transfer 0xa9059cbb
address to
uint256 amount
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 amount
returns: bool

Recent Transactions

No transactions found for this address