Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x3b109aa111BdF11B30350CdfAd7e9Cf091421Aa4
Balance 0 ETH
Nonce 1
Code Size 12600 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

12600 bytes
0x60806040526004361015610011575f80fd5b5f3560e01c80626f2ca31461019357806306fdde031461018e578063095ea7b314610189578063122f79c01461018457806318160ddd1461017f57806323b872dd1461017a578063313ce5671461017557806337bdba35146101705780633f707e6b1461016b57806347e7ef241461016657806356018c461461016157806370a082311461015c57806373f4256114610157578063830f953a1461015257806389e90a7a1461014d57806395d89b4114610148578063a9059cbb14610143578063abcd28211461013e578063b8404caf14610139578063bc9cbe3c14610134578063c3f1fad81461012f578063f63ddc7d1461012a578063fbfa77cf146101255763feaf968c14610120575f80fd5b61105a565b611017565b610f59565b610e23565b610df6565b610d15565b610c6d565b610c3d565b610be0565b610a61565b610882565b610861565b61083a565b610800565b610717565b6106b3565b6105fb565b6105a4565b61056c565b610531565b610368565b6102e0565b61026e565b6101a6565b5f9103126101a257565b5f80fd5b346101a2575f3660031901126101a2576101be6116a7565b60409081519060208083018184528251809152818585019301915f5b8281106101e75785850386f35b835180516001600160a01b039081168752818401511686840152870151151587860152606090940193928101926001016101da565b91908251928382525f5b848110610246575050825f602080949584010152601f8019910116010190565b602081830181015184830182015201610226565b90602061026b92818152019061021c565b90565b346101a2575f3660031901126101a2576102cb60405161028d81611328565b601481527f4c6c616d61506179526f757465724f7261636c65000000000000000000000000602082015260405191829160208352602083019061021c565b0390f35b6001600160a01b038116036101a257565b346101a25760403660031901126101a2576102fc6004356102cf565b60046040517f6fa34337000000000000000000000000000000000000000000000000000000008152fd5b604435906001600160d81b03821682036101a257565b608435906001600160d81b03821682036101a257565b606435906001600160d81b03821682036101a257565b346101a25760803660031901126101a257600435610385816102cf565b602435610391816102cf565b610399610326565b91606435906001600160a01b0390817f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba1633036105015782156104d75782610448926103e8610454954261151f565b6104046103f6898987612cc1565b5f52600260205260405f2090565b556040519182526001600160d81b03871691818716918416907fae6bb0ccd81134c38fbccd7c60256cfdf3a6379b8833bbea45634a8dd8ca190f90602090a46125e9565b6001600160a01b031690565b91823b156101a2576040517fc355f3430000000000000000000000000000000000000000000000000000000081526001600160a01b0390921660048301526001600160d81b03166024820152905f908290818381604481015b03925af180156104d2576104bd57005b806104ca6104d09261130f565b80610198565b005b611540565b60046040517fcacbd3b8000000000000000000000000000000000000000000000000000000008152fd5b60246040517f0ee1e70d000000000000000000000000000000000000000000000000000000008152336004820152fd5b346101a2575f8060031936011261056957805460ff1615610558576020905b604051908152f35b506020670de0b6b3a7640000610550565b80fd5b346101a25760603660031901126101a2576105886004356102cf565b6105936024356102cf565b600460405163253aeffd60e21b8152fd5b346101a2575f3660031901126101a257602060405160128152f35b60609060031901126101a2576004356105d7816102cf565b906024356105e4816102cf565b906044356001600160d81b03811681036101a25790565b346101a257610609366105bf565b90610613836125e9565b61061e838386612cc1565b90815f52600260205260405f20549485156106765750428511610645576104d09450611d66565b602485604051907f4e0eef220000000000000000000000000000000000000000000000000000000082526004820152fd5b604051630d1744b360e01b81526001600160a01b03918216600482015290841660248201526001600160d81b0385166044820152606490fd5b0390fd5b346101a25760203660031901126101a25760043567ffffffffffffffff8082116101a257366023830112156101a25781600401359081116101a2573660248260051b840101116101a25760246107119261070b612d73565b016111eb565b60018055005b346101a2575f604036600319011261056957600435610735816102cf565b6024357f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba916001600160a01b038084163303610501578261079891869561077a612d73565b61079083610787876125e9565b92309088612e29565b168093612e98565b803b156107fc576024839260405194859384927fb6b55f2500000000000000000000000000000000000000000000000000000000845260048401525af180156104d2576107ed575b506107ea60018055565b80f35b6107f69061130f565b5f6107e0565b5050fd5b346101a2575f3660031901126101a25760206040517f00000000000000000000000000000000000000000000000000000000000000028152f35b346101a25760203660031901126101a257602061055060043561085c816102cf565b611160565b346101a2575f3660031901126101a257602060ff5f54166040519015158152f35b346101a25760c03660031901126101a25760043561089f816102cf565b6024356108ab816102cf565b6108b3610326565b6064356108bf816102cf565b6108c761033c565b9260a435926001600160a01b03807f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba16330361050157610906876125e9565b85156104d75761091783858a612cc1565b9561092a875f52600260205260405f2090565b5415610a2957610947985f809a81995f52600260205260405f2090565b55610952824261151f565b6109606103f68b8a85612cc1565b556040519182526001600160d81b03891691848816918516907fae6bb0ccd81134c38fbccd7c60256cfdf3a6379b8833bbea45634a8dd8ca190f90602090a41691823b15610a25576040517fc6a647710000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201526001600160d81b03928316602482015293166044840152939093166064820152918290608490829084905af180156104d257610a18575080f35b806104ca6107ea9261130f565b8480fd5b604051630d1744b360e01b81526001600160a01b038a81166004830152861660248201526001600160d81b0385166044820152606490fd5b346101a25760a03660031901126101a257600435610a7e816102cf565b60243560443591610a8e836102cf565b610a96610352565b90608435906001600160a01b03907f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba828116330361050157610ad6612d73565b610adf826125e9565b84156104d757610b0d8785938a610b046103f68b610afd8c4261151f565b938a612cc1565b55309086612e29565b1692610b1a868584612e98565b6040519081526001600160d81b038516928781169216907fae6bb0ccd81134c38fbccd7c60256cfdf3a6379b8833bbea45634a8dd8ca190f90602090a4803b156101a2576040517f5ed1b15d00000000000000000000000000000000000000000000000000000000815260048101939093526001600160a01b039390931660248301526001600160d81b03166044820152905f908290606490829084905af180156104d257610bcd575b6104d060018055565b806104ca610bda9261130f565b5f610bc4565b346101a2575f3660031901126101a2576102cb604051610bff81611328565b600481527f4c50524f00000000000000000000000000000000000000000000000000000000602082015260405191829160208352602083019061021c565b346101a25760403660031901126101a257610c596004356102cf565b6020610c636110fd565b6040519015158152f35b346101a257610c7b366105bf565b906001600160a01b037f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba16330361050157610cb5836125e9565b610cc0838386612cc1565b90815f52600260205260405f205415610cdd576104d09450611d66565b604051630d1744b360e01b81526001600160a01b038681166004830152841660248201526001600160d81b0385166044820152606490fd5b346101a2575f60403660031901126105695760043590610d34826102cf565b602435917f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba6001600160a01b03808216330361050157610d73836125e9565b16803b15610df2578380916024604051809481937fbfda0b450000000000000000000000000000000000000000000000000000000083528a60048401525af180156104d257610ddb575b50610dd56107ea9394610dcf8461295b565b9061154b565b91612dc8565b6107ea93610deb610dd59261130f565b9350610dbd565b8380fd5b346101a257610e0d610e07366105bf565b91612cc1565b5f526002602052602060405f2054604051908152f35b346101a25760203660031901126101a257600435610e40816102cf565b6001600160a01b03907f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba828116330361050157610e7f610448836125e9565b803b156101a2575f80916004604051809481937fa3f83f6e0000000000000000000000000000000000000000000000000000000083525af180156104d257610f46575b506040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152926020908490602490829086165afa9182156104d2576104d0935f93610f16575b50612dc8565b610f3891935060203d8111610f3f575b610f308183611344565b810190611569565b915f610f10565b503d610f26565b806104ca610f539261130f565b5f610ec2565b346101a257610f67366105bf565b916001600160a01b0390817f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba16330361050157610fa3816125e9565b90610fb26103f6868684612cc1565b541561067657501691823b156101a2576040517f5b0c2f2f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390921660048301526001600160d81b03166024820152905f908290818381604481016104ad565b346101a2575f3660031901126101a25760206040516001600160a01b037f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba168152f35b346101a2575f8060031936011261056957611073611dfd565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116110cc5760a09181156110c3575b6040519181835260208301528060408301524260608301526080820152f35b600191506110a4565b602490604051907f24775e060000000000000000000000000000000000000000000000000000000082526004820152fd5b6001600160a01b037f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba16331480611154575b61114457600460405163253aeffd60e21b8152fd5b600160ff195f5416175f55600190565b5060ff5f54161561112f565b6001600160a01b03807f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba169116036111ac575f5460ff16156111a0575f90565b670de0b6b3a764000090565b5f90565b634e487b7160e01b5f52603260045260245ffd5b91908110156111e65760051b81013590605e19813603018212156101a2570190565b6111b0565b906001600160a01b036040517f8da5cb5b000000000000000000000000000000000000000000000000000000008152602081600481857f0000000000000000000000008624f61cc6e5a86790e173712afdd480fa8b73ba165afa9081156104d2575f916112c0575b5016330361128f57801561128b575f5b81811061126f57505050565b8061128561128060019385876111c4565b611448565b01611263565b5050565b6040517f39d014ff000000000000000000000000000000000000000000000000000000008152336004820152602490fd5b6112e1915060203d81116112e7575b6112d98183611344565b810190612cac565b5f611253565b503d6112cf565b908092918237015f815290565b634e487b7160e01b5f52604160045260245ffd5b67ffffffffffffffff811161132357604052565b6112fb565b6040810190811067ffffffffffffffff82111761132357604052565b90601f8019910116810190811067ffffffffffffffff82111761132357604052565b604051906060820182811067ffffffffffffffff82111761132357604052565b3d156113c0573d9067ffffffffffffffff821161132357604051916113b5601f8201601f191660200184611344565b82523d5f602084013e565b606090565b90602082526001600160a01b0381356113dd816102cf565b166020830152602081013560408301526040810135601e19823603018112156101a25701906020823592019167ffffffffffffffff81116101a25780360383136101a2578060a093606080850152816080850152848401375f828201840152601f01601f1916010190565b8035611453816102cf565b6040820135601e19833603018112156101a25782019081359167ffffffffffffffff83116101a2576020019082360382136101a2575f92839261149b604051809381936112ee565b03916020860135905af16114ad611386565b90156114e957507f581f7fcb4603641e147a9f037fab064116f10ab9f195b7de511aac72fb3a4532604051806114e43394826113c5565b0390a2565b6106af906040519182917fcb9b8ad40000000000000000000000000000000000000000000000000000000083526004830161025a565b9190820180921161152c57565b634e487b7160e01b5f52601160045260245ffd5b6040513d5f823e3d90fd5b8115611555570490565b634e487b7160e01b5f52601260045260245ffd5b908160209103126101a2575190565b67ffffffffffffffff81116113235760051b60200190565b9061159a82611578565b6040906115a982519182611344565b83815280936115ba601f1991611578565b01905f92835b8381106115ce575050505050565b815190606082019180831067ffffffffffffffff84111761132357602092845286815282878183015287858301528286010152016115c0565b8051156111e65760200190565b8051600110156111e65760400190565b8051600210156111e65760600190565b8051600310156111e65760800190565b8051600410156111e65760a00190565b8051600510156111e65760c00190565b8051600610156111e65760e00190565b8051600710156111e6576101000190565b8051600810156111e6576101200190565b8051600910156111e6576101400190565b7f00000000000000000000000000000000000000000000000000000000000000026116d181611590565b906116da611366565b6001600160a01b037f0000000000000000000000003e67cc2c7fff86d9870db9d02c43e789b52fb29616815260209161173e7f000000000000000000000000986b5e1e1755e3c2440e960477f25201b0a8bbd4848401906001600160a01b03169052565b7f000000000000000000000000000000000000000000000000000000000000000015156040838101919091529161177485611607565b5261177e84611607565b5060018114611d605761178f611366565b6001600160a01b037f0000000000000000000000004ddb1bb1ed2749995e8e490ae2ac4edaa4738ab81681526001600160a01b037f0000000000000000000000001b39ee86ec5979ba5c322b826b3ecb8c7999169916818501527f000000000000000000000000000000000000000000000000000000000000000015158184015261181985611614565b5261182384611614565b5060028114611d6057611834611366565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016818501527f00000000000000000000000000000000000000000000000000000000000000001515818401526118be85611624565b526118c884611624565b5060038114611d60576118d9611366565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016818501527f000000000000000000000000000000000000000000000000000000000000000015158184015261196385611634565b5261196d84611634565b5060048114611d605761197e611366565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016818501527f0000000000000000000000000000000000000000000000000000000000000000151581840152611a0885611644565b52611a1284611644565b5060058114611d6057611a23611366565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016818501527f0000000000000000000000000000000000000000000000000000000000000000151581840152611aad85611654565b52611ab784611654565b5060068114611d6057611ac8611366565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016818501527f0000000000000000000000000000000000000000000000000000000000000000151581840152611b5285611664565b52611b5c84611664565b5060078114611d6057611b6d611366565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016818501527f0000000000000000000000000000000000000000000000000000000000000000151581840152611bf785611674565b52611c0184611674565b5060088114611d6057600990611c15611366565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016818601527f0000000000000000000000000000000000000000000000000000000000000000151581850152611c9f86611685565b52611ca985611685565b5014611d5b57611d4490611d1b611cbe611366565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152936001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690850152565b7f0000000000000000000000000000000000000000000000000000000000000000151590830152565b611d4d82611696565b52611d5781611696565b5090565b505090565b50505090565b6001600160a01b039094939192945f938452600260205283604081205516803b15611df9576040517f807a379c0000000000000000000000000000000000000000000000000000000081526001600160a01b0390951660048601526001600160d81b03909116602485015291929182908183816044810103925af180156104d257611dee5750565b611df79061130f565b565b8280fd5b611e897f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000056bc75e2d631000007f000000000000000000000000986b5e1e1755e3c2440e960477f25201b0a8bbd47f0000000000000000000000003e67cc2c7fff86d9870db9d02c43e789b52fb296612425565b7f0000000000000000000000000000000000000000000000000000000000000002906001821461242057611f4990611f437f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000056bc75e2d631000007f0000000000000000000000001b39ee86ec5979ba5c322b826b3ecb8c799916997f0000000000000000000000004ddb1bb1ed2749995e8e490ae2ac4edaa4738ab8612425565b9061151f565b806002831461241a5750611fe390611f437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612425565b806003831461241a575061207d90611f437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612425565b806004831461241a575061211790611f437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612425565b806005831461241a57506121b190611f437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612425565b806006831461241a575061224b90611f437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612425565b806007831461241a57506122e590611f437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612425565b806008831461241a575061237f90611f437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612425565b906009829114611d575761026b9150611f437f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612425565b91505090565b905090565b6040517f27e235e30000000000000000000000000000000000000000000000000000000081523060048201529391906001600160a01b03906020908690602490829085165afa9485156104d2575f956124c6575b5084156124bd578116156124b3579261249461026b946124fd565b91806124a1575b50612d20565b916124ac919261154b565b905f61249b565b5050506064900490565b50505050505f90565b6124df91955060203d8111610f3f57610f308183611344565b935f612479565b519069ffffffffffffffffffff821682036101a257565b604051907ffeaf968c00000000000000000000000000000000000000000000000000000000825260a0826004816001600160a01b0385165afa9182156104d2575f9261259b575b505f821315612557575061026b90612d38565b6040517fef4ca0030000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526024810191909152604490fd5b909160a0823d82116125e1575b816125b560a09383611344565b8101031261056957506125c7816124e6565b506125d96080602083015192016124e6565b50905f612544565b3d91506125a8565b6001600160a01b0380821690811561293a57807f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816821461291357807f000000000000000000000000c00e94cb662c3520282e6f5717214004a7f268881682146128ec57807f00000000000000000000000000000000000000000000000000000000000000001682146128c557807f000000000000000000000000000000000000000000000000000000000000000016821461289e57807f000000000000000000000000000000000000000000000000000000000000000016821461287757807f000000000000000000000000000000000000000000000000000000000000000016821461285057807f000000000000000000000000000000000000000000000000000000000000000016821461282957807f000000000000000000000000000000000000000000000000000000000000000016821461280257807f00000000000000000000000000000000000000000000000000000000000000001682146127db577f000000000000000000000000000000000000000000000000000000000000000016146127b6576040516399cdbc8d60e01b81526001600160a01b03919091166004820152602490fd5b507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f0000000000000000000000004ddb1bb1ed2749995e8e490ae2ac4edaa4738ab890565b5050507f0000000000000000000000003e67cc2c7fff86d9870db9d02c43e789b52fb29690565b6040516399cdbc8d60e01b81526001600160a01b0384166004820152602490fd5b6001600160a01b0380821690811561293a57807f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168214612c8557807f000000000000000000000000c00e94cb662c3520282e6f5717214004a7f26888168214612c5e57807f0000000000000000000000000000000000000000000000000000000000000000168214612c3757807f0000000000000000000000000000000000000000000000000000000000000000168214612c1057807f0000000000000000000000000000000000000000000000000000000000000000168214612be957807f0000000000000000000000000000000000000000000000000000000000000000168214612bc257807f0000000000000000000000000000000000000000000000000000000000000000168214612b9b57807f0000000000000000000000000000000000000000000000000000000000000000168214612b7457807f0000000000000000000000000000000000000000000000000000000000000000168214612b4d577f00000000000000000000000000000000000000000000000000000000000000001614612b28576040516399cdbc8d60e01b81526001600160a01b03919091166004820152602490fd5b507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000000090565b5050507f000000000000000000000000000000000000000000000000000000000000006490565b5050507f00000000000000000000000000000000000000000000000000005af3107a400090565b908160209103126101a2575161026b816102cf565b90916040519160208301936bffffffffffffffffffffffff19809260601b16855260601b16603483015264ffffffffff199060281b166048820152604381526080810181811067ffffffffffffffff8211176113235760405251902090565b818102918183041490151782151516156101a2570490565b5f8112612d425790565b602490604051907fa8ce44320000000000000000000000000000000000000000000000000000000082526004820152fd5b600260015414612d84576002600155565b606460405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b0390921660248301526044820192909252611df791612e2482606481015b03601f198101845283611344565b612f66565b9290604051927f23b872dd0000000000000000000000000000000000000000000000000000000060208501526001600160a01b03809216602485015216604483015260648201526064815260a081019181831067ffffffffffffffff84111761132357611df792604052612f66565b604460206001600160a01b03604051928380927fdd62ed3e000000000000000000000000000000000000000000000000000000008252306004830152808816602483015286165afa9081156104d257611df794612e2492612f00925f91612f48575b5061151f565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000060208201526001600160a01b03909416602485015260448401528260648101612e16565b612f60915060203d8111610f3f57610f308183611344565b5f612efa565b6001600160a01b031690612fc5604051612f7f81611328565b6020938482527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564858301525f808587829751910182855af1612fbf611386565b91613072565b80519182159184831561304a575b505050905015612fe05750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b91938180945001031261306e578201519081151582036105695750805f8084612fd3565b5080fd5b919290156130d35750815115613086575090565b3b1561308f5790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156130e65750805190602001fd5b6106af9060405191829162461bcd60e51b83526004830161025a56fea264697066735822122080da084b805f10a554670a90153b67b3171297abef3b34b35c3d106b305b336c64736f6c63430008150033

Verified Source Code Full Match

Compiler: v0.8.21+commit.d9974bed EVM: shanghai Optimization: Yes (1000 runs)
Types.sol 95 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import "@openzeppelin/IERC20.sol";
import "./interfaces/IAssetRegistry.sol";

// Types.sol
//
// This file defines the types used in V2.

/// @notice Combination of contract address and sighash to be used in allowlist.
/// @dev It's packed as follows:
///      [target 160 bits] [selector 32 bits] [<empty> 64 bits]
type TargetSighash is bytes32;

/// @notice Struct encapulating an asset and an associated value.
/// @param asset Asset address.
/// @param value The associated value for this asset (e.g., amount or price).
struct AssetValue {
    IERC20 asset;
    uint256 value;
}

/// @notice Execution details for a vault operation.
/// @param target Target contract address.
/// @param value Native token amount.
/// @param data Calldata.
struct Operation {
    address target;
    uint256 value;
    bytes data;
}

/// @notice Contract address and sighash struct to be used in the public interface.
struct TargetSighashData {
    address target;
    bytes4 selector;
}

/// @notice Parameters for vault deployment.
/// @param owner Initial owner address.
/// @param assetRegistry Asset registry address.
/// @param hooks Hooks address.
/// @param guardian Guardian address.
/// @param feeRecipient Fee recipient address.
/// @param fee Fees accrued per second, denoted in 18 decimal fixed point format.
struct Parameters {
    address owner;
    address assetRegistry;
    address hooks;
    address guardian;
    address feeRecipient;
    uint256 fee;
}

/// @notice Vault parameters for vault deployment.
/// @param owner Initial owner address.
/// @param guardian Guardian address.
/// @param feeRecipient Fee recipient address.
/// @param fee Fees accrued per second, denoted in 18 decimal fixed point format.
struct VaultParameters {
    address owner;
    address guardian;
    address feeRecipient;
    uint256 fee;
}

/// @notice Asset registry parameters for asset registry deployment.
/// @param factory Asset registry factory address.
/// @param owner Initial owner address.
/// @param assets Initial list of registered assets.
/// @param numeraireToken Numeraire token address.
/// @param feeToken Fee token address.
/// @param sequencer Sequencer Uptime Feed address for L2.
struct AssetRegistryParameters {
    address factory;
    address owner;
    IAssetRegistry.AssetInformation[] assets;
    IERC20 numeraireToken;
    IERC20 feeToken;
    AggregatorV2V3Interface sequencer;
}

/// @notice Hooks parameters for hooks deployment.
/// @param factory Hooks factory address.
/// @param owner Initial owner address.
/// @param minDailyValue The fraction of value that the vault has to retain per day
///                      in the course of submissions.
/// @param targetSighashAllowlist Array of target contract and sighash combinations to allow.
struct HooksParameters {
    address factory;
    address owner;
    uint256 minDailyValue;
    TargetSighashData[] targetSighashAllowlist;
}
Math.sol 31 lines
// SPDX-License-Identifier: MIT
// slither-disable-start dead-code
pragma solidity 0.8.21;

library Math {
    /// @dev Multiply two quantities, then divide by a third.
    ///      Copied from solmate FixedPointMathLib.
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        // slither-disable-next-line assembly
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(
                and(
                    iszero(iszero(denominator)),
                    or(iszero(x), eq(div(z, x), y))
                )
            ) { revert(0, 0) }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }
}
// slither-disable-end dead-code
Executor.sol 70 lines
// SPDX-License-Identifier: BUSL-1.1
// slither-disable-start unimplemented-functions
pragma solidity 0.8.21;

import "@openzeppelin/ReentrancyGuard.sol";
import "./interfaces/IExecutor.sol";
import "src/v2/Types.sol";

abstract contract Executor is IExecutor, ReentrancyGuard {
    /// FUNCTIONS ///

    /// @inheritdoc IExecutor
    function execute(Operation[] calldata operations) external nonReentrant {
        // Requirements: check operations to be executed.
        _checkOperations(operations);

        uint256 numOperations = operations.length;

        // Requirements: check that the number of operations is not zero.
        if (numOperations == 0) return;

        for (uint256 i = 0; i < numOperations;) {
            // Effects: execute operation.
            _executeOperation(operations[i]);
            unchecked {
                i++;
            } // gas savings
        }
    }

    /// INTERNAL FUNCTIONS ///

    /// @dev Execute a single operation.
    function _executeOperation(Operation calldata operation)
        internal
        virtual
    {
        // Requirements: check the operation.
        _checkOperation(operation);

        // Interactions: execute operation.
        // slither-disable-next-line calls-loop,arbitrary-send-eth
        (bool success, bytes memory result) =
            operation.target.call{value: operation.value}(operation.data);

        // Invariants: check that the operation was successful.
        // Note: if operation.target is EOA, success will always be true.
        // It is caller responsibility to check that the operation.target is a contract.
        if (!success) {
            revert AeraPeriphery__ExecutionFailed(result);
        }

        // Log that the operation was executed.
        emit Executed(msg.sender, operation);
    }

    /// @dev Authorize the execution of operations. Intended to be marked by
    ///      `onlyOwner` or similar access control modifier.
    function _checkOperations(Operation[] calldata operations)
        internal
        view
        virtual;

    /// @dev Authorize the execution of a single operation.
    function _checkOperation(Operation calldata operation)
        internal
        view
        virtual;
}
// slither-disable-end unimplemented-functions
AbstractAssetOracle.sol 148 lines
// SPDX-License-Identifier: BUSL-1.1
// slither-disable-start dead-code,unimplemented-functions
pragma solidity 0.8.21;

import {SafeCast} from "./dependencies/openzeppelin/SafeCast.sol";
import {IAeraV2Oracle} from "./interfaces/IAeraV2Oracle.sol";
import {Math} from "./Math.sol";

abstract contract AbstractAssetOracle is IAeraV2Oracle {
    using SafeCast for *;

    /// @dev The total supply of the token.
    uint256 private constant _TOTAL_SUPPLY = 1e18;
    /// @dev Vault address.
    address internal immutable _vault;

    /// ERRORS ///

    /// @notice Thrown when the vault address is zero.
    error AeraPeriphery__VaultIsZeroAddress();
    /// @notice Thrown when a user tries to transfer tokens.
    error AeraPeriphery__TransfersAreNotAllowed();
    /// @notice Thrown when a user tries to approve.
    error AeraPeriphery__ApprovalsAreNotAllowed();
    /// @notice Thrown when the price <= 0.
    error AeraPeriphery__InvalidPrice(address priceFeed, int256 price);

    /// STORAGE ///

    /// @notice Flag to check if the token is burned.
    bool public burned;

    /// FUNCTIONS ///

    /// @notice Constructor for the AbstractAssetOracle contract.
    /// @param vault_ The address of the AeraVaultV2 contract.
    constructor(address vault_) {
        // Requirements: check the vault address is not zero.
        if (vault_ == address(0)) {
            revert AeraPeriphery__VaultIsZeroAddress();
        }
        // Effects: set the vault address.
        _vault = vault_;
    }

    /// @dev When called by the vault, this function burns
    ///      the token and makes this token unusable.
    function transfer(address, uint256) external returns (bool) {
        if (msg.sender == _vault && !burned) {
            burned = true;
            return true;
        }
        revert AeraPeriphery__TransfersAreNotAllowed();
    }

    function transferFrom(
        address,
        address,
        uint256
    ) external virtual returns (bool) {
        revert AeraPeriphery__TransfersAreNotAllowed();
    }

    function approve(address, uint256) external virtual returns (bool) {
        revert AeraPeriphery__ApprovalsAreNotAllowed();
    }

    /// @dev Returns the amount of tokens owned by `account`.
    function balanceOf(address account) external view returns (uint256) {
        // For everyone else, the balance is zero.
        if (account != _vault) return 0;

        // For the vault, the balance is the total supply, if not burned.
        return burned ? 0 : _TOTAL_SUPPLY;
    }

    /// @dev Returns the amount of tokens in existence.
    function totalSupply() external view returns (uint256) {
        return burned ? 0 : _TOTAL_SUPPLY;
    }

    /// @inheritdoc IAeraV2Oracle
    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        roundId = 0;
        // Note: since the amount of the token is fixed then 1 * answer = position value.
        answer = _getValue().toInt256();
        if (answer == 0) {
            answer = 1; // Avoid zero price, which would break AeraVaultAssetRegistry
        }
        startedAt = 0;
        updatedAt = block.timestamp;
        answeredInRound = 0;
    }

    /// @notice Returns the AeraVaultV2 address.
    function vault() external view virtual returns (address) {
        return _vault;
    }

    /// @notice Because this contract is oracle and ERC20 token at the same time,
    ///         this function represent decimals for oracle price and token decimals.
    /// @return decimals The number of decimals.
    function decimals() public pure returns (uint8) {
        return 18;
    }

    /// @notice Name of ERC20 token.
    function name() external pure virtual returns (string memory);

    /// @notice Symbol of ERC20 token.
    function symbol() external pure virtual returns (string memory);

    /// INTERNAL FUNCTIONS ///

    /// @dev Gets and validates the price from the price feed.
    function _getPrice(address priceFeed) internal view returns (uint256) {
        (, int256 price,,,) = IAeraV2Oracle(priceFeed).latestRoundData();

        if (price <= 0) {
            revert AeraPeriphery__InvalidPrice(priceFeed, price);
        }
        return price.toUint256();
    }

    /// @dev Returns the value of the position.
    function _getValue() internal view virtual returns (uint256);

    /// @dev Multiply two quantities, then divide by a third.
    ///      Copied from solmale FixedPointMathLib.
    function _mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        return Math.mulDiv(x, y, denominator);
    }
}
// slither-disable-end dead-code,unimplemented-functions
LlamaPayRouterOracle.sol 807 lines
// SPDX-License-Identifier: BUSL-1.1
// slither-disable-start similar-names
pragma solidity 0.8.21;

import {IERC20} from "@openzeppelin/IERC20.sol";
import {Ownable} from "@openzeppelin/Ownable.sol";
import {SafeERC20} from "@openzeppelin/SafeERC20.sol";
import {AggregatorV2V3Interface} from
    "@chainlink/interfaces/AggregatorV2V3Interface.sol";
import {Operation} from "src/v2/Types.sol";
import {ILlamaPay} from "./dependencies/llamapay/ILlamaPay.sol";
import {ILlamaPayRouterOracle} from "./interfaces/ILlamaPayRouterOracle.sol";
import {Executor} from "./Executor.sol";
import {AbstractAssetOracle} from "./AbstractAssetOracle.sol";

/// @title LlamaPayRouterOracle.
/// @notice An router/oracle that calculates the value of LlamaPay streams.
///         The router/oracle can create, cancel, pause, modify, deposit and withdraw LlamaPay streams.
/// @notice Note 1: The contract does not check for LlamaPay contract duplicates in constructor.
/// @notice Note 2: All "amountPerSec" arguments should use 20 decimals.
/// @notice Note 3: In the withdrawPayer() function, "amount" argument should use 20 decimals.
/// @notice ### Audit Note: L-01 Vault Value Can Go Below Daily Lower Bound
/// @notice Since a withdrawal action by the beneficiary of a LlamaPay instance does not affect the daily
///         multiplier of a vault, the value locked within a vault over any 24-hour period can reach a lower
///         value than would be expected, given the `minDailyValue`.
///         Assume the following scenario:
///         * The vault holds `X` amount of value and has a `minDailyValue` multiplier of `0.9`.
///         * There is `Y = 0.03 * X` amount of value sitting in a LlamaPay contract managed by
///         the `LlamaPayRouterOracle` contract.
///         * The beneficiary of the LlamaPay stream withdraws `Y` value. Now there is `X - Y =
///         0.97 * X` value in the vault, but the `cumulativeDailyMultiplier` stays at `1`.
///         * The guardian submits a batch of actions which reduces the amount of value in the vault
///         by `0.09 * X`. This would put the vault at `0.88 * X` value which is below the
///         threshold of `0.9 * X`. Since the `cumulativeDailyMultiplier` did not account for
///         the withdrawal of funds from the LlamaPay stream, the calculation of the
///         `newMultiplier` in the `afterSubmit` hook results in a value greater than `0.9` and
///         therefore allows it. The exact calculation of `newMultiplier`
///         in this case is the following: `(1 * (0.88 * X)) / (0.97 * X)`,
///         which results in `~0.907 > 0.9`. Therefore, the daily multiplier check passes.
contract LlamaPayRouterOracle is
    ILlamaPayRouterOracle,
    AbstractAssetOracle,
    Executor
{
    using SafeERC20 for IERC20;

    /// CONSTANTS ///

    /// @notice The maximum number of LlamaPay contracts.
    uint256 private constant _MAX_LLAMAPAY_COUNT = 10;
    /// @notice Downscale factor from LlamaPay balance to 18 decimals.
    uint256 private constant _LLAMAPAY_DOWNSCALE_FACTOR = 1e2;
    /// @notice The number of decimals of the LlamaPay payer balance.
    uint256 private constant _LLAMAPAY_DECIMALS = 20;
    /// @notice The name of the token.
    string private constant _NAME = "LlamaPayRouterOracle";
    /// @notice The symbol of the token.
    string private constant _SYMBOL = "LPRO";

    /// IMMUTABLES ///

    /// @notice Number of LlamaPay contracts.
    uint256 public immutable llamaPayCount;

    /// @notice Decomposed list of LlamaPay contracts (one for each token).
    ILlamaPay private immutable _llamaPay0;
    ILlamaPay private immutable _llamaPay1;
    ILlamaPay private immutable _llamaPay2;
    ILlamaPay private immutable _llamaPay3;
    ILlamaPay private immutable _llamaPay4;
    ILlamaPay private immutable _llamaPay5;
    ILlamaPay private immutable _llamaPay6;
    ILlamaPay private immutable _llamaPay7;
    ILlamaPay private immutable _llamaPay8;
    ILlamaPay private immutable _llamaPay9;

    /// @notice Decomposed decimals divisor list.
    uint256 private immutable _decimalsDivisor0;
    uint256 private immutable _decimalsDivisor1;
    uint256 private immutable _decimalsDivisor2;
    uint256 private immutable _decimalsDivisor3;
    uint256 private immutable _decimalsDivisor4;
    uint256 private immutable _decimalsDivisor5;
    uint256 private immutable _decimalsDivisor6;
    uint256 private immutable _decimalsDivisor7;
    uint256 private immutable _decimalsDivisor8;
    uint256 private immutable _decimalsDivisor9;

    /// @notice Decomposed price feed list.
    address private immutable _priceFeed0;
    address private immutable _priceFeed1;
    address private immutable _priceFeed2;
    address private immutable _priceFeed3;
    address private immutable _priceFeed4;
    address private immutable _priceFeed5;
    address private immutable _priceFeed6;
    address private immutable _priceFeed7;
    address private immutable _priceFeed8;
    address private immutable _priceFeed9;

    /// @notice Decomposed LlamaPay token list.
    IERC20 private immutable _token0;
    IERC20 private immutable _token1;
    IERC20 private immutable _token2;
    IERC20 private immutable _token3;
    IERC20 private immutable _token4;
    IERC20 private immutable _token5;
    IERC20 private immutable _token6;
    IERC20 private immutable _token7;
    IERC20 private immutable _token8;
    IERC20 private immutable _token9;

    /// @notice Decomposed rescale factor list.
    uint256 private immutable _rescaleFactor0;
    uint256 private immutable _rescaleFactor1;
    uint256 private immutable _rescaleFactor2;
    uint256 private immutable _rescaleFactor3;
    uint256 private immutable _rescaleFactor4;
    uint256 private immutable _rescaleFactor5;
    uint256 private immutable _rescaleFactor6;
    uint256 private immutable _rescaleFactor7;
    uint256 private immutable _rescaleFactor8;
    uint256 private immutable _rescaleFactor9;

    /// @notice Decomposed inverted price numerator list.
    uint256 private immutable _invertedPriceNumerator0;
    uint256 private immutable _invertedPriceNumerator1;
    uint256 private immutable _invertedPriceNumerator2;
    uint256 private immutable _invertedPriceNumerator3;
    uint256 private immutable _invertedPriceNumerator4;
    uint256 private immutable _invertedPriceNumerator5;
    uint256 private immutable _invertedPriceNumerator6;
    uint256 private immutable _invertedPriceNumerator7;
    uint256 private immutable _invertedPriceNumerator8;
    uint256 private immutable _invertedPriceNumerator9;

    /// MODIFIERS ///

    /// @notice Check that the caller is the Vault owner.
    modifier onlyVaultOwner() {
        // Requirements: check that the caller is the Vault owner.
        if (msg.sender != Ownable(_vault).owner()) {
            revert AeraPeriphery__CallerIsNotVaultOwner(msg.sender);
        }
        _;
    }

    /// @notice Check that the caller is the Vault.
    modifier onlyVault() {
        // Requirements: check that the caller is the Vault.
        if (msg.sender != _vault) {
            revert AeraPeriphery__CallerIsNotVault(msg.sender);
        }
        _;
    }

    /// STORAGE ///

    /// @notice Mapping of stream end dates.
    mapping(bytes32 streamId => uint256 endDate) internal _streamEndDate;

    /// FUNCTIONS ///

    /// @notice The constructor for the LlamaPayRouterOracle. The contract does not check for LlamaPay contract duplicates.
    /// @notice It is deployer responsibility to ensure that there are no LlamaPay contract duplicates in provided llamaPayInfoList.
    constructor(
        address vault_,
        LlamaPayInfo[] memory llamaPayInfoList_
    ) AbstractAssetOracle(vault_) {
        // Effects: set llamaPay count.
        llamaPayCount = llamaPayInfoList_.length;

        // Requirements: check that the llamaPay count is not zero.
        if (llamaPayCount == 0) {
            revert AeraPeriphery__LlamaPayInfoCountIsZero();
        }

        // Requirements: check that the llamaPay count does not exceed the maximum.
        if (llamaPayCount > _MAX_LLAMAPAY_COUNT) {
            revert AeraPeriphery__LlamaPayInfoCountExceedsMax(
                _MAX_LLAMAPAY_COUNT
            );
        }

        // Effects: initialize immutable variables
        (
            _llamaPay0,
            _token0,
            _priceFeed0,
            _decimalsDivisor0,
            _invertedPriceNumerator0,
            _rescaleFactor0
        ) = _getLlamaPayInfo(llamaPayInfoList_[0]);
        if (llamaPayCount == 1) return;

        (
            _llamaPay1,
            _token1,
            _priceFeed1,
            _decimalsDivisor1,
            _invertedPriceNumerator1,
            _rescaleFactor1
        ) = _getLlamaPayInfo(llamaPayInfoList_[1]);
        if (llamaPayCount == 2) return;

        (
            _llamaPay2,
            _token2,
            _priceFeed2,
            _decimalsDivisor2,
            _invertedPriceNumerator2,
            _rescaleFactor2
        ) = _getLlamaPayInfo(llamaPayInfoList_[2]);
        if (llamaPayCount == 3) return;

        (
            _llamaPay3,
            _token3,
            _priceFeed3,
            _decimalsDivisor3,
            _invertedPriceNumerator3,
            _rescaleFactor3
        ) = _getLlamaPayInfo(llamaPayInfoList_[3]);
        if (llamaPayCount == 4) return;

        (
            _llamaPay4,
            _token4,
            _priceFeed4,
            _decimalsDivisor4,
            _invertedPriceNumerator4,
            _rescaleFactor4
        ) = _getLlamaPayInfo(llamaPayInfoList_[4]);
        if (llamaPayCount == 5) return;

        (
            _llamaPay5,
            _token5,
            _priceFeed5,
            _decimalsDivisor5,
            _invertedPriceNumerator5,
            _rescaleFactor5
        ) = _getLlamaPayInfo(llamaPayInfoList_[5]);
        if (llamaPayCount == 6) return;

        (
            _llamaPay6,
            _token6,
            _priceFeed6,
            _decimalsDivisor6,
            _invertedPriceNumerator6,
            _rescaleFactor6
        ) = _getLlamaPayInfo(llamaPayInfoList_[6]);
        if (llamaPayCount == 7) return;

        (
            _llamaPay7,
            _token7,
            _priceFeed7,
            _decimalsDivisor7,
            _invertedPriceNumerator7,
            _rescaleFactor7
        ) = _getLlamaPayInfo(llamaPayInfoList_[7]);
        if (llamaPayCount == 8) return;

        (
            _llamaPay8,
            _token8,
            _priceFeed8,
            _decimalsDivisor8,
            _invertedPriceNumerator8,
            _rescaleFactor8
        ) = _getLlamaPayInfo(llamaPayInfoList_[8]);
        if (llamaPayCount == 9) return;

        (
            _llamaPay9,
            _token9,
            _priceFeed9,
            _decimalsDivisor9,
            _invertedPriceNumerator9,
            _rescaleFactor9
        ) = _getLlamaPayInfo(llamaPayInfoList_[9]);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function createStream(
        IERC20 token,
        address to,
        uint216 amountPerSec,
        uint256 duration
    ) external onlyVault {
        // Requirements: check that the duration is not zero.
        if (duration == 0) {
            revert AeraPeriphery__DurationIsZero();
        }

        // Effects: remember the end date.
        _streamEndDate[_streamId(token, to, amountPerSec)] =
            block.timestamp + duration;

        // Log that a new expiring stream was created.
        // Note: event is emitted before the interaction with LlamaPay contract to avoid reentrancy issues
        // and make slither happy.
        // https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3
        emit ExpiringStreamCreated(token, to, amountPerSec, duration);

        // Interactions: create stream on LlamaPay contract.
        _getLlamaPay(token).createStream(to, amountPerSec);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function cancelStream(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) external onlyVault {
        ILlamaPay llamaPay = _getLlamaPay(token);

        bytes32 streamId = _streamId(token, to, amountPerSec);

        // Requirements: check that the stream exists.
        if (_streamEndDate[streamId] == 0) {
            revert AeraPeriphery__StreamDoesNotExist(token, to, amountPerSec);
        }

        // Effects & Interactions: delete endDate and cancel stream on LlamaPay contract.
        _cancelStream(llamaPay, streamId, to, amountPerSec);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function pauseStream(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) external onlyVault {
        ILlamaPay llamaPay = _getLlamaPay(token);
        bytes32 streamId = _streamId(token, to, amountPerSec);

        // Requirements: check that the stream exists.
        if (_streamEndDate[streamId] == 0) {
            revert AeraPeriphery__StreamDoesNotExist(token, to, amountPerSec);
        }

        // Interactions: pause stream on LlamaPay contract.
        llamaPay.pauseStream(to, amountPerSec);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function cancelExpiredStream(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) external {
        ILlamaPay llamaPay = _getLlamaPay(token);
        bytes32 streamId = _streamId(token, to, amountPerSec);
        // Cache the end date to avoid SLOAD.
        uint256 streamEndDate = _streamEndDate[streamId];

        // Requirements: check that the stream exists.
        if (streamEndDate == 0) {
            revert AeraPeriphery__StreamDoesNotExist(token, to, amountPerSec);
        }

        // Requirements: check that the stream is expired.
        if (streamEndDate > block.timestamp) {
            revert AeraPeriphery__StreamNotExpired(streamEndDate);
        }

        // Effects & Interactions: delete endDate and cancel stream on LlamaPay contract.
        _cancelStream(llamaPay, streamId, to, amountPerSec);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function modifyStream(
        IERC20 token,
        address oldTo,
        uint216 oldAmountPerSec,
        address to,
        uint216 amountPerSec,
        uint256 duration
    ) external onlyVault {
        ILlamaPay llamaPay = _getLlamaPay(token);

        // Requirements: check that the endDate is not in the past.
        if (duration == 0) {
            revert AeraPeriphery__DurationIsZero();
        }
        bytes32 oldStreamId = _streamId(token, oldTo, oldAmountPerSec);

        // Requirements: check that the old stream exists.
        if (_streamEndDate[oldStreamId] == 0) {
            revert AeraPeriphery__StreamDoesNotExist(
                token, oldTo, oldAmountPerSec
            );
        }

        // Effects: delete old end date.
        delete _streamEndDate[oldStreamId];

        // Effects: remember the end date.
        _streamEndDate[_streamId(token, to, amountPerSec)] =
            block.timestamp + duration;

        // Log that a new expiring stream was created.
        // Note: event is emitted before the interaction with LlamaPay contract to avoid reentrancy issues
        // and make slither happy.
        // https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-3
        emit ExpiringStreamCreated(token, to, amountPerSec, duration);

        // Interactions: modify the stream on LlamaPay contract.
        llamaPay.modifyStream(oldTo, oldAmountPerSec, to, amountPerSec);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function deposit(
        IERC20 token,
        uint256 amount
    ) external onlyVault nonReentrant {
        ILlamaPay llamaPay = _getLlamaPay(token);

        // Interactions: transfer the tokens from the Vault to this contract.
        // slither-disable-next-line arbitrary-send-erc20
        token.safeTransferFrom(_vault, address(this), amount);

        // Interactions: increase the allowance of the LlamaPay contract.
        token.safeIncreaseAllowance(address(llamaPay), amount);

        // Interactions: deposit the tokens to the LlamaPay contract.
        llamaPay.deposit(amount);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function depositAndCreate(
        IERC20 token,
        uint256 amountToDeposit,
        address to,
        uint216 amountPerSec,
        uint256 duration
    ) external onlyVault nonReentrant {
        ILlamaPay llamaPay = _getLlamaPay(token);

        // Requirements: check that the duration is not zero.
        if (duration == 0) {
            revert AeraPeriphery__DurationIsZero();
        }

        // Effects: remember the end date.
        _streamEndDate[_streamId(token, to, amountPerSec)] =
            block.timestamp + duration;

        // Interactions: transfer the tokens from the Vault to this contract.
        // slither-disable-next-line arbitrary-send-erc20
        token.safeTransferFrom(_vault, address(this), amountToDeposit);

        // Interactions: increase the allowance of the LlamaPay contract.
        token.safeIncreaseAllowance(address(llamaPay), amountToDeposit);

        // Log that a new expiring stream was created.
        // Note: event is emitted before the interaction with LlamaPay to be consistent with the
        // createStream and modifyStream functions.
        emit ExpiringStreamCreated(token, to, amountPerSec, duration);

        // Interactions: deposit the tokens to the LlamaPay contract and create a stream.
        llamaPay.depositAndCreate(amountToDeposit, to, amountPerSec);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function withdrawPayer(IERC20 token, uint256 amount) external onlyVault {
        // Interactions: withdraw tokens from the LlamaPay contract.
        _getLlamaPay(token).withdrawPayer(amount);

        uint256 decimalsDivisor = _getDecimalsDivisor(token);

        // Interactions: transfer the tokens from this contract to the Vault.
        token.safeTransfer(_vault, amount / decimalsDivisor);
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function withdrawPayerAll(IERC20 token) external onlyVault {
        // Interactions: withdraw all tokens from the LlamaPay contract.
        _getLlamaPay(token).withdrawPayerAll();

        // Interactions: transfer the tokens from this contract to the Vault.
        token.safeTransfer(_vault, token.balanceOf(address(this)));
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function endDate(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) external view returns (uint256) {
        return _streamEndDate[_streamId(token, to, amountPerSec)];
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function llamaPayInfoList()
        external
        view
        returns (LlamaPayInfo[] memory)
    {
        // Create a new array with the LlamaPayInfo.
        LlamaPayInfo[] memory result = new LlamaPayInfo[](llamaPayCount);
        // Fill the array with the LlamaPayInfo.
        result[0] = LlamaPayInfo({
            llamaPay: _llamaPay0,
            priceFeed: _priceFeed0,
            invertPrice: _invertedPriceNumerator0 > 0
        });
        if (llamaPayCount == 1) return result;

        result[1] = LlamaPayInfo({
            llamaPay: _llamaPay1,
            priceFeed: _priceFeed1,
            invertPrice: _invertedPriceNumerator1 > 0
        });
        if (llamaPayCount == 2) return result;

        result[2] = LlamaPayInfo({
            llamaPay: _llamaPay2,
            priceFeed: _priceFeed2,
            invertPrice: _invertedPriceNumerator2 > 0
        });
        if (llamaPayCount == 3) return result;

        result[3] = LlamaPayInfo({
            llamaPay: _llamaPay3,
            priceFeed: _priceFeed3,
            invertPrice: _invertedPriceNumerator3 > 0
        });
        if (llamaPayCount == 4) return result;

        result[4] = LlamaPayInfo({
            llamaPay: _llamaPay4,
            priceFeed: _priceFeed4,
            invertPrice: _invertedPriceNumerator4 > 0
        });
        if (llamaPayCount == 5) return result;

        result[5] = LlamaPayInfo({
            llamaPay: _llamaPay5,
            priceFeed: _priceFeed5,
            invertPrice: _invertedPriceNumerator5 > 0
        });
        if (llamaPayCount == 6) return result;

        result[6] = LlamaPayInfo({
            llamaPay: _llamaPay6,
            priceFeed: _priceFeed6,
            invertPrice: _invertedPriceNumerator6 > 0
        });
        if (llamaPayCount == 7) return result;

        result[7] = LlamaPayInfo({
            llamaPay: _llamaPay7,
            priceFeed: _priceFeed7,
            invertPrice: _invertedPriceNumerator7 > 0
        });
        if (llamaPayCount == 8) return result;

        result[8] = LlamaPayInfo({
            llamaPay: _llamaPay8,
            priceFeed: _priceFeed8,
            invertPrice: _invertedPriceNumerator8 > 0
        });
        if (llamaPayCount == 9) return result;

        result[9] = LlamaPayInfo({
            llamaPay: _llamaPay9,
            priceFeed: _priceFeed9,
            invertPrice: _invertedPriceNumerator9 > 0
        });
        return result;
    }

    /// @inheritdoc ILlamaPayRouterOracle
    function vault()
        external
        view
        override(ILlamaPayRouterOracle, AbstractAssetOracle)
        returns (address)
    {
        return _vault;
    }

    /// @inheritdoc AbstractAssetOracle
    function name() external pure override returns (string memory) {
        return _NAME;
    }

    /// @inheritdoc AbstractAssetOracle
    function symbol() external pure override returns (string memory) {
        return _SYMBOL;
    }

    /// INTERNAL FUNCTIONS ///

    /// @notice Delete stream end date and cancel the stream.
    function _cancelStream(
        ILlamaPay llamaPay,
        bytes32 streamId,
        address to,
        uint216 amountPerSec
    ) internal {
        // Effects: delete the end date.
        delete _streamEndDate[streamId];

        // Interactions: cancel the stream on LlamaPay contract.
        llamaPay.cancelStream(to, amountPerSec);
    }

    /// @inheritdoc AbstractAssetOracle
    function _getValue() internal view override returns (uint256 balance) {
        balance = _llamaPayBalance(
            _llamaPay0, _priceFeed0, _rescaleFactor0, _invertedPriceNumerator0
        );
        if (llamaPayCount == 1) return balance;

        balance += _llamaPayBalance(
            _llamaPay1, _priceFeed1, _rescaleFactor1, _invertedPriceNumerator1
        );
        if (llamaPayCount == 2) return balance;

        balance += _llamaPayBalance(
            _llamaPay2, _priceFeed2, _rescaleFactor2, _invertedPriceNumerator2
        );
        if (llamaPayCount == 3) return balance;

        balance += _llamaPayBalance(
            _llamaPay3, _priceFeed3, _rescaleFactor3, _invertedPriceNumerator3
        );
        if (llamaPayCount == 4) return balance;

        balance += _llamaPayBalance(
            _llamaPay4, _priceFeed4, _rescaleFactor4, _invertedPriceNumerator4
        );
        if (llamaPayCount == 5) return balance;

        balance += _llamaPayBalance(
            _llamaPay5, _priceFeed5, _rescaleFactor5, _invertedPriceNumerator5
        );
        if (llamaPayCount == 6) return balance;

        balance += _llamaPayBalance(
            _llamaPay6, _priceFeed6, _rescaleFactor6, _invertedPriceNumerator6
        );
        if (llamaPayCount == 7) return balance;

        balance += _llamaPayBalance(
            _llamaPay7, _priceFeed7, _rescaleFactor7, _invertedPriceNumerator7
        );
        if (llamaPayCount == 8) return balance;

        balance += _llamaPayBalance(
            _llamaPay8, _priceFeed8, _rescaleFactor8, _invertedPriceNumerator8
        );
        if (llamaPayCount == 9) return balance;

        balance += _llamaPayBalance(
            _llamaPay9, _priceFeed9, _rescaleFactor9, _invertedPriceNumerator9
        );
    }

    /// @notice Get the LlamaPay balance in numerator terms scaled to 18 decimals.
    function _llamaPayBalance(
        ILlamaPay llamaPay,
        address priceFeed,
        uint256 rescaleFactor,
        uint256 invertedPriceNumerator
    ) internal view returns (uint256) {
        // Get the balance of this contract from LlamaPay contract.
        // Note: balance is always 20 decimals.
        uint256 balance = llamaPay.balances(address(this));
        // If the balance is zero, return zero.
        if (balance == 0) return 0;
        // If the price feed is absent, return balance downscaled to 18 decimals.
        if (priceFeed == address(0)) {
            return balance / _LLAMAPAY_DOWNSCALE_FACTOR;
        }

        uint256 price = _getPrice(priceFeed);
        // Invert the price if necessary.
        if (invertedPriceNumerator > 0) {
            price = invertedPriceNumerator / price;
        }

        // Multiply balance by the price and rescale to 18 decimals.
        return _mulDiv(balance, price, rescaleFactor);
    }

    /// @notice Return decomposed LlamaPayInfo, rescale factor, invertedPriceNumerator, token and decimalsDivisor.
    /// @return llamaPay The LlamaPay contract.
    /// @return token LlamaPay stream token.
    /// @return priceFeed The price feed address.
    /// @return decimalsDivisor The decimals divisor. DECIMALS_DIVISOR from LlamaPay contract. Signifies the scale to scale the balance to 20 decimals.
    /// @return invertedPriceNumerator The invert price numerator.
    /// @return rescaleFactor The rescale factor.
    function _getLlamaPayInfo(LlamaPayInfo memory llamaPayInfo)
        internal
        view
        returns (
            ILlamaPay llamaPay,
            IERC20 token,
            address priceFeed,
            uint256 decimalsDivisor,
            uint256 invertedPriceNumerator,
            uint256 rescaleFactor
        )
    {
        llamaPay = llamaPayInfo.llamaPay;
        decimalsDivisor = ILlamaPay(llamaPay).DECIMALS_DIVISOR();
        priceFeed = llamaPayInfo.priceFeed;
        token = llamaPay.token();

        // Get price feed decimals
        uint8 priceFeedDecimals = (
            priceFeed == address(0)
                ? 0
                : AggregatorV2V3Interface(priceFeed).decimals()
        );

        uint256 totalDecimals = _LLAMAPAY_DECIMALS + priceFeedDecimals;
        // Since this oracle is designed to work with 18 decimals, we need to rescale the value.
        // _LLAMAPAY_DECIMALS is 20, totalDecimals will always be >= 20.
        rescaleFactor = 10 ** (totalDecimals - 18);

        // Inverted price numerator to be used in value calculation.
        invertedPriceNumerator = (
            priceFeed != address(0) && llamaPayInfo.invertPrice
                ? 100 ** priceFeedDecimals // 10 ** (priceFeedDecimals * 2)
                : 0
        );
    }

    /// @notice Returns the LlamaPay contract for a given token.
    function _getLlamaPay(IERC20 token) internal view returns (ILlamaPay) {
        if (address(token) == address(0)) {
            revert AeraPeriphery__InvalidToken(token);
        }

        // Generally we would use a mapping here, but SHA3 operation costs 30+ units of gas and
        // EQ operation costs 3 units of gas. Roughly, one SHA3 = 10 comparisons.
        // We have maximum 10 tokens, so it's cheaper (or the same in worst case) to compare them manually.
        if (token == _token0) return _llamaPay0;
        if (token == _token1) return _llamaPay1;
        if (token == _token2) return _llamaPay2;
        if (token == _token3) return _llamaPay3;
        if (token == _token4) return _llamaPay4;
        if (token == _token5) return _llamaPay5;
        if (token == _token6) return _llamaPay6;
        if (token == _token7) return _llamaPay7;
        if (token == _token8) return _llamaPay8;
        if (token == _token9) return _llamaPay9;

        revert AeraPeriphery__InvalidToken(token);
    }

    /// @notice Returns cached decimals divisor for a given token.
    function _getDecimalsDivisor(IERC20 token)
        internal
        view
        returns (uint256)
    {
        if (address(token) == address(0)) {
            revert AeraPeriphery__InvalidToken(token);
        }
        // Note: see _getLlamaPay() for explanation.
        if (token == _token0) return _decimalsDivisor0;
        if (token == _token1) return _decimalsDivisor1;
        if (token == _token2) return _decimalsDivisor2;
        if (token == _token3) return _decimalsDivisor3;
        if (token == _token4) return _decimalsDivisor4;
        if (token == _token5) return _decimalsDivisor5;
        if (token == _token6) return _decimalsDivisor6;
        if (token == _token7) return _decimalsDivisor7;
        if (token == _token8) return _decimalsDivisor8;
        if (token == _token9) return _decimalsDivisor9;

        revert AeraPeriphery__InvalidToken(token);
    }

    /// @inheritdoc Executor
    function _checkOperations(Operation[] calldata operations)
        internal
        view
        override
        onlyVaultOwner
    {}

    /// @inheritdoc Executor
    function _checkOperation(Operation calldata operation)
        internal
        view
        override
    {}

    /// @notice Returns the stream ID.
    function _streamId(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(token, to, amountPerSec));
    }
}
// slither-disable-end similar-names
IExecutor.sol 22 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import {Operation} from "src/v2/Types.sol";

interface IExecutor {
    /// EVENTS ///

    /// @notice Emitted when operations are executed.
    event Executed(address indexed caller, Operation operation);

    /// ERRORS ///

    /// @notice Error emitted when the execution of an operation fails.
    error AeraPeriphery__ExecutionFailed(bytes result);

    /// FUNCTIONS ///

    /// @notice Execute arbitrary actions.
    /// @param operations The operations to execute.
    function execute(Operation[] calldata operations) external;
}
IAssetRegistry.sol 62 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import "@chainlink/interfaces/AggregatorV2V3Interface.sol";
import "@openzeppelin/IERC20.sol";

/// @title IAssetRegistry
/// @notice Asset registry interface.
/// @dev Any implementation MUST also implement Ownable2Step and ERC165.
interface IAssetRegistry {
    /// @param asset Asset address.
    /// @param heartbeat Frequency of oracle price updates.
    /// @param isERC4626 True if yield-bearing asset, false if just an ERC20 asset.
    /// @param oracle If applicable, oracle address for asset.
    struct AssetInformation {
        IERC20 asset;
        uint256 heartbeat;
        bool isERC4626;
        AggregatorV2V3Interface oracle;
    }

    /// @param asset Asset address.
    /// @param spotPrice Spot price of an asset in Numeraire token terms.
    struct AssetPriceReading {
        IERC20 asset;
        uint256 spotPrice;
    }

    /// @notice Get address of vault.
    /// @return vault Address of vault.
    function vault() external view returns (address vault);

    /// @notice Get a list of all registered assets.
    /// @return assets List of assets.
    /// @dev MUST return assets in an order sorted by address.
    function assets()
        external
        view
        returns (AssetInformation[] memory assets);

    /// @notice Get address of fee token.
    /// @return feeToken Address of fee token.
    /// @dev Represented as an address for efficiency reasons.
    /// @dev MUST be present in assets array.
    function feeToken() external view returns (IERC20 feeToken);

    /// @notice Get the index of the Numeraire token in the assets array.
    /// @return numeraireToken Numeraire token address.
    /// @dev Represented as an index for efficiency reasons.
    /// @dev MUST be a number between 0 (inclusive) and the length of assets array (exclusive).
    function numeraireToken() external view returns (IERC20 numeraireToken);

    /// @notice Calculate spot prices of non-ERC4626 assets.
    /// @return spotPrices Spot prices of non-ERC4626 assets in 18 decimals.
    /// @dev MUST return assets in the same order as in assets but with ERC4626 assets filtered out.
    /// @dev MUST also include Numeraire token (spot price = 1).
    /// @dev MAY revert if oracle prices for any asset are unreliable at the time.
    function spotPrices()
        external
        view
        returns (AssetPriceReading[] memory spotPrices);
}
IAeraV2Oracle.sol 26 lines
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

/// @title IAeraV2Oracle
/// @notice Used to calculate price of ERC20 tokens using the same interface as Chainlink.
interface IAeraV2Oracle {
    /// @notice The decimals returned from the answer in latestRoundData.
    function decimals() external view returns (uint8);

    /// @notice Returns the latest price.
    /// @return roundId Optional, doesn't apply to non-Chainlink oracles.
    /// @return answer The price.
    /// @return startedAt Optional, doesn't apply to non-Chainlink oracles.
    /// @return updatedAt The most recent timestamp the price was updated
    /// @return answeredInRound Optional, doesn't apply to non-Chainlink oracles.
    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );
}
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);
}
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;
    }
}
Ownable.sol 83 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "./Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
ILlamaPay.sol 56 lines
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import "@openzeppelin/IERC20.sol";

interface ILlamaPay {
    struct Payer {
        uint40 lastPayerUpdate;
        uint216 totalPaidPerSec;
    }

    // solhint-disable-next-line func-name-mixedcase
    function DECIMALS_DIVISOR() external view returns (uint256);
    function token() external view returns (IERC20);
    function balances(address) external view returns (uint256);

    function createStream(address to, uint216 amountPerSec) external;

    function withdrawable(
        address from,
        address to,
        uint216 amountPerSec
    )
        external
        view
        returns (uint256 withdrawableAmount, uint256 lastUpdate, uint256 owed);

    function withdraw(
        address from,
        address to,
        uint216 amountPerSec
    ) external;

    function cancelStream(address to, uint216 amountPerSec) external;

    function pauseStream(address to, uint216 amountPerSec) external;

    function modifyStream(
        address oldTo,
        uint216 oldAmountPerSec,
        address to,
        uint216 amountPerSec
    ) external;

    function deposit(uint256 amountToDeposit) external;

    function depositAndCreate(
        uint256 amountToDeposit,
        address to,
        uint216 amountPerSec
    ) external;

    function withdrawPayer(uint256 amount) external;

    function withdrawPayerAll() external;
}
ILlamaPayRouterOracle.sol 137 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import {IERC20} from "@openzeppelin/IERC20.sol";
import {Operation} from "src/v2/Types.sol";
import {IExecutor} from "./IExecutor.sol";
import {ILlamaPayRouterOracleTypes} from "./ILlamaPayRouterOracleTypes.sol";

/// @title ILlamaPayRouterOracle.
interface ILlamaPayRouterOracle is IExecutor, ILlamaPayRouterOracleTypes {
    /// @notice Creates a LlamaPay stream and remembers the end date.
    /// @dev MUST revert if not called by vault.
    /// @dev MUST revert if endDate ≤ block.timestamp.
    /// @dev MUST revert if token is not part of a supported LlamaPay contract.
    /// @param token The token of the stream.
    /// @param to The address of the stream.
    /// @param amountPerSec The amount per second of the stream. Must use 20 decimals.
    /// @param duration Duration of the stream in seconds.
    function createStream(
        IERC20 token,
        address to,
        uint216 amountPerSec,
        uint256 duration
    ) external;

    /// @notice Cancel a given stream. Just calls the same function on LlamaPay.
    /// @dev MUST revert if not called by vault.
    /// @dev MUST revert if stream doesn't exist.
    /// @param token The token of the stream.
    /// @param to The address of the stream.
    /// @param amountPerSec The amount per second of the stream. Must use 20 decimals.
    function cancelStream(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) external;

    /// @notice Pause a given stream. Just calls the same function on LlamaPay.
    /// @dev MUST revert if not called by vault.
    /// @dev MUST revert if stream doesn't exist.
    /// @param token The token of the stream.
    /// @param to The address of the stream.
    /// @param amountPerSec The amount per second of the stream. Must use 20 decimals.
    function pauseStream(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) external;

    /// @notice Cancel a given stream if block.timestamp ≥ endDate for that stream.
    /// @dev MUST revert if endDate > block.timestamp.
    /// @dev MUST revert if stream doesn't exist.
    /// @param token The token of the stream.
    /// @param to The address of the stream.
    /// @param amountPerSec The amount per second of the stream. Must use 20 decimals.
    function cancelExpiredStream(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) external;

    /// @notice Modifies an existing LlamaPay stream.
    /// @dev MUST revert if not called by vault.
    /// @dev MUST revert if endDate ≤ block.timestamp.
    /// @dev MUST revert if stream doesn't exist.
    /// @param token The token of the stream.
    /// @param oldTo The address of the stream.
    /// @param oldAmountPerSec The amount per second of the stream. Must use 20 decimals.
    /// @param to The new address of the stream.
    /// @param amountPerSec The new amount per second of the stream. Must use 20 decimals.
    /// @param duration Duration of the stream in seconds.
    function modifyStream(
        IERC20 token,
        address oldTo,
        uint216 oldAmountPerSec,
        address to,
        uint216 amountPerSec,
        uint256 duration
    ) external;

    /// @notice Forwards a deposit on behalf of vault.
    /// @dev MUST revert if not called by vault.
    /// @dev MUST revert if stream doesn't exist.
    /// @param token The token to deposit.
    /// @param amount The amount to deposit.
    function deposit(IERC20 token, uint256 amount) external;

    /// @notice Creates a LlamaPay stream with a deposit and remembers the end date.
    /// @dev MUST revert if not called by vault.
    /// @dev MUST revert if endDate ≤ block.timestamp.
    /// @dev MUST revert if stream doesn't exist.
    /// @param token The token to deposit.
    /// @param amountToDeposit The amount to deposit.
    /// @param to The address to send the stream to.
    /// @param amountPerSec The amount per second to send. Must use 20 decimals.
    /// @param duration Duration of the stream in seconds.
    function depositAndCreate(
        IERC20 token,
        uint256 amountToDeposit,
        address to,
        uint216 amountPerSec,
        uint256 duration
    ) external;

    /// @notice Recover money back to vault.
    /// @dev MUST revert if not called by vault.
    /// @param token The token to withdraw.
    /// @param amount The amount to withdraw. Must use 20 decimals.
    function withdrawPayer(IERC20 token, uint256 amount) external;

    /// @notice Recover all money back to vault.
    /// @dev MUST revert if not called by vault.
    /// @param token The token to withdraw.
    function withdrawPayerAll(IERC20 token) external;

    /// @notice Returns the end date of a given stream.
    /// @param token The token of the stream.
    /// @param to The address of the stream.
    /// @param amountPerSec The amount per second of the stream. Must use 20 decimals.
    /// @return endDate The end date of the stream.
    function endDate(
        IERC20 token,
        address to,
        uint216 amountPerSec
    ) external view returns (uint256);

    /// @notice Returns Vault address.
    /// @return vault The Vault address.
    function vault() external view returns (address);

    /// @notice Returns the LlamaPay Info list.
    /// @return llamaPayInfoList The LlamaPay Info list.
    function llamaPayInfoList()
        external
        view
        returns (LlamaPayInfo[] memory);
}
SafeERC20.sol 143 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./IERC20Permit.sol";
import "./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. Compatible with tokens that require the approval to be set to
     * 0 before setting it to a non-zero value.
     */
    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));
    }
}
SafeCast.sol 1229 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.21;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value)
        internal
        pure
        returns (int248 downcasted)
    {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value)
        internal
        pure
        returns (int240 downcasted)
    {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value)
        internal
        pure
        returns (int232 downcasted)
    {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value)
        internal
        pure
        returns (int224 downcasted)
    {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value)
        internal
        pure
        returns (int216 downcasted)
    {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value)
        internal
        pure
        returns (int208 downcasted)
    {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value)
        internal
        pure
        returns (int200 downcasted)
    {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value)
        internal
        pure
        returns (int192 downcasted)
    {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value)
        internal
        pure
        returns (int184 downcasted)
    {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value)
        internal
        pure
        returns (int176 downcasted)
    {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value)
        internal
        pure
        returns (int168 downcasted)
    {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value)
        internal
        pure
        returns (int160 downcasted)
    {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value)
        internal
        pure
        returns (int152 downcasted)
    {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value)
        internal
        pure
        returns (int144 downcasted)
    {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value)
        internal
        pure
        returns (int136 downcasted)
    {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value)
        internal
        pure
        returns (int128 downcasted)
    {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value)
        internal
        pure
        returns (int120 downcasted)
    {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value)
        internal
        pure
        returns (int112 downcasted)
    {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value)
        internal
        pure
        returns (int104 downcasted)
    {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }
}
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);
}
ILlamaPayRouterOracleTypes.sol 48 lines
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import {IERC20} from "@openzeppelin/IERC20.sol";
import {ILlamaPay} from "periphery/dependencies/llamapay/ILlamaPay.sol";

/// @title ILlamaPayRouterOracleTypes.
/// @notice The types used by the ILlamaPayRouterOracle.
interface ILlamaPayRouterOracleTypes {
    /// STRUCTS ///

    struct LlamaPayInfo {
        ILlamaPay llamaPay;
        address priceFeed;
        bool invertPrice;
    }

    /// ERRORS ///

    /// @dev Thrown when the llamaPayCount is zero.
    error AeraPeriphery__LlamaPayInfoCountIsZero();
    /// @dev Thrown when the llamaPayCount exceeds the maximum.
    error AeraPeriphery__LlamaPayInfoCountExceedsMax(uint256 maxLlamaPayCount);
    /// @dev Thrown when the caller is not the Vault owner.
    error AeraPeriphery__CallerIsNotVaultOwner(address caller);
    /// @dev Thrown when the caller is not the Vault.
    error AeraPeriphery__CallerIsNotVault(address caller);
    /// @dev Thrown when the token is invalid.
    error AeraPeriphery__InvalidToken(IERC20 token);
    /// @dev Thrown when the duration is zero when creating a new stream.
    error AeraPeriphery__DurationIsZero();
    /// @dev Thrown when the endDate is in the future when trying to cancel stream or stream does not exist.
    error AeraPeriphery__StreamNotExpired(uint256 endDate);
    /// @dev Thrown when stream does not exist.
    error AeraPeriphery__StreamDoesNotExist(
        IERC20 token, address to, uint216 amountPerSec
    );

    /// EVENTS ///

    /// @dev Emitted when a new LlamaPay stream is created.
    event ExpiringStreamCreated(
        IERC20 indexed token,
        address indexed to,
        uint216 indexed amountPerSec,
        uint256 duration
    );
}
ReentrancyGuard.sol 77 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @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() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

    /**
     * @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;
    }
}
AggregatorInterface.sol 18 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorInterface {
  function latestAnswer() external view returns (int256);

  function latestTimestamp() external view returns (uint256);

  function latestRound() external view returns (uint256);

  function getAnswer(uint256 roundId) external view returns (int256);

  function getTimestamp(uint256 roundId) external view returns (uint256);

  event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 updatedAt);

  event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
}
AggregatorV3Interface.sol 35 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
  function decimals() external view returns (uint8);

  function description() external view returns (string memory);

  function version() external view returns (uint256);

  // getRoundData and latestRoundData should both raise "No data present"
  // if they do not have data to report, instead of returning unset values
  // which could be misinterpreted as actual reported values.
  function getRoundData(uint80 _roundId)
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );

  function latestRoundData()
    external
    view
    returns (
      uint80 roundId,
      int256 answer,
      uint256 startedAt,
      uint256 updatedAt,
      uint80 answeredInRound
    );
}
AggregatorV2V3Interface.sol 7 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./AggregatorInterface.sol";
import "./AggregatorV3Interface.sol";

interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface {}

Read Contract

balanceOf 0x70a08231 → uint256
burned 0x73f42561 → bool
decimals 0x313ce567 → uint8
endDate 0xbc9cbe3c → uint256
latestRoundData 0xfeaf968c → uint80, int256, uint256, uint256, uint80
llamaPayCount 0x56018c46 → uint256
llamaPayInfoList 0x006f2ca3 → tuple[]
name 0x06fdde03 → string
symbol 0x95d89b41 → string
totalSupply 0x18160ddd → uint256
vault 0xfbfa77cf → address

Write Contract 13 functions

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

approve 0x095ea7b3
address
uint256
returns: bool
cancelExpiredStream 0x37bdba35
address token
address to
uint216 amountPerSec
cancelStream 0xabcd2821
address token
address to
uint216 amountPerSec
createStream 0x122f79c0
address token
address to
uint216 amountPerSec
uint256 duration
deposit 0x47e7ef24
address token
uint256 amount
depositAndCreate 0x89e90a7a
address token
uint256 amountToDeposit
address to
uint216 amountPerSec
uint256 duration
execute 0xfab5b395
tuple[] operations
modifyStream 0x830f953a
address token
address oldTo
uint216 oldAmountPerSec
address to
uint216 amountPerSec
uint256 duration
pauseStream 0xf63ddc7d
address token
address to
uint216 amountPerSec
transfer 0xa9059cbb
address
uint256
returns: bool
transferFrom 0x23b872dd
address
address
uint256
returns: bool
withdrawPayer 0xb8404caf
address token
uint256 amount
withdrawPayerAll 0xc3f1fad8
address token

Recent Transactions

No transactions found for this address