Transaction Hash:
Block:
22215098 at Apr-07-2025 06:00:11 AM +UTC
Transaction Fee:
0.000598067969491748 ETH
$1.38
Gas Used:
324,716 Gas / 1.841818603 Gwei
Emitted Events:
| 578 |
ERC1967Proxy.0xd06a6b7f4918494b3719217d1802786c1f5112a6c1d88fe2cfec00b4584f6aec( 0xd06a6b7f4918494b3719217d1802786c1f5112a6c1d88fe2cfec00b4584f6aec, 0xf9c96a45784d0ce4390825a43a313149da787e6a6c66076f3a3f83e92501baeb, 0000000000000000000000000000000000000000000000000000000067f369df, 00000000000000000000000000000000000000000000000000000000059b86f9, 00000000000000000000000000000000000000000000000000000000000369e9 )
|
| 579 |
0xcb26c67ece9d7bbdb0205ee4c33f8503a0346134.0x8f8f4d49bbb03ffac818a5d588ec1786a4d2d17269871cbf5b1745f58b64c15d( 0x8f8f4d49bbb03ffac818a5d588ec1786a4d2d17269871cbf5b1745f58b64c15d, 0x00000000000000000000000020d12300b4805708d4293d29cb83c1e01d6f8913, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000002710, 0000000000000000000000000000000000000000000000000000000000000640, 00000000000000000000000000000000000000000000000000000000000005a4, bc36c0a900000000000000000000000000000000000000000000000000000000, 0000002000000000000000000000000000000000000000000000000000000000, 0000000100000000000000000000000000000000000000000000000000000000, 0000002000000000000000000000000000000000000000000000000000000000, 0000050b504e41550100000003b801000000040d00780afaba14204f0f5751b7, 4e7ec43eeac39851ce11fd9bdafc3cfebee94a5df40382100a326c132c694a44, e75afb933d3681a82b8fc3ca5330d5a7c309531dcd0102a6982c128c9af4b0a1, 5e94e1f000c0aa19253a9f69971b309d13385a4b075e2c4e0eddb61dfe21fdd2, 7c4164919e1b61b54257c0257b6863550599076dbe023601031c891e87ee8ac2, f2dd7d7d035c3b10d9dce137d8a0373e43a68274d26189a39c2ba3ea14ca9d6f, 4e601db1f1d09734c4bd97a5d4dd3f14ea58e4845ab8a66d5100047970933091, e0abcdd69a9b259125d46bb1597d1ec55b9e9d9709f0541ea6b5064313343c3e, b4f33d4843b71ebad32eca73150945548d38e26068600862e8d0d70106c3cb2c, 3111b618d6a24ce1c269b77864511284a53177249282b04a11404b72d9168f35, 8bdbf042d02ffbb646574e1176c9881531b4291b450363092035fe2e39000882, 8bf5b912ee7083450a7aa9980402207a1c87fca5fd9792b2d55f060c4492c201, 8811efae0a4e4452b57af652ed2a6237e4a4a8802150359151ebb45bdf832d00, 0a1b5d6105ad894a585a4bfba745c32b3400ee6fbf718090b554d0ebd7e0b80f, 3039619c96d4bb1ee5aeda0af06842a58b43f4a5456e62f3ea77a4c19f2f1538, c6010ba02465dfc657a6d52137a2d14f707786cee03d2a1c1aa0de1bff772625, 20ea5a7629b6a5848c1036b22823edcb6aaf56e5110caa6bcd3afec9e70002b9, 1e062f000de24e893b40e7a6114e0a353060bf2a5409b324bf981d724071f9be, e4cb10b4691c3570419ef1d43b1fb96cce5e3b10f8aaf9e814228ae469beb9e8, cd0875f903010e8777dd603acc398447e4d1d354ae4f873df1ef132ea7740e89, 66f3ac93281dc40ae2350308dbfb7844792778ea2d8c8f61fcc4775c3f3a08f0, 8e8151d5a2e9e6000f0b988eaaf946987c9506c92fd2c84f0b18ae288de52268, 18e5110d2ef5cd47a96e891d629a05bf8650a898181421b555b74803a84e31d2, 06f4093ae5919e0a1a0010c86940c999d121dce9eb0b51e16566117c02b9235a, 45dde2d96181373371a5ae68394ca567775c1947997b5808ba716780100b8a63, 821fc541eeec7cb4cb76d00111003fd3c3599a8ed5127bb438392b328f2f1200, d7276bedc6245d2b482918e9960dafc85b097dcd66e46cdb0470349ccad6c01c, 76978e77720f8460d0c005cf2d0167f369df00000000001ae101faedac5851e3, 2b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71000000000767a366, 014155575600000000000c74ec3a000027102a3f705e7618035f3210f1e82fc3, bf957d14f25501005500f9c96a45784d0ce4390825a43a313149da787e6a6c66, 076f3a3f83e92501baeb00000000059b86f900000000000369e9fffffff80000, 000067f369df0000000067f369df00000000059b44d60000000000032e620c64, befef33fc88e9699514884748cf1e78e4496fa0ae60d2298a27d7cda37d4533b, cb293fe4bece2ee7a7a17996dd321afaa980230c1c0745c823e6846d5eebb217, db849a04a46378d74afbe5620b61d9b65a613d98a274e8b30c63702aa8acb1d1, 403d55fe996e25039ecec5c59a72b5dce4e42dbe46435dc89e06cfd11cbb7b64, cb3932c0f9cd0d3d3fb9333abd13f27c746f9d835b5980093d92e5293355d037, 495fc91c4f4296b033a13a1304c1f99f6780f30d48052502d53a231a49959800, 9dccf3a810d5d9a018f1bcbb77b49875f797b7450a3fa11c49b0a97a01d47fdf, 0417b9845358c3c3ec2367600936fe0000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
| 580 |
EIP173Proxy.0x6bc93adab97dd835bee818087939b726558bc8d9177650a0018dd05eb00e56c8( 0x6bc93adab97dd835bee818087939b726558bc8d9177650a0018dd05eb00e56c8, 372ef793ce7f4c5830a8d9aa136e0c1b3c11c3db0ab96e3910de8dea1fb7e397, 37717b1710ec8bb427c7a0a1bf880e3c6336f6bf92a3bd3345a5e22ce2ad1471, 0000000000000000000000000000000000000000000000000000000000000001 )
|
| 581 |
Gelato.0xc6cfec28363edf86fe132edcedb30ccc6dbb89a7ec5f428aaeeb8ae5d901537d( 0xc6cfec28363edf86fe132edcedb30ccc6dbb89a7ec5f428aaeeb8ae5d901537d, 37717b1710ec8bb427c7a0a1bf880e3c6336f6bf92a3bd3345a5e22ce2ad1471, 0000000000000000000000002a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0, 000000000000000000000000df8f2aeea963803140df7b4ddd11216e584577b4, 000000000000000000000000417b4adc279743fc49f047c323fc668db9e600d8 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x20D12300...01d6f8913 | 0.010000000002189757 Eth | 0.010000000002199756 Eth | 0.000000000000009999 | ||
| 0x3CACa7b4...9180E83b6 | |||||
| 0x417B4Adc...db9E600D8 | (Bundler: 0x417...0d8) |
6.290462320694787695 Eth
Nonce: 31993
|
6.289864252725295947 Eth
Nonce: 31994
| 0.000598067969491748 | |
| 0x4305FB66...90A4c69C6 | (Angle Protocol: Pyth Oracle) | 0.000000908717056804 Eth | 0.000000908717056805 Eth | 0.000000000000000001 | |
|
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 13.496037976586367252 Eth | 13.496435063608691384 Eth | 0.000397087022324132 | |
| 0xCB26C67e...3a0346134 | 0.00999999999781 Eth | 0.0099999999978 Eth | 0.00000000000001 |
Execution Trace
Gelato.09c56431( )
0x1c410b318a26c0d6223e8f79ce8c5fd099c529d4.09c56431( )
-
Null: 0x000...001.07c9e498( ) EIP173Proxy.80381407( )Automate.execBypassModule( _taskCreator=0x0F6e98A756A40dD050dC78959f45559F98d3289d, _execAddress=0xCB26C67eCE9d7Bbdb0205ee4C33f8503a0346134, _taskId=372EF793CE7F4C5830A8D9AA136E0C1B3C11C3DB0AB96E3910DE8DEA1FB7E397, _correlationId=37717B1710EC8BB427C7A0A1BF880E3C6336F6BF92A3BD3345A5E22CE2AD1471, _execData=0x54132D7800000000000000000000000020D12300B4805708D4293D29CB83C1E01D6F89130000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000005A4BC36C0A9000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000050B504E41550100000003B801000000040D00780AFABA14204F0F5751B74E7EC43EEAC39851CE11FD9BDAFC3CFEBEE94A5DF40382100A326C132C694A44E75AFB933D3681A82B8FC3CA5330D5A7C309531DCD0102A6982C128C9AF4B0A15E94E1F000C0AA19253A9F69971B309D13385A4B075E2C4E0EDDB61DFE21FDD27C4164919E1B61B54257C0257B6863550599076DBE023601031C891E87EE8AC2F2DD7D7D035C3B10D9DCE137D8A0373E43A68274D26189A39C2BA3EA14CA9D6F4E601DB1F1D09734C4BD97A5D4DD3F14EA58E4845AB8A66D5100047970933091E0ABCDD69A9B259125D46BB1597D1EC55B9E9D9709F0541EA6B5064313343C3EB4F33D4843B71EBAD32ECA73150945548D38E26068600862E8D0D70106C3CB2C3111B618D6A24CE1C269B77864511284A53177249282B04A11404B72D9168F358BDBF042D02FFBB646574E1176C9881531B4291B450363092035FE2E390008828BF5B912EE7083450A7AA9980402207A1C87FCA5FD9792B2D55F060C4492C2018811EFAE0A4E4452B57AF652ED2A6237E4A4A8802150359151EBB45BDF832D000A1B5D6105AD894A585A4BFBA745C32B3400EE6FBF718090B554D0EBD7E0B80F3039619C96D4BB1EE5AEDA0AF06842A58B43F4A5456E62F3EA77A4C19F2F1538C6010BA02465DFC657A6D52137A2D14F707786CEE03D2A1C1AA0DE1BFF77262520EA5A7629B6A5848C1036B22823EDCB6AAF56E5110CAA6BCD3AFEC9E70002B91E062F000DE24E893B40E7A6114E0A353060BF2A5409B324BF981D724071F9BEE4CB10B4691C3570419EF1D43B1FB96CCE5E3B10F8AAF9E814228AE469BEB9E8CD0875F903010E8777DD603ACC398447E4D1D354AE4F873DF1EF132EA7740E8966F3AC93281DC40AE2350308DBFB7844792778EA2D8C8F61FCC4775C3F3A08F08E8151D5A2E9E6000F0B988EAAF946987C9506C92FD2C84F0B18AE288DE5226818E5110D2EF5CD47A96E891D629A05BF8650A898181421B555B74803A84E31D206F4093AE5919E0A1A0010C86940C999D121DCE9EB0B51E16566117C02B9235A45DDE2D96181373371A5AE68394CA567775C1947997B5808BA716780100B8A63821FC541EEEC7CB4CB76D00111003FD3C3599A8ED5127BB438392B328F2F1200D7276BEDC6245D2B482918E9960DAFC85B097DCD66E46CDB0470349CCAD6C01C76978E77720F8460D0C005CF2D0167F369DF00000000001AE101FAEDAC5851E32B9B23B5F9411A8C2BAC4AAE3ED4DD7B811DD1A72EA4AA71000000000767A366014155575600000000000C74EC3A000027102A3F705E7618035F3210F1E82FC3BF957D14F25501005500F9C96A45784D0CE4390825A43A313149DA787E6A6C66076F3A3F83E92501BAEB00000000059B86F900000000000369E9FFFFFFF80000000067F369DF0000000067F369DF00000000059B44D60000000000032E620C64BEFEF33FC88E9699514884748CF1E78E4496FA0AE60D2298A27D7CDA37D4533BCB293FE4BECE2EE7A7A17996DD321AFAA980230C1C0745C823E6846D5EEBB217DB849A04A46378D74AFBE5620B61D9B65A613D98A274E8B30C63702AA8ACB1D1403D55FE996E25039ECEC5C59A72B5DCE4E42DBE46435DC89E06CFD11CBB7B64CB3932C0F9CD0D3D3FB9333ABD13F27C746F9D835B5980093D92E5293355D037495FC91C4F4296B033A13A1304C1F99F6780F30D48052502D53A231A499598009DCCF3A810D5D9A018F1BCBB77B49875F797B7450A3FA11C49B0A97A01D47FDF0417B9845358C3C3EC2367600936FE00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, _revertOnFailure=True, _singleExec=False )0xcb26c67ece9d7bbdb0205ee4c33f8503a0346134.54132d78( )OpsProxy.executeCall( _target=0x20D12300B4805708d4293d29cb83c1e01d6f8913, _data=0xBC36C0A9000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000050B504E41550100000003B801000000040D00780AFABA14204F0F5751B74E7EC43EEAC39851CE11FD9BDAFC3CFEBEE94A5DF40382100A326C132C694A44E75AFB933D3681A82B8FC3CA5330D5A7C309531DCD0102A6982C128C9AF4B0A15E94E1F000C0AA19253A9F69971B309D13385A4B075E2C4E0EDDB61DFE21FDD27C4164919E1B61B54257C0257B6863550599076DBE023601031C891E87EE8AC2F2DD7D7D035C3B10D9DCE137D8A0373E43A68274D26189A39C2BA3EA14CA9D6F4E601DB1F1D09734C4BD97A5D4DD3F14EA58E4845AB8A66D5100047970933091E0ABCDD69A9B259125D46BB1597D1EC55B9E9D9709F0541EA6B5064313343C3EB4F33D4843B71EBAD32ECA73150945548D38E26068600862E8D0D70106C3CB2C3111B618D6A24CE1C269B77864511284A53177249282B04A11404B72D9168F358BDBF042D02FFBB646574E1176C9881531B4291B450363092035FE2E390008828BF5B912EE7083450A7AA9980402207A1C87FCA5FD9792B2D55F060C4492C2018811EFAE0A4E4452B57AF652ED2A6237E4A4A8802150359151EBB45BDF832D000A1B5D6105AD894A585A4BFBA745C32B3400EE6FBF718090B554D0EBD7E0B80F3039619C96D4BB1EE5AEDA0AF06842A58B43F4A5456E62F3EA77A4C19F2F1538C6010BA02465DFC657A6D52137A2D14F707786CEE03D2A1C1AA0DE1BFF77262520EA5A7629B6A5848C1036B22823EDCB6AAF56E5110CAA6BCD3AFEC9E70002B91E062F000DE24E893B40E7A6114E0A353060BF2A5409B324BF981D724071F9BEE4CB10B4691C3570419EF1D43B1FB96CCE5E3B10F8AAF9E814228AE469BEB9E8CD0875F903010E8777DD603ACC398447E4D1D354AE4F873DF1EF132EA7740E8966F3AC93281DC40AE2350308DBFB7844792778EA2D8C8F61FCC4775C3F3A08F08E8151D5A2E9E6000F0B988EAAF946987C9506C92FD2C84F0B18AE288DE5226818E5110D2EF5CD47A96E891D629A05BF8650A898181421B555B74803A84E31D206F4093AE5919E0A1A0010C86940C999D121DCE9EB0B51E16566117C02B9235A45DDE2D96181373371A5AE68394CA567775C1947997B5808BA716780100B8A63821FC541EEEC7CB4CB76D00111003FD3C3599A8ED5127BB438392B328F2F1200D7276BEDC6245D2B482918E9960DAFC85B097DCD66E46CDB0470349CCAD6C01C76978E77720F8460D0C005CF2D0167F369DF00000000001AE101FAEDAC5851E32B9B23B5F9411A8C2BAC4AAE3ED4DD7B811DD1A72EA4AA71000000000767A366014155575600000000000C74EC3A000027102A3F705E7618035F3210F1E82FC3BF957D14F25501005500F9C96A45784D0CE4390825A43A313149DA787E6A6C66076F3A3F83E92501BAEB00000000059B86F900000000000369E9FFFFFFF80000000067F369DF0000000067F369DF00000000059B44D60000000000032E620C64BEFEF33FC88E9699514884748CF1E78E4496FA0AE60D2298A27D7CDA37D4533BCB293FE4BECE2EE7A7A17996DD321AFAA980230C1C0745C823E6846D5EEBB217DB849A04A46378D74AFBE5620B61D9B65A613D98A274E8B30C63702AA8ACB1D1403D55FE996E25039ECEC5C59A72B5DCE4E42DBE46435DC89E06CFD11CBB7B64CB3932C0F9CD0D3D3FB9333ABD13F27C746F9D835B5980093D92E5293355D037495FC91C4F4296B033A13A1304C1F99F6780F30D48052502D53A231A499598009DCCF3A810D5D9A018F1BCBB77B49875F797B7450A3FA11C49B0A97A01D47FDF0417B9845358C3C3EC2367600936FE000000000000000000000000000000000000000000, _value=10000 )- ETH 0.00000000000001
PythUpdater.updateFeeds( priceUpdateData=[UE5BVQEAAAADuAEAAAAEDQB4Cvq6FCBPD1dRt05+xD7qw5hRzhH9m9r8PP6+6Upd9AOCEAoybBMsaUpE51r7kz02gagrj8PKUzDVp8MJUx3NAQKmmCwSjJr0sKFelOHwAMCqGSU6n2mXGzCdEzhaSwdeLE4O3bYd/iH90nxBZJGeG2G1QlfAJXtoY1UFmQdtvgI2AQMciR6H7orC8t19fQNcOxDZ3OE32KA3PkOmgnTSYYmjnCuj6hTKnW9OYB2x8dCXNMS9l6XU3T8U6ljkhFq4pm1RAAR5cJMwkeCrzdaamyWRJdRrsVl9HsVbnp2XCfBUHqa1BkMTNDw+tPM9SEO3HrrTLspzFQlFVI044mBoYAhi6NDXAQbDyywxEbYY1qJM4cJpt3hkURKEpTF3JJKCsEoRQEty2RaPNYvb8ELQL/u2RldOEXbJiBUxtCkbRQNjCSA1/i45AAiCi/W5Eu5wg0UKeqmYBAIgehyH/KX9l5Ky1V8GDESSwgGIEe+uCk5EUrV69lLtKmI35KSogCFQNZFR67Rb34MtAAobXWEFrYlKWFpL+6dFwys0AO5vv3GAkLVU0OvX4LgPMDlhnJbUux7lrtoK8GhCpYtD9KVFbmLz6nekwZ8vFTjGAQugJGXfxlem1SE3otFPcHeGzuA9KhwaoN4b/3cmJSDqWnYptqWEjBA2sigj7ctqr1blEQyqa806/snnAAK5HgYvAA3iTok7QOemEU4KNTBgvypUCbMkv5gdckBx+b7kyxC0aRw1cEGe8dQ7H7lszl47EPiq+egUIorkab656M0IdfkDAQ6Hd91gOsw5hEfk0dNUrk+HPfHvEy6ndA6JZvOskygdxAriNQMI2/t4RHkneOotjI9h/MR3XD86CPCOgVHVounmAA8LmI6q+UaYfJUGyS/SyE8LGK4ojeUiaBjlEQ0u9c1HqW6JHWKaBb+GUKiYGBQhtVW3SAOoTjHSBvQJOuWRngoaABDIaUDJmdEh3OnrC1HhZWYRfAK5I1pF3eLZYYE3M3Glrmg5TKVnd1wZR5l7WAi6cWeAEAuKY4IfxUHu7Hy0y3bQAREAP9PDWZqO1RJ7tDg5KzKPLxIA1ydr7cYkXStIKRjplg2vyFsJfc1m5GzbBHA0nMrWwBx2l453cg+EYNDABc8tAWfzad8AAAAAABrhAfrtrFhR4yubI7X5QRqMK6xKrj7U3XuBHdGnLqSqcQAAAAAHZ6NmAUFVV1YAAAAAAAx07DoAACcQKj9wXnYYA18yEPHoL8O/lX0U8lUBAFUA+clqRXhNDOQ5CCWkOjExSdp4fmpsZgdvOj+D6SUBuusAAAAABZuG+QAAAAAAA2np////+AAAAABn82nfAAAAAGfzad8AAAAABZtE1gAAAAAAAy5iDGS+/vM/yI6WmVFIhHSM8eeORJb6CuYNIpiifXzaN9RTO8spP+S+zi7np6F5lt0yGvqpgCMMHAdFyCPmhG1e67IX24SaBKRjeNdK++ViC2HZtlphPZiidOizDGNwKqissdFAPVX+mW4lA57OxcWacrXc5OQtvkZDXcieBs/RHLt7ZMs5MsD5zQ09P7kzOr0T8nx0b52DW1mACT2S5SkzVdA3SV/JHE9ClrAzoToTBMH5n2eA8w1IBSUC1TojGkmVmACdzPOoENXZoBjxvLt3tJh195e3RQo/oRxJsKl6AdR/3wQXuYRTWMPD7CNnYAk2/g==] )
- ETH 0.00000000000001
-
File 1 of 6: Gelato
File 2 of 6: ERC1967Proxy
File 3 of 6: EIP173Proxy
File 4 of 6: Automate
File 5 of 6: OpsProxy
File 6 of 6: PythUpdater
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
import {LibDiamond} from "./libraries/standard/LibDiamond.sol";
import {IDiamondLoupe} from "./interfaces/standard/IDiamondLoupe.sol";
import {IDiamondCut} from "./interfaces/standard/IDiamondCut.sol";
import {IERC173} from "./interfaces/standard/IERC173.sol";
import {IERC165} from "./interfaces/standard/IERC165.sol";
contract Gelato {
// more arguments are added to this struct
// this avoids stack too deep errors
struct DiamondArgs {
address owner;
}
constructor(
IDiamondCut.FacetCut[] memory _diamondCut,
DiamondArgs memory _args
) payable {
LibDiamond.diamondCut(_diamondCut, address(0), new bytes(0));
LibDiamond.setContractOwner(_args.owner);
LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
// adding ERC165 data
ds.supportedInterfaces[type(IERC165).interfaceId] = true;
ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true;
ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true;
ds.supportedInterfaces[type(IERC173).interfaceId] = true;
}
// Find facet for function that is called and execute the
// function if a facet is found and return any value.
// solhint-disable-next-line no-complex-fallback
fallback() external payable {
LibDiamond.DiamondStorage storage ds;
bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
assembly {
ds.slot := position
}
address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
require(facet != address(0), "Gelato: Function does not exist");
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
// solhint-disable-next-line no-empty-blocks, ordering
receive() external payable {}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
/******************************************************************************\\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/
interface IDiamondCut {
enum FacetCutAction {Add, Replace, Remove}
// Add=0, Replace=1, Remove=2
struct FacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}
event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
/// @notice Add/replace/remove any number of functions and optionally execute
/// a function with delegatecall
/// @param _diamondCut Contains the facet addresses and function selectors
/// @param _init The address of the contract or facet to execute _calldata
/// @param _calldata A function call, including function selector and arguments
/// _calldata is executed with delegatecall on _init
function diamondCut(
FacetCut[] calldata _diamondCut,
address _init,
bytes calldata _calldata
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
/******************************************************************************\\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/
// A loupe is a small magnifying glass used to look at diamonds.
// These functions look at diamonds
interface IDiamondLoupe {
/// These functions are expected to be called frequently
/// by tools.
struct Facet {
address facetAddress;
bytes4[] functionSelectors;
}
/// @notice Gets all facet addresses and their four byte function selectors.
/// @return facets_ Facet
function facets() external view returns (Facet[] memory facets_);
/// @notice Gets all the function selectors supported by a specific facet.
/// @param _facet The facet address.
/// @return facetFunctionSelectors_
function facetFunctionSelectors(address _facet)
external
view
returns (bytes4[] memory facetFunctionSelectors_);
/// @notice Get all the facet addresses used by a diamond.
/// @return facetAddresses_
function facetAddresses()
external
view
returns (address[] memory facetAddresses_);
/// @notice Gets the facet that supports the given selector.
/// @dev If facet is not found return address(0).
/// @param _functionSelector The function selector.
/// @return facetAddress_ The facet address.
function facetAddress(bytes4 _functionSelector)
external
view
returns (address facetAddress_);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
interface IERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceId The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
/// @title ERC-173 Contract Ownership Standard
/// Note: the ERC-165 identifier for this interface is 0x7f5828d0
/* is ERC165 */
interface IERC173 {
/// @dev This emits when ownership of a contract changes.
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/// @notice Set the address of the new owner of the contract
/// @dev Set _newOwner to address(0) to renounce any ownership.
/// @param _newOwner The address of the new owner of the contract
function transferOwnership(address _newOwner) external;
/// @notice Get the address of the owner
/// @return owner_ The address of the owner.
function owner() external view returns (address owner_);
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
// https://github.com/mudgen/diamond-3/blob/b009cd08b7822bad727bbcc47aa1b50d8b50f7f0/contracts/libraries/LibDiamond.sol#L1
/******************************************************************************\\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/
import "../../interfaces/standard/IDiamondCut.sol";
// Custom due to incorrect string casting (non UTF-8 formatted)
import {GelatoBytes} from "../../../../lib/GelatoBytes.sol";
library LibDiamond {
bytes32 constant DIAMOND_STORAGE_POSITION =
keccak256("diamond.standard.diamond.storage");
struct FacetAddressAndPosition {
address facetAddress;
uint16 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
}
struct FacetFunctionSelectors {
bytes4[] functionSelectors;
uint16 facetAddressPosition; // position of facetAddress in facetAddresses array
}
struct DiamondStorage {
// maps function selector to the facet address and
// the position of the selector in the facetFunctionSelectors.selectors array
mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
// maps facet addresses to function selectors
mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
// facet addresses
address[] facetAddresses;
// Used to query if a contract implements an interface.
// Used to implement ERC-165.
mapping(bytes4 => bool) supportedInterfaces;
// owner of the contract
address contractOwner;
}
function diamondStorage()
internal
pure
returns (DiamondStorage storage ds)
{
bytes32 position = DIAMOND_STORAGE_POSITION;
assembly {
ds.slot := position
}
}
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
function setContractOwner(address _newOwner) internal {
DiamondStorage storage ds = diamondStorage();
address previousOwner = ds.contractOwner;
ds.contractOwner = _newOwner;
emit OwnershipTransferred(previousOwner, _newOwner);
}
function contractOwner() internal view returns (address contractOwner_) {
contractOwner_ = diamondStorage().contractOwner;
}
function isContractOwner(address _guy) internal view returns (bool) {
return _guy == contractOwner();
}
function enforceIsContractOwner() internal view {
require(
msg.sender == diamondStorage().contractOwner,
"LibDiamond: Must be contract owner"
);
}
event DiamondCut(
IDiamondCut.FacetCut[] _diamondCut,
address _init,
bytes _calldata
);
// Internal function version of diamondCut
function diamondCut(
IDiamondCut.FacetCut[] memory _diamondCut,
address _init,
bytes memory _calldata
) internal {
for (
uint256 facetIndex;
facetIndex < _diamondCut.length;
facetIndex++
) {
IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
if (action == IDiamondCut.FacetCutAction.Add) {
addFunctions(
_diamondCut[facetIndex].facetAddress,
_diamondCut[facetIndex].functionSelectors
);
} else if (action == IDiamondCut.FacetCutAction.Replace) {
replaceFunctions(
_diamondCut[facetIndex].facetAddress,
_diamondCut[facetIndex].functionSelectors
);
} else if (action == IDiamondCut.FacetCutAction.Remove) {
removeFunctions(
_diamondCut[facetIndex].facetAddress,
_diamondCut[facetIndex].functionSelectors
);
} else {
revert("LibDiamondCut: Incorrect FacetCutAction");
}
}
emit DiamondCut(_diamondCut, _init, _calldata);
initializeDiamondCut(_init, _calldata);
}
function addFunctions(
address _facetAddress,
bytes4[] memory _functionSelectors
) internal {
require(
_functionSelectors.length > 0,
"LibDiamondCut: No selectors in facet to cut"
);
DiamondStorage storage ds = diamondStorage();
// uint16 selectorCount = uint16(diamondStorage().selectors.length);
require(
_facetAddress != address(0),
"LibDiamondCut: Add facet can't be address(0)"
);
uint16 selectorPosition =
uint16(
ds.facetFunctionSelectors[_facetAddress]
.functionSelectors
.length
);
// add new facet address if it does not exist
if (selectorPosition == 0) {
enforceHasContractCode(
_facetAddress,
"LibDiamondCut: New facet has no code"
);
ds.facetFunctionSelectors[_facetAddress]
.facetAddressPosition = uint16(ds.facetAddresses.length);
ds.facetAddresses.push(_facetAddress);
}
for (
uint256 selectorIndex;
selectorIndex < _functionSelectors.length;
selectorIndex++
) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress =
ds.selectorToFacetAndPosition[selector].facetAddress;
require(
oldFacetAddress == address(0),
"LibDiamondCut: Can't add function that already exists"
);
ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(
selector
);
ds.selectorToFacetAndPosition[selector]
.facetAddress = _facetAddress;
ds.selectorToFacetAndPosition[selector]
.functionSelectorPosition = selectorPosition;
selectorPosition++;
}
}
function replaceFunctions(
address _facetAddress,
bytes4[] memory _functionSelectors
) internal {
require(
_functionSelectors.length > 0,
"LibDiamondCut: No selectors in facet to cut"
);
DiamondStorage storage ds = diamondStorage();
require(
_facetAddress != address(0),
"LibDiamondCut: Add facet can't be address(0)"
);
uint16 selectorPosition =
uint16(
ds.facetFunctionSelectors[_facetAddress]
.functionSelectors
.length
);
// add new facet address if it does not exist
if (selectorPosition == 0) {
enforceHasContractCode(
_facetAddress,
"LibDiamondCut: New facet has no code"
);
ds.facetFunctionSelectors[_facetAddress]
.facetAddressPosition = uint16(ds.facetAddresses.length);
ds.facetAddresses.push(_facetAddress);
}
for (
uint256 selectorIndex;
selectorIndex < _functionSelectors.length;
selectorIndex++
) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress =
ds.selectorToFacetAndPosition[selector].facetAddress;
require(
oldFacetAddress != _facetAddress,
"LibDiamondCut: Can't replace function with same function"
);
removeFunction(oldFacetAddress, selector);
// add function
ds.selectorToFacetAndPosition[selector]
.functionSelectorPosition = selectorPosition;
ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(
selector
);
ds.selectorToFacetAndPosition[selector]
.facetAddress = _facetAddress;
selectorPosition++;
}
}
function removeFunctions(
address _facetAddress,
bytes4[] memory _functionSelectors
) internal {
require(
_functionSelectors.length > 0,
"LibDiamondCut: No selectors in facet to cut"
);
DiamondStorage storage ds = diamondStorage();
// if function does not exist then do nothing and return
require(
_facetAddress == address(0),
"LibDiamondCut: Remove facet address must be address(0)"
);
for (
uint256 selectorIndex;
selectorIndex < _functionSelectors.length;
selectorIndex++
) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress =
ds.selectorToFacetAndPosition[selector].facetAddress;
removeFunction(oldFacetAddress, selector);
}
}
function removeFunction(address _facetAddress, bytes4 _selector) internal {
DiamondStorage storage ds = diamondStorage();
require(
_facetAddress != address(0),
"LibDiamondCut: Can't remove function that doesn't exist"
);
// an immutable function is a function defined directly in a diamond
require(
_facetAddress != address(this),
"LibDiamondCut: Can't remove immutable function"
);
// replace selector with last selector, then delete last selector
uint256 selectorPosition =
ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
uint256 lastSelectorPosition =
ds.facetFunctionSelectors[_facetAddress].functionSelectors.length -
1;
// if not the same then replace _selector with lastSelector
if (selectorPosition != lastSelectorPosition) {
bytes4 lastSelector =
ds.facetFunctionSelectors[_facetAddress].functionSelectors[
lastSelectorPosition
];
ds.facetFunctionSelectors[_facetAddress].functionSelectors[
selectorPosition
] = lastSelector;
ds.selectorToFacetAndPosition[lastSelector]
.functionSelectorPosition = uint16(selectorPosition);
}
// delete the last selector
ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
delete ds.selectorToFacetAndPosition[_selector];
// if no more selectors for facet address then delete the facet address
if (lastSelectorPosition == 0) {
// replace facet address with last facet address and delete last facet address
uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
uint256 facetAddressPosition =
ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
if (facetAddressPosition != lastFacetAddressPosition) {
address lastFacetAddress =
ds.facetAddresses[lastFacetAddressPosition];
ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
ds.facetFunctionSelectors[lastFacetAddress]
.facetAddressPosition = uint16(facetAddressPosition);
}
ds.facetAddresses.pop();
delete ds.facetFunctionSelectors[_facetAddress]
.facetAddressPosition;
}
}
function initializeDiamondCut(address _init, bytes memory _calldata)
internal
{
if (_init == address(0)) {
require(
_calldata.length == 0,
"LibDiamondCut: _init is address(0) but_calldata is not empty"
);
} else {
require(
_calldata.length > 0,
"LibDiamondCut: _calldata is empty but _init is not address(0)"
);
if (_init != address(this)) {
enforceHasContractCode(
_init,
"LibDiamondCut: _init address has no code"
);
}
(bool success, bytes memory error) = _init.delegatecall(_calldata);
if (!success) {
if (error.length > 0) {
// bubble up the error
GelatoBytes.revertWithError(error, "LibDiamondCut:_init:");
} else {
revert("LibDiamondCut: _init function reverted");
}
}
}
}
function enforceHasContractCode(
address _contract,
string memory _errorMessage
) internal view {
uint256 contractSize;
assembly {
contractSize := extcodesize(_contract)
}
require(contractSize > 0, _errorMessage);
}
}
// "SPDX-License-Identifier: UNLICENSED"
pragma solidity 0.8.0;
library GelatoBytes {
function calldataSliceSelector(bytes calldata _bytes)
internal
pure
returns (bytes4 selector)
{
selector =
_bytes[0] |
(bytes4(_bytes[1]) >> 8) |
(bytes4(_bytes[2]) >> 16) |
(bytes4(_bytes[3]) >> 24);
}
function memorySliceSelector(bytes memory _bytes)
internal
pure
returns (bytes4 selector)
{
selector =
_bytes[0] |
(bytes4(_bytes[1]) >> 8) |
(bytes4(_bytes[2]) >> 16) |
(bytes4(_bytes[3]) >> 24);
}
function revertWithError(bytes memory _bytes, string memory _tracingInfo)
internal
pure
{
// 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
if (_bytes.length % 32 == 4) {
bytes4 selector;
assembly {
selector := mload(add(0x20, _bytes))
}
if (selector == 0x08c379a0) {
// Function selector for Error(string)
assembly {
_bytes := add(_bytes, 68)
}
revert(string(abi.encodePacked(_tracingInfo, string(_bytes))));
} else {
revert(
string(abi.encodePacked(_tracingInfo, "NoErrorSelector"))
);
}
} else {
revert(
string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"))
);
}
}
function returnError(bytes memory _bytes, string memory _tracingInfo)
internal
pure
returns (string memory)
{
// 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
if (_bytes.length % 32 == 4) {
bytes4 selector;
assembly {
selector := mload(add(0x20, _bytes))
}
if (selector == 0x08c379a0) {
// Function selector for Error(string)
assembly {
_bytes := add(_bytes, 68)
}
return string(abi.encodePacked(_tracingInfo, string(_bytes)));
} else {
return
string(abi.encodePacked(_tracingInfo, "NoErrorSelector"));
}
} else {
return
string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"));
}
}
}
File 2 of 6: ERC1967Proxy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internall call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
// solhint-disable-next-line no-inline-assembly
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
/**
* @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internall call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback () external payable virtual {
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive () external payable virtual {
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overriden should call `super._beforeFallback()`.
*/
function _beforeFallback() internal virtual {
}
}
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*
*/
abstract contract ERC1967Upgrade {
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
address oldImplementation = _getImplementation();
// Initial upgrade and setup call
_setImplementation(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
// Perform rollback test if not already in progress
StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
if (!rollbackTesting.value) {
// Trigger rollback using upgradeTo from the new implementation
rollbackTesting.value = true;
Address.functionDelegateCall(
newImplementation,
abi.encodeWithSignature(
"upgradeTo(address)",
oldImplementation
)
);
rollbackTesting.value = false;
// Check rollback was effective
require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
// Finally reset to the new implementation and log the upgrade
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Emitted when the beacon is upgraded.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(
Address.isContract(newBeacon),
"ERC1967: new beacon is not a contract"
);
require(
Address.isContract(IBeacon(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
}
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}
/**
* @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
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 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://diligence.consensys.net/posts/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.5.11/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");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// 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
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly {
r.slot := slot
}
}
}
/*
* @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) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @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 () {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = 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");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
* explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
*/
contract ProxyAdmin is Ownable {
/**
* @dev Returns the current implementation of `proxy`.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
// We need to manually run the static call since the getter cannot be flagged as view
// bytes4(keccak256("implementation()")) == 0x5c60da1b
(bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
require(success);
return abi.decode(returndata, (address));
}
/**
* @dev Returns the current admin of `proxy`.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
// We need to manually run the static call since the getter cannot be flagged as view
// bytes4(keccak256("admin()")) == 0xf851a440
(bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
require(success);
return abi.decode(returndata, (address));
}
/**
* @dev Changes the admin of `proxy` to `newAdmin`.
*
* Requirements:
*
* - This contract must be the current admin of `proxy`.
*/
function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
proxy.changeAdmin(newAdmin);
}
/**
* @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
proxy.upgradeTo(implementation);
}
/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
* {TransparentUpgradeableProxy-upgradeToAndCall}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
*/
function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
proxy.upgradeToAndCall{value: msg.value}(implementation, data);
}
}
/**
* @dev Base contract for building openzeppelin-upgrades compatible implementations for the {ERC1967Proxy}. It includes
* publicly available upgrade functions that are called by the plugin and by the secure upgrade mechanism to verify
* continuation of the upgradability.
*
* The {_authorizeUpgrade} function MUST be overridden to include access restriction to the upgrade mechanism.
*
* _Available since v4.1._
*/
abstract contract UUPSUpgradeable is ERC1967Upgrade {
function upgradeTo(address newImplementation) external virtual {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallSecure(newImplementation, bytes(""), false);
}
function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallSecure(newImplementation, data, true);
}
function _authorizeUpgrade(address newImplementation) internal virtual;
}
abstract contract Proxiable is UUPSUpgradeable {
function _authorizeUpgrade(address newImplementation) internal override {
_beforeUpgrade(newImplementation);
}
function _beforeUpgrade(address newImplementation) internal virtual;
}
contract ChildOfProxiable is Proxiable {
function _beforeUpgrade(address newImplementation) internal virtual override {}
}
/**
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
* implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/
contract ERC1967Proxy is Proxy, ERC1967Upgrade {
/**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
*
* If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
* function call, and allows initializating the storage of the proxy like a Solidity constructor.
*/
constructor(address _logic, bytes memory _data) payable {
assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
_upgradeToAndCall(_logic, _data, false);
}
/**
* @dev Returns the current implementation address.
*/
function _implementation() internal view virtual override returns (address impl) {
return ERC1967Upgrade._getImplementation();
}
}
/**
* @dev This contract implements a proxy that is upgradeable by an admin.
*
* To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
* clashing], which can potentially be used in an attack, this contract uses the
* https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
* things that go hand in hand:
*
* 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
* that call matches one of the admin functions exposed by the proxy itself.
* 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
* implementation. If the admin tries to call a function on the implementation it will fail with an error that says
* "admin cannot fallback to proxy target".
*
* These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
* the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
* to sudden errors when trying to call a function from the proxy implementation.
*
* Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
* you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
*/
contract TransparentUpgradeableProxy is ERC1967Proxy {
/**
* @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
* optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
*/
constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
_changeAdmin(admin_);
}
/**
* @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
*/
modifier ifAdmin() {
if (msg.sender == _getAdmin()) {
_;
} else {
_fallback();
}
}
/**
* @dev Returns the current admin.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function admin() external ifAdmin returns (address admin_) {
admin_ = _getAdmin();
}
/**
* @dev Returns the current implementation.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/
function implementation() external ifAdmin returns (address implementation_) {
implementation_ = _implementation();
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
*/
function changeAdmin(address newAdmin) external virtual ifAdmin {
_changeAdmin(newAdmin);
}
/**
* @dev Upgrade the implementation of the proxy.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
*/
function upgradeTo(address newImplementation) external ifAdmin {
_upgradeToAndCall(newImplementation, bytes(""), false);
}
/**
* @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
* by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
* proxied contract.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
*/
function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
_upgradeToAndCall(newImplementation, data, true);
}
/**
* @dev Returns the current admin.
*/
function _admin() internal view virtual returns (address) {
return _getAdmin();
}
/**
* @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
*/
function _beforeFallback() internal virtual override {
require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
super._beforeFallback();
}
}
// Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
}File 3 of 6: EIP173Proxy
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./Proxy.sol";
interface ERC165 {
function supportsInterface(bytes4 id) external view returns (bool);
}
///@notice Proxy implementing EIP173 for ownership management
contract EIP173Proxy is Proxy {
// ////////////////////////// EVENTS ///////////////////////////////////////////////////////////////////////
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
// /////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////
constructor(
address implementationAddress,
address ownerAddress,
bytes memory data
) payable {
_setImplementation(implementationAddress, data);
_setOwner(ownerAddress);
}
// ///////////////////// EXTERNAL ///////////////////////////////////////////////////////////////////////////
function owner() external view returns (address) {
return _owner();
}
function supportsInterface(bytes4 id) external view returns (bool) {
if (id == 0x01ffc9a7 || id == 0x7f5828d0) {
return true;
}
if (id == 0xFFFFFFFF) {
return false;
}
ERC165 implementation;
// solhint-disable-next-line security/no-inline-assembly
assembly {
implementation := sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
}
// Technically this is not standard compliant as ERC-165 require 30,000 gas which that call cannot ensure
// because it is itself inside `supportsInterface` that might only get 30,000 gas.
// In practise this is unlikely to be an issue.
try implementation.supportsInterface(id) returns (bool support) {
return support;
} catch {
return false;
}
}
function transferOwnership(address newOwner) external onlyOwner {
_setOwner(newOwner);
}
function upgradeTo(address newImplementation) external onlyOwner {
_setImplementation(newImplementation, "");
}
function upgradeToAndCall(address newImplementation, bytes calldata data) external payable onlyOwner {
_setImplementation(newImplementation, data);
}
// /////////////////////// MODIFIERS ////////////////////////////////////////////////////////////////////////
modifier onlyOwner() {
require(msg.sender == _owner(), "NOT_AUTHORIZED");
_;
}
// ///////////////////////// INTERNAL //////////////////////////////////////////////////////////////////////
function _owner() internal view returns (address adminAddress) {
// solhint-disable-next-line security/no-inline-assembly
assembly {
adminAddress := sload(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103)
}
}
function _setOwner(address newOwner) internal {
address previousOwner = _owner();
// solhint-disable-next-line security/no-inline-assembly
assembly {
sstore(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103, newOwner)
}
emit OwnershipTransferred(previousOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
// EIP-1967
abstract contract Proxy {
// /////////////////////// EVENTS ///////////////////////////////////////////////////////////////////////////
event ProxyImplementationUpdated(address indexed previousImplementation, address indexed newImplementation);
// ///////////////////// EXTERNAL ///////////////////////////////////////////////////////////////////////////
receive() external payable virtual {
revert("ETHER_REJECTED"); // explicit reject by default
}
fallback() external payable {
_fallback();
}
// ///////////////////////// INTERNAL //////////////////////////////////////////////////////////////////////
function _fallback() internal {
// solhint-disable-next-line security/no-inline-assembly
assembly {
let implementationAddress := sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
calldatacopy(0x0, 0x0, calldatasize())
let success := delegatecall(gas(), implementationAddress, 0x0, calldatasize(), 0, 0)
let retSz := returndatasize()
returndatacopy(0, 0, retSz)
switch success
case 0 {
revert(0, retSz)
}
default {
return(0, retSz)
}
}
}
function _setImplementation(address newImplementation, bytes memory data) internal {
address previousImplementation;
// solhint-disable-next-line security/no-inline-assembly
assembly {
previousImplementation := sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
}
// solhint-disable-next-line security/no-inline-assembly
assembly {
sstore(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, newImplementation)
}
emit ProxyImplementationUpdated(previousImplementation, newImplementation);
if (data.length > 0) {
(bool success, ) = newImplementation.delegatecall(data);
if (!success) {
assembly {
// This assembly ensure the revert contains the exact string data
let returnDataSize := returndatasize()
returndatacopy(0, 0, returnDataSize)
revert(0, returnDataSize)
}
}
}
}
}
File 4 of 6: Automate
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `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);
/**
* @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);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
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));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
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");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @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");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.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
* ====
*
* [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://diligence.consensys.net/posts/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.5.11/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 functionCall(target, data, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(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) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(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) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason 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 {
// 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
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.14;
import {
EnumerableSet
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Gelatofied} from "./vendor/gelato/Gelatofied.sol";
import {GelatoBytes} from "./vendor/gelato/GelatoBytes.sol";
import {Proxied} from "./vendor/proxy/EIP173/Proxied.sol";
import {AutomateStorage} from "./AutomateStorage.sol";
import {LibDataTypes} from "./libraries/LibDataTypes.sol";
import {LibEvents} from "./libraries/LibEvents.sol";
import {LibTaskId} from "./libraries/LibTaskId.sol";
import {LibTaskModule} from "./libraries/LibTaskModule.sol";
import {LibBypassModule} from "./libraries/LibBypassModule.sol";
import {IAutomate} from "./interfaces/IAutomate.sol";
/**
* @notice Automate enables everyone to have Gelato monitor and execute transactions.
* @notice ExecAddress refers to the contract that has the function which Gelato will call.
* @notice Modules allow users to customise conditions and specifications when creating a task.
*/
//solhint-disable function-max-lines
//solhint-disable no-empty-blocks
contract Automate is Gelatofied, Proxied, AutomateStorage, IAutomate {
using GelatoBytes for bytes;
using EnumerableSet for EnumerableSet.Bytes32Set;
// solhint-disable const-name-snakecase
string public constant version = "7";
constructor(address payable _gelato) Gelatofied(_gelato) {}
///@inheritdoc IAutomate
function createTask(
address _execAddress,
bytes calldata _execDataOrSelector,
LibDataTypes.ModuleData calldata _moduleData,
address _feeToken
) external override returns (bytes32 taskId) {
address taskCreator;
(taskCreator, _execAddress) = LibTaskModule.preCreateTask(
msg.sender,
_execAddress,
taskModuleAddresses
);
taskId = _createTask(
taskCreator,
_execAddress,
_execDataOrSelector,
_moduleData,
_feeToken
);
}
///@inheritdoc IAutomate
function cancelTask(bytes32 _taskId) external {
address _taskCreator = LibTaskModule.preCancelTask(
_taskId,
msg.sender,
taskModuleAddresses
);
_cancelTask(_taskCreator, _taskId);
}
///@inheritdoc IAutomate
function exec(
address _taskCreator,
address _execAddress,
bytes memory _execData,
LibDataTypes.ModuleData calldata _moduleData,
uint256 _txFee,
address _feeToken,
bool _revertOnFailure
) external onlyGelato {
bytes32 taskId = LibTaskId.getTaskId(
_taskCreator,
_execAddress,
_execData.memorySliceSelector(),
_moduleData,
_feeToken
);
require(
_createdTasks[_taskCreator].contains(taskId),
"Automate.exec: Task not found"
);
fee = _txFee;
feeToken = _feeToken;
bool success = LibTaskModule.onExecTask(
taskId,
_taskCreator,
_execAddress,
_execData,
_moduleData.modules,
_revertOnFailure,
taskModuleAddresses
);
delete fee;
delete feeToken;
emit LibEvents.ExecSuccess(
_txFee,
_feeToken,
_execAddress,
_execData,
taskId,
success
);
}
///@inheritdoc IAutomate
function exec1Balance(
address _taskCreator,
address _execAddress,
bytes memory _execData,
LibDataTypes.ModuleData calldata _moduleData,
Gelato1BalanceParam calldata _oneBalanceParam,
bool _revertOnFailure
) external onlyGelato {
bytes32 taskId = LibTaskId.getTaskId(
_taskCreator,
_execAddress,
_execData.memorySliceSelector(),
_moduleData,
address(0)
);
require(
_createdTasks[_taskCreator].contains(taskId),
"Automate.exec: Task not found"
);
bool success = LibTaskModule.onExecTask(
taskId,
_taskCreator,
_execAddress,
_execData,
_moduleData.modules,
_revertOnFailure,
taskModuleAddresses
);
emit LibEvents.ExecSuccess(
0,
address(0),
_execAddress,
_execData,
taskId,
success
);
emit LogUseGelato1Balance(
_oneBalanceParam.sponsor,
_execAddress,
_oneBalanceParam.feeToken,
_oneBalanceParam.oneBalanceChainId,
_oneBalanceParam.nativeToFeeTokenXRateNumerator,
_oneBalanceParam.nativeToFeeTokenXRateDenominator,
_oneBalanceParam.correlationId
);
}
function execBypassModuleSyncFee(
address _taskCreator,
address _execAddress,
bytes32 _taskId,
uint256 _txFee,
address _feeToken,
bytes memory _execData,
bool _revertOnFailure,
bool _singleExec
) external onlyGelato {
require(
_createdTasks[_taskCreator].contains(_taskId),
"Automate.exec: Task not found"
);
fee = _txFee;
feeToken = _feeToken;
bool success = LibBypassModule.onExecTask(
_taskId,
_taskCreator,
_execAddress,
_execData,
_revertOnFailure,
_singleExec,
_createdTasks
);
delete fee;
delete feeToken;
emit LibEvents.ExecBypassModuleSyncFeeSuccess(
_taskId,
_txFee,
_feeToken,
success
);
}
///@inheritdoc IAutomate
function execBypassModule(
address _taskCreator,
address _execAddress,
bytes32 _taskId,
bytes32 _correlationId,
bytes memory _execData,
bool _revertOnFailure,
bool _singleExec
) external onlyGelato {
require(
_createdTasks[_taskCreator].contains(_taskId),
"Automate.exec: Task not found"
);
bool success = LibBypassModule.onExecTask(
_taskId,
_taskCreator,
_execAddress,
_execData,
_revertOnFailure,
_singleExec,
_createdTasks
);
emit LibEvents.ExecBypassModuleSuccess(
_taskId,
_correlationId,
success
);
}
///@inheritdoc IAutomate
function setModule(
LibDataTypes.Module[] calldata _modules,
address[] calldata _moduleAddresses
) external onlyProxyAdmin {
uint256 length = _modules.length;
for (uint256 i; i < length; i++) {
taskModuleAddresses[_modules[i]] = _moduleAddresses[i];
}
}
///@inheritdoc IAutomate
function getFeeDetails() external view returns (uint256, address) {
return (fee, feeToken);
}
///@inheritdoc IAutomate
function getTaskIdsByUser(address _taskCreator)
external
view
returns (bytes32[] memory)
{
bytes32[] memory taskIds = _createdTasks[_taskCreator].values();
return taskIds;
}
///@inheritdoc IAutomate
function getTaskId(
address taskCreator,
address execAddress,
bytes4 execSelector,
LibDataTypes.ModuleData memory moduleData,
address feeToken
) external pure returns (bytes32 taskId) {
taskId = LibTaskId.getTaskId(
taskCreator,
execAddress,
execSelector,
moduleData,
feeToken
);
}
function _createTask(
address _taskCreator,
address _execAddress,
bytes memory _execDataOrSelector,
LibDataTypes.ModuleData memory _moduleData,
address _feeToken
) private returns (bytes32 taskId) {
taskId = LibTaskId.getTaskId(
_taskCreator,
_execAddress,
_execDataOrSelector.memorySliceSelector(),
_moduleData,
_feeToken
);
require(
!_createdTasks[_taskCreator].contains(taskId),
"Automate.createTask: Duplicate task"
);
LibTaskModule.onCreateTask(
taskId,
_taskCreator,
_execAddress,
_execDataOrSelector,
_moduleData,
taskModuleAddresses
);
_createdTasks[_taskCreator].add(taskId);
emit LibEvents.TaskCreated(
_taskCreator,
_execAddress,
_execDataOrSelector,
_moduleData,
_feeToken,
taskId
);
}
function _cancelTask(address _taskCreator, bytes32 _taskId) private {
require(
_createdTasks[_taskCreator].contains(_taskId),
"Automate.cancelTask: Task not found"
);
_createdTasks[_taskCreator].remove(_taskId);
emit LibEvents.TaskCancelled(_taskId, _taskCreator);
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
import {
EnumerableSet
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {LibDataTypes} from "./libraries/LibDataTypes.sol";
/**
* @notice Storage layout of Automate smart contract.
*/
// solhint-disable max-states-count
abstract contract AutomateStorage {
mapping(bytes32 => address) public taskCreator; ///@dev Deprecated
mapping(bytes32 => address) public execAddresses; ///@dev Deprecated
mapping(address => EnumerableSet.Bytes32Set) internal _createdTasks;
uint256 public fee;
address public feeToken;
///@dev Appended State
mapping(bytes32 => LibDataTypes.Time) public timedTask; ///@dev Deprecated
mapping(LibDataTypes.Module => address) public taskModuleAddresses;
mapping(bytes32 => uint256) public nonce1Balance; ///@dev Deprecated
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {GelatoBytes} from "../vendor/gelato/GelatoBytes.sol";
// solhint-disable private-vars-leading-underscore
// solhint-disable func-visibility
function _call(
address _add,
bytes memory _data,
uint256 _value,
bool _revertOnFailure,
string memory _tracingInfo
) returns (bool success, bytes memory returnData) {
(success, returnData) = _add.call{value: _value}(_data);
if (!success && _revertOnFailure)
GelatoBytes.revertWithError(returnData, _tracingInfo);
}
function _delegateCall(
address _add,
bytes memory _data,
string memory _tracingInfo
) returns (bool success, bytes memory returnData) {
(success, returnData) = _add.delegatecall(_data);
if (!success) GelatoBytes.revertWithError(returnData, _tracingInfo);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
import {
SafeERC20,
IERC20
} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// solhint-disable private-vars-leading-underscore
// solhint-disable func-visibility
function _transfer(
address payable _to,
address _paymentToken,
uint256 _amount
) {
if (_paymentToken == ETH) {
(bool success, ) = _to.call{value: _amount}("");
require(success, "_transfer: ETH transfer failed");
} else {
SafeERC20.safeTransfer(IERC20(_paymentToken), _to, _amount);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {LibDataTypes} from "../libraries/LibDataTypes.sol";
import {IGelato1Balance} from "./IGelato1Balance.sol";
// solhint-disable max-line-length
interface IAutomate is IGelato1Balance {
/**
* @notice Initiates a task with conditions which Gelato will monitor and execute when conditions are met.
*
* @param execAddress Address of contract that should be called by Gelato.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
* @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
* @param feeToken Address of token to be used as payment. Use address(0) if Gelato 1Balance is being used, 0xeeeeee... for ETH or native tokens.
*
* @return taskId Unique hash of the task created.
*/
function createTask(
address execAddress,
bytes calldata execData,
LibDataTypes.ModuleData calldata moduleData,
address feeToken
) external returns (bytes32 taskId);
/**
* @notice Terminates a task that was created and Gelato can no longer execute it.
*
* @param taskId Unique hash of the task that is being cancelled. {See LibTaskId-getTaskId}
*/
function cancelTask(bytes32 taskId) external;
/**
* @notice Execution API called by Gelato, using Sync Fee as fee payment method
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called by Gelato.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
* @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
* @param txFee Fee paid to Gelato for execution, transfered to Gelato.feeCollector().
* @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
* @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
*/
function exec(
address taskCreator,
address execAddress,
bytes memory execData,
LibDataTypes.ModuleData calldata moduleData,
uint256 txFee,
address feeToken,
bool revertOnFailure
) external;
/**
* @notice Execution API called by Gelato, using Gelato 1Balance as fee payment method.
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called by Gelato.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
* @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
* @param oneBalanceParam Parameters required for fee payment with Gelato 1Balance.
* @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
*/
function exec1Balance(
address taskCreator,
address execAddress,
bytes memory execData,
LibDataTypes.ModuleData calldata moduleData,
Gelato1BalanceParam calldata oneBalanceParam,
bool revertOnFailure
) external;
/**
* @notice Execution API called by Gelato, using Gelato 1Balance as fee payment method.
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called by Gelato.
* @param taskId Unique hash of the task.
* @param correlationId Id of the execution to be used for 1Balance settlement.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
* @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
* @param singleExec If the task is a SingleExec task. If true, task will be cancelled after execution.
*/
function execBypassModule(
address taskCreator,
address execAddress,
bytes32 taskId,
bytes32 correlationId,
bytes memory execData,
bool revertOnFailure,
bool singleExec
) external;
/**
* @notice Execution API called by Gelato, using Gelato Sync fee as fee payment method.
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called by Gelato.
* @param taskId Unique hash of the task.
* @param txFee Fee paid to Gelato for execution, transfered to Gelato.feeCollector().
* @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
* @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
* @param singleExec If the task is a SingleExec task. If true, task will be cancelled after execution.
*/
function execBypassModuleSyncFee(
address taskCreator,
address execAddress,
bytes32 taskId,
uint256 txFee,
address feeToken,
bytes memory execData,
bool revertOnFailure,
bool singleExec
) external;
/**
* @notice Sets the address of task modules. Only callable by proxy admin.
*
* @param modules List of modules to be set
* @param moduleAddresses List of addresses for respective modules.
*/
function setModule(
LibDataTypes.Module[] calldata modules,
address[] calldata moduleAddresses
) external;
/**
* @notice Helper function to query fee and feeToken to be used for payment. (For executions which pays itself)
*
* @return uint256 Fee amount to be paid.
* @return address Token to be paid. (Determined and passed by taskCreator during createTask)
*/
function getFeeDetails() external view returns (uint256, address);
/**
* @notice Helper func to query all open tasks by a task creator.
*
* @param taskCreator Address of task creator to query.
*
* @return bytes32[] List of taskIds created.
*/
function getTaskIdsByUser(address taskCreator)
external
view
returns (bytes32[] memory);
/**
* @notice Helper function to compute task id with module arguments
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that will be called by Gelato.
* @param execSelector Signature of the function which will be called by Gelato.
* @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
* @param feeToken Address of token to be used as payment. Use address(0) if Gelato 1Balance is being used, 0xeeeeee... for ETH or native tokens.
*/
function getTaskId(
address taskCreator,
address execAddress,
bytes4 execSelector,
LibDataTypes.ModuleData memory moduleData,
address feeToken
) external pure returns (bytes32 taskId);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IGelato1Balance {
struct Gelato1BalanceParam {
address sponsor;
address feeToken;
uint256 oneBalanceChainId;
uint256 nativeToFeeTokenXRateNumerator;
uint256 nativeToFeeTokenXRateDenominator;
bytes32 correlationId;
}
event LogUseGelato1Balance(
address indexed sponsor,
address indexed target,
address indexed feeToken,
uint256 oneBalanceChainId,
uint256 nativeToFeeTokenXRateNumerator,
uint256 nativeToFeeTokenXRateDenominator,
bytes32 correlationId
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
// solhint-disable max-line-length
interface ITaskModule {
/**
* @notice Called before generating taskId.
* @dev Modules can override execAddress or taskCreator. {See ProxyModule-preCreateTask}
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called.
*
* @return address Overriden or original taskCreator.
* @return address Overriden or original execAddress.
*/
function preCreateTask(address taskCreator, address execAddress)
external
returns (address, address);
/**
* @notice Initiates task module whenever `createTask` is being called.
*
* @param taskId Unique hash of the task created.
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
* @param initModuleArg Encoded arguments for module if any.
*/
function onCreateTask(
bytes32 taskId,
address taskCreator,
address execAddress,
bytes calldata execData,
bytes calldata initModuleArg
) external;
/**
* @notice Called before taskId is removed from _createdTasks[].
* @dev Modules can override taskCreator.
*
* @param taskId Unique hash of the task created.
* @param taskCreator The address which created the task.
*
* @return address Overriden or original taskCreator.
*/
function preCancelTask(bytes32 taskId, address taskCreator)
external
returns (address);
/**
* @notice Called during `exec` and before execAddress is called.
*
* @param taskId Unique hash of the task created.
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
*
* @return address Overriden or original execution address.
* @return bytes Overriden or original execution data.
*/
function preExecCall(
bytes32 taskId,
address taskCreator,
address execAddress,
bytes calldata execData
) external returns (address, bytes memory);
/**
* @notice Called during `exec` and after execAddress is called.
*
* @param taskId Unique hash of the task created.
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that should be called.
* @param execData Execution data to be called with / function selector if execution data is yet to be determined.
*/
function postExecCall(
bytes32 taskId,
address taskCreator,
address execAddress,
bytes calldata execData
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {
EnumerableSet
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {_call, _delegateCall} from "../functions/FExec.sol";
import {LibDataTypes} from "./LibDataTypes.sol";
import {LibEvents} from "./LibEvents.sol";
import {LibTaskModule} from "./LibTaskModule.sol";
import {LibTaskModuleConfig} from "./LibTaskModuleConfig.sol";
import {ITaskModule} from "../interfaces/ITaskModule.sol";
// solhint-disable function-max-lines
/// @notice Simplified library for task executions
library LibBypassModule {
using EnumerableSet for EnumerableSet.Bytes32Set;
using LibTaskModuleConfig for LibDataTypes.Module;
/**
* @notice Delegate calls SingleExecModule on exec for single exec tasks.
*
* @param _taskId Unique hash of the task. {See LibTaskId-getTaskId}
* @param _taskCreator Address which created the task.
* @param _execAddress Address of contract that will be called by Gelato.
* @param _execData Execution data to be called with / function selector.
* @param _revertOnFailure To revert or not if call to execAddress fails.
* @param _singleExec If task is a single exec task.
* @param _createdTasks The storage reference of owner to the taskIds created mapping.
*/
function onExecTask(
bytes32 _taskId,
address _taskCreator,
address _execAddress,
bytes memory _execData,
bool _revertOnFailure,
bool _singleExec,
mapping(address => EnumerableSet.Bytes32Set) storage _createdTasks
) internal returns (bool callSuccess) {
(callSuccess, ) = _call(
_execAddress,
abi.encodePacked(_execData, _taskCreator),
0,
_revertOnFailure,
"Automate.exec: "
);
if (_singleExec) {
_createdTasks[_taskCreator].remove(_taskId);
emit LibEvents.TaskCancelled(_taskId, _taskCreator);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
// solhint-disable max-line-length
library LibDataTypes {
/**
* @notice Whitelisted modules that are available for users to customise conditions and specifications of their tasks.
*
* @param RESOLVER Use dynamic condition & input data for execution. {See ResolverModule.sol}
* @param DEPRECATED_TIME deprecated
* @param PROXY Creates a dedicated caller (msg.sender) to be used when executing the task. {See ProxyModule.sol}
* @param SINGLE_EXEC Task is cancelled after one execution. {See SingleExecModule.sol}
* @param WEB3_FUNCTION Use off-chain condition & input data for execution. {See Web3FunctionModule.sol}
* @param TRIGGER Repeated execution of task ata a specified timing and interval or cron. {See TriggerModule.sol}
*/
enum Module {
RESOLVER,
DEPRECATED_TIME, // @deprecated
PROXY,
SINGLE_EXEC,
WEB3_FUNCTION,
TRIGGER
}
/**
* @notice Struct to contain modules and their relative arguments that are used for task creation.
*
* @param modules List of selected modules.
* @param args Arguments of modules if any. Pass "0x" for modules which does not require args {See encodeModuleArg}
*/
struct ModuleData {
Module[] modules;
bytes[] args;
}
/**
* @notice Struct for time module.
*
* @param nextExec Time when the next execution should occur.
* @param interval Time interval between each execution.
*/
struct Time {
uint128 nextExec;
uint128 interval;
}
/**
* @notice Types of trigger
*
* @param TIME Time triggered tasks, starting at a specific time and triggered intervally
* @param CRON Cron triggered tasks, triggered according to the cron conditions
*/
enum TriggerType {
TIME,
CRON,
EVENT,
BLOCK
}
/**
* @notice Struct for trigger module
*
* @param triggerType Type of the trigger
* @param triggerConfig Trigger configuration that shuold be parsed according to triggerType
*/
struct TriggerModuleData {
TriggerType triggerType;
bytes triggerConfig;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {LibDataTypes} from "./LibDataTypes.sol";
library LibEvents {
/**
* @notice Emitted when `createTask` is called.
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that is called by Gelato.
* @param execDataOrSelector Execution data / function selector.
* @param moduleData Conditional modules used. {See LibDataTypes-ModuleData}
* @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
* @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
*/
event TaskCreated(
address indexed taskCreator,
address indexed execAddress,
bytes execDataOrSelector,
LibDataTypes.ModuleData moduleData,
address feeToken,
bytes32 indexed taskId
);
/**
* @notice Emitted when `cancelTask` is called.
*
* @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
* @param taskCreator The address which owned the task.
*/
event TaskCancelled(bytes32 taskId, address taskCreator);
/**
* @notice Emitted when `exec` is called.
*
* @param txFee Fee paid to Gelato for execution
* @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
* @param execAddress Address of contract that will be called by Gelato.
* @param execData Execution data / function selector.
* @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
* @param callSuccess Status of the call to execAddress.
*/
event ExecSuccess(
uint256 indexed txFee,
address indexed feeToken,
address indexed execAddress,
bytes execData,
bytes32 taskId,
bool callSuccess
);
/**
* @notice Emitted when `execBypassModule` is called.
*
* @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
* @param correlationId Id of the execution to be used for 1Balance settlement.
* @param callSuccess Status of the call to execAddress.
*/
event ExecBypassModuleSuccess(
bytes32 taskId,
bytes32 correlationId,
bool callSuccess
);
/**
* @notice Emitted when `execBypassModuleSyncFee` is called.
*
* @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
* @param txFee Fee paid to Gelato for execution
* @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
* @param callSuccess Status of the call to execAddress.
*/
event ExecBypassModuleSyncFeeSuccess(
bytes32 taskId,
uint256 txFee,
address feeToken,
bool callSuccess
);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {LibDataTypes} from "./LibDataTypes.sol";
/**
* @notice Library to compute taskId of tasks.
*/
// solhint-disable max-line-length
library LibTaskId {
/**
* @notice Returns taskId of taskCreator.
*
* @param taskCreator The address which created the task.
* @param execAddress Address of contract that will be called by Gelato.
* @param execSelector Signature of the function which will be called by Gelato.
* @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
* @param feeToken Address of token to be used as payment. Use address(0) if Gelato 1Balance is being used, 0xeeeeee... for ETH or native tokens.
*/
function getTaskId(
address taskCreator,
address execAddress,
bytes4 execSelector,
LibDataTypes.ModuleData memory moduleData,
address feeToken
) internal pure returns (bytes32 taskId) {
taskId = keccak256(
abi.encode(
taskCreator,
execAddress,
execSelector,
moduleData,
feeToken
)
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {_call, _delegateCall} from "../functions/FExec.sol";
import {LibDataTypes} from "./LibDataTypes.sol";
import {LibTaskModuleConfig} from "./LibTaskModuleConfig.sol";
import {ITaskModule} from "../interfaces/ITaskModule.sol";
// solhint-disable function-max-lines
/// @notice Library to call task modules on task creation and execution.
library LibTaskModule {
using LibTaskModuleConfig for LibDataTypes.Module;
/**
* @notice Delegate calls task modules before generating taskId.
*
* @param _execAddress Address of contract that will be called by Gelato.
* @param _taskCreator The address which created the task.
* @param taskModuleAddresses The storage reference to the mapping of modules to their address.
*/
function preCreateTask(
address _taskCreator,
address _execAddress,
mapping(LibDataTypes.Module => address) storage taskModuleAddresses
) internal returns (address, address) {
uint256 length = uint256(type(LibDataTypes.Module).max) + 1;
for (uint256 i; i < length; i++) {
LibDataTypes.Module module = LibDataTypes.Module(i);
if (!module.requirePreCreate()) continue;
address moduleAddress = taskModuleAddresses[module];
_moduleInitialised(moduleAddress);
bytes memory delegatecallData = abi.encodeWithSelector(
ITaskModule.preCreateTask.selector,
_taskCreator,
_execAddress
);
(, bytes memory returnData) = _delegateCall(
moduleAddress,
delegatecallData,
"Automate.preCreateTask: "
);
(_taskCreator, _execAddress) = abi.decode(
returnData,
(address, address)
);
}
return (_taskCreator, _execAddress);
}
/**
* @notice Delegate calls task modules on create task to initialise them.
*
* @param _taskId Unique hash of the task. {See LibTaskId-getTaskId}
* @param _taskCreator The address which created the task.
* @param _execAddress Address of contract that will be called by Gelato.
* @param _execData Execution data to be called with / function selector.
* @param _moduleData Modules that will be used for the task. {See LibDataTypes-ModuleData}
* @param taskModuleAddresses The storage reference to the mapping of modules to their address.
*/
function onCreateTask(
bytes32 _taskId,
address _taskCreator,
address _execAddress,
bytes memory _execData,
LibDataTypes.ModuleData memory _moduleData,
mapping(LibDataTypes.Module => address) storage taskModuleAddresses
) internal {
uint256 length = _moduleData.modules.length;
_validModules(_moduleData.modules);
for (uint256 i; i < length; i++) {
LibDataTypes.Module module = _moduleData.modules[i];
if (!module.requireOnCreate()) continue;
address moduleAddress = taskModuleAddresses[module];
_moduleInitialised(moduleAddress);
bytes memory delegatecallData = abi.encodeWithSelector(
ITaskModule.onCreateTask.selector,
_taskId,
_taskCreator,
_execAddress,
_execData,
_moduleData.args[i]
);
_delegateCall(
moduleAddress,
delegatecallData,
"Automate.onCreateTask: "
);
}
}
/**
* @notice Delegate calls task modules before removing task.
*
* @param _taskId Unique hash of the task. {See LibTaskId-getTaskId}
* @param _taskCreator The address which created the task.
* @param taskModuleAddresses The storage reference to the mapping of modules to their address.
*/
function preCancelTask(
bytes32 _taskId,
address _taskCreator,
mapping(LibDataTypes.Module => address) storage taskModuleAddresses
) internal returns (address) {
uint256 length = uint256(type(LibDataTypes.Module).max);
for (uint256 i; i <= length; i++) {
LibDataTypes.Module module = LibDataTypes.Module(i);
if (!module.requirePreCancel()) continue;
address moduleAddress = taskModuleAddresses[module];
_moduleInitialised(moduleAddress);
bytes memory delegatecallData = abi.encodeWithSelector(
ITaskModule.preCancelTask.selector,
_taskId,
_taskCreator
);
(, bytes memory returnData) = _delegateCall(
moduleAddress,
delegatecallData,
"Automate.preCancelTask: "
);
(_taskCreator) = abi.decode(returnData, (address));
}
return _taskCreator;
}
/**
* @notice Delegate calls task modules on exec.
*
* @param _taskId Unique hash of the task. {See LibTaskId-getTaskId}
* @param _taskCreator Address which created the task.
* @param _execAddress Address of contract that will be called by Gelato.
* @param _execData Execution data to be called with / function selector.
* @param _modules Modules that is used for the task. {See LibDataTypes-Module}
* @param _revertOnFailure To revert or not if call to execAddress fails.
* @param taskModuleAddresses The storage reference to the mapping of modules to their address.
*/
function onExecTask(
bytes32 _taskId,
address _taskCreator,
address _execAddress,
bytes memory _execData,
LibDataTypes.Module[] memory _modules,
bool _revertOnFailure,
mapping(LibDataTypes.Module => address) storage taskModuleAddresses
) internal returns (bool callSuccess) {
address[] memory moduleAddresses = _getModuleAddresses(
_modules,
taskModuleAddresses
);
(_execAddress, _execData) = preExecCall(
_taskId,
_taskCreator,
_execAddress,
_execData,
_modules,
moduleAddresses
);
(callSuccess, ) = _call(
_execAddress,
abi.encodePacked(_execData, _taskCreator),
0,
_revertOnFailure,
"Automate.exec: "
);
postExecCall(
_taskId,
_taskCreator,
_execAddress,
_execData,
_modules,
moduleAddresses
);
}
function preExecCall(
bytes32 _taskId,
address _taskCreator,
address _execAddress,
bytes memory _execData,
LibDataTypes.Module[] memory _modules,
address[] memory _moduleAddresses
) internal returns (address, bytes memory) {
uint256 length = _modules.length;
for (uint256 i; i < length; i++) {
if (!_modules[i].requirePreExec()) continue;
bytes memory delegatecallData = abi.encodeWithSelector(
ITaskModule.preExecCall.selector,
_taskId,
_taskCreator,
_execAddress,
_execData
);
(, bytes memory returnData) = _delegateCall(
_moduleAddresses[i],
delegatecallData,
"Automate.preExecCall: "
);
(_execAddress, _execData) = abi.decode(
returnData,
(address, bytes)
);
}
return (_execAddress, _execData);
}
function postExecCall(
bytes32 _taskId,
address _taskCreator,
address _execAddress,
bytes memory _execData,
LibDataTypes.Module[] memory _modules,
address[] memory _moduleAddresses
) internal {
uint256 length = _moduleAddresses.length;
for (uint256 i; i < length; i++) {
if (!_modules[i].requirePostExec()) continue;
bytes memory delegatecallData = abi.encodeWithSelector(
ITaskModule.postExecCall.selector,
_taskId,
_taskCreator,
_execAddress,
_execData
);
_delegateCall(
_moduleAddresses[i],
delegatecallData,
"Automate.postExecCall: "
);
}
}
function _getModuleAddresses(
LibDataTypes.Module[] memory _modules,
mapping(LibDataTypes.Module => address) storage taskModuleAddresses
) private view returns (address[] memory) {
uint256 length = _modules.length;
address[] memory moduleAddresses = new address[](length);
for (uint256 i; i < length; i++) {
moduleAddresses[i] = taskModuleAddresses[_modules[i]];
}
return moduleAddresses;
}
function _moduleInitialised(address _moduleAddress) private pure {
require(
_moduleAddress != address(0),
"Automate._moduleInitialised: Not init"
);
}
/**
* @dev
* - No duplicate modules
* - No deprecated TIME
* - No RESOLVER && WEB3_FUNCTION
* - PROXY is required
*/
function _validModules(LibDataTypes.Module[] memory _modules) private pure {
uint256 length = _modules.length;
uint256 existsLength = uint256(type(LibDataTypes.Module).max) + 1;
bool[] memory exists = new bool[](existsLength);
for (uint256 i = 0; i < length; i++) {
if (i > 0) {
require(
_modules[i] > _modules[i - 1],
"Automate._validModules: Asc only"
);
}
exists[uint256(_modules[i])] = true;
}
require(
!exists[uint256(LibDataTypes.Module.DEPRECATED_TIME)],
"Automate._validModules: TIME is deprecated"
);
require(
!(exists[uint256(LibDataTypes.Module.RESOLVER)] &&
exists[uint256(LibDataTypes.Module.WEB3_FUNCTION)]),
"Automate._validModules: Only RESOLVER or WEB3_FUNCTION"
);
require(
exists[uint256(LibDataTypes.Module.PROXY)],
"Automate._validModules: PROXY is required"
);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {LibDataTypes} from "./LibDataTypes.sol";
/**
* @notice Library to determine wether to call task modules to reduce unnecessary calls.
*/
library LibTaskModuleConfig {
function requirePreCreate(LibDataTypes.Module _module)
internal
pure
returns (bool)
{
if (_module == LibDataTypes.Module.PROXY) return true;
return false;
}
function requirePreCancel(LibDataTypes.Module _module)
internal
pure
returns (bool)
{
if (_module == LibDataTypes.Module.PROXY) return true;
return false;
}
function requireOnCreate(LibDataTypes.Module _module)
internal
pure
returns (bool)
{
if (_module == LibDataTypes.Module.PROXY) return true;
return false;
}
function requirePreExec(LibDataTypes.Module _module)
internal
pure
returns (bool)
{
if (_module == LibDataTypes.Module.PROXY) return true;
return false;
}
function requirePostExec(LibDataTypes.Module _module)
internal
pure
returns (bool)
{
if (_module == LibDataTypes.Module.SINGLE_EXEC) return true;
return false;
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
library GelatoBytes {
function calldataSliceSelector(bytes calldata _bytes)
internal
pure
returns (bytes4 selector)
{
selector =
_bytes[0] |
(bytes4(_bytes[1]) >> 8) |
(bytes4(_bytes[2]) >> 16) |
(bytes4(_bytes[3]) >> 24);
}
function memorySliceSelector(bytes memory _bytes)
internal
pure
returns (bytes4 selector)
{
selector =
_bytes[0] |
(bytes4(_bytes[1]) >> 8) |
(bytes4(_bytes[2]) >> 16) |
(bytes4(_bytes[3]) >> 24);
}
function revertWithError(bytes memory _bytes, string memory _tracingInfo)
internal
pure
{
// 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
if (_bytes.length % 32 == 4) {
bytes4 selector;
assembly {
selector := mload(add(0x20, _bytes))
}
if (selector == 0x08c379a0) {
// Function selector for Error(string)
assembly {
_bytes := add(_bytes, 68)
}
revert(string(abi.encodePacked(_tracingInfo, string(_bytes))));
} else {
revert(
string(abi.encodePacked(_tracingInfo, "NoErrorSelector"))
);
}
} else {
revert(
string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"))
);
}
}
function returnError(bytes memory _bytes, string memory _tracingInfo)
internal
pure
returns (string memory)
{
// 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
if (_bytes.length % 32 == 4) {
bytes4 selector;
assembly {
selector := mload(add(0x20, _bytes))
}
if (selector == 0x08c379a0) {
// Function selector for Error(string)
assembly {
_bytes := add(_bytes, 68)
}
return string(abi.encodePacked(_tracingInfo, string(_bytes)));
} else {
return
string(abi.encodePacked(_tracingInfo, "NoErrorSelector"));
}
} else {
return
string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"));
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
import {
SafeERC20,
IERC20
} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {_transfer, ETH} from "../../functions/FUtils.sol";
abstract contract Gelatofied {
address payable public immutable gelato;
modifier gelatofy(uint256 _amount, address _paymentToken) {
require(msg.sender == gelato, "Gelatofied: Only gelato");
_;
_transfer(gelato, _paymentToken, _amount);
}
modifier onlyGelato() {
require(msg.sender == gelato, "Gelatofied: Only gelato");
_;
}
constructor(address payable _gelato) {
gelato = _gelato;
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
abstract contract Proxied {
/// @notice to be used by initialisation / postUpgrade function so that only the proxy's admin can execute them
/// It also allows these functions to be called inside a contructor
/// even if the contract is meant to be used without proxy
modifier proxied() {
address proxyAdminAddress = _proxyAdmin();
// With hardhat-deploy proxies
// the proxyAdminAddress is zero only for the implementation contract
// if the implementation contract want to be used as a standalone/immutable contract
// it simply has to execute the `proxied` function
// This ensure the proxyAdminAddress is never zero post deployment
// And allow you to keep the same code for both proxied contract and immutable contract
if (proxyAdminAddress == address(0)) {
// ensure can not be called twice when used outside of proxy : no admin
// solhint-disable-next-line security/no-inline-assembly
assembly {
sstore(
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
)
}
} else {
require(msg.sender == proxyAdminAddress);
}
_;
}
modifier onlyProxyAdmin() {
require(msg.sender == _proxyAdmin(), "NOT_AUTHORIZED");
_;
}
function _proxyAdmin() internal view returns (address adminAddress) {
// solhint-disable-next-line security/no-inline-assembly
assembly {
adminAddress := sload(
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
)
}
}
}
File 5 of 6: OpsProxy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import {GelatoBytes} from "../vendor/gelato/GelatoBytes.sol";
// solhint-disable private-vars-leading-underscore
// solhint-disable func-visibility
function _call(
address _add,
bytes memory _data,
uint256 _value,
bool _revertOnFailure,
string memory _tracingInfo
) returns (bool success, bytes memory returnData) {
(success, returnData) = _add.call{value: _value}(_data);
if (!success && _revertOnFailure)
GelatoBytes.revertWithError(returnData, _tracingInfo);
}
function _delegateCall(
address _add,
bytes memory _data,
string memory _tracingInfo
) returns (bool success, bytes memory returnData) {
(success, returnData) = _add.delegatecall(_data);
if (!success) GelatoBytes.revertWithError(returnData, _tracingInfo);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
interface IOpsProxy {
/**
* @notice Emitted when proxy calls a contract successfully in `executeCall`
*
* @param target Address of contract that is called
* @param data Data used in the call.
* @param value Native token value used in the call.
* @param returnData Data returned by the call.
*/
event ExecuteCall(
address indexed target,
bytes data,
uint256 value,
bytes returnData
);
/**
* @notice Multicall to different contracts with different datas.
*
* @param targets Addresses of contracts to be called.
* @param datas Datas for each contract call.
* @param values Native token value for each contract call.
*/
function batchExecuteCall(
address[] calldata targets,
bytes[] calldata datas,
uint256[] calldata values
) external payable;
/**
* @notice Call to a single contract.
*
* @param target Address of contracts to be called.
* @param data Data for contract call.
* @param value Native token value for contract call.
*/
function executeCall(
address target,
bytes calldata data,
uint256 value
) external payable;
/**
* @return address Ops smart contract address
*/
function ops() external view returns (address);
/**
* @return address Owner of the proxy
*/
function owner() external view returns (address);
/**
* @return uint256 version of OpsProxy.
*/
function version() external view returns (uint256);
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
import {Proxied} from "../vendor/proxy/EIP173/Proxied.sol";
import {_call} from "../functions/FExec.sol";
import {IOpsProxy} from "../interfaces/IOpsProxy.sol";
contract OpsProxy is Proxied, IOpsProxy {
// solhint-disable const-name-snakecase
uint256 public constant override version = 1;
address public immutable override ops;
modifier onlyAuth() {
address proxyOwner = owner();
if (msg.sender != proxyOwner) {
require(msg.sender == ops, "OpsProxy: Not authorised");
require(
_getTaskCreator() == proxyOwner,
"OpsProxy: Only tasks created by owner"
);
} // else msg.sender == proxyOwner
_;
}
// solhint-disable no-empty-blocks
constructor(address _ops) {
ops = _ops;
}
receive() external payable {}
///@inheritdoc IOpsProxy
function batchExecuteCall(
address[] calldata _targets,
bytes[] calldata _datas,
uint256[] calldata _values
) external payable override onlyAuth {
uint256 length = _targets.length;
require(
length == _datas.length && length == _values.length,
"OpsProxy: Length mismatch"
);
for (uint256 i; i < length; i++)
_executeCall(_targets[i], _datas[i], _values[i]);
}
///@inheritdoc IOpsProxy
function executeCall(
address _target,
bytes calldata _data,
uint256 _value
) external payable override onlyAuth {
_executeCall(_target, _data, _value);
}
function owner() public view returns (address) {
return _proxyAdmin();
}
function _executeCall(
address _target,
bytes calldata _data,
uint256 _value
) private {
(, bytes memory returnData) = _call(
_target,
_data,
_value,
true,
"OpsProxy.executeCall: "
);
emit ExecuteCall(_target, _data, _value, returnData);
}
function _getTaskCreator() private pure returns (address taskCreator) {
assembly {
taskCreator := shr(96, calldataload(sub(calldatasize(), 20)))
}
}
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.12;
library GelatoBytes {
function calldataSliceSelector(bytes calldata _bytes)
internal
pure
returns (bytes4 selector)
{
selector =
_bytes[0] |
(bytes4(_bytes[1]) >> 8) |
(bytes4(_bytes[2]) >> 16) |
(bytes4(_bytes[3]) >> 24);
}
function memorySliceSelector(bytes memory _bytes)
internal
pure
returns (bytes4 selector)
{
selector =
_bytes[0] |
(bytes4(_bytes[1]) >> 8) |
(bytes4(_bytes[2]) >> 16) |
(bytes4(_bytes[3]) >> 24);
}
function revertWithError(bytes memory _bytes, string memory _tracingInfo)
internal
pure
{
// 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
if (_bytes.length % 32 == 4) {
bytes4 selector;
assembly {
selector := mload(add(0x20, _bytes))
}
if (selector == 0x08c379a0) {
// Function selector for Error(string)
assembly {
_bytes := add(_bytes, 68)
}
revert(string(abi.encodePacked(_tracingInfo, string(_bytes))));
} else {
revert(
string(abi.encodePacked(_tracingInfo, "NoErrorSelector"))
);
}
} else {
revert(
string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"))
);
}
}
function returnError(bytes memory _bytes, string memory _tracingInfo)
internal
pure
returns (string memory)
{
// 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
if (_bytes.length % 32 == 4) {
bytes4 selector;
assembly {
selector := mload(add(0x20, _bytes))
}
if (selector == 0x08c379a0) {
// Function selector for Error(string)
assembly {
_bytes := add(_bytes, 68)
}
return string(abi.encodePacked(_tracingInfo, string(_bytes)));
} else {
return
string(abi.encodePacked(_tracingInfo, "NoErrorSelector"));
}
} else {
return
string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"));
}
}
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
abstract contract Proxied {
/// @notice to be used by initialisation / postUpgrade function so that only the proxy's admin can execute them
/// It also allows these functions to be called inside a contructor
/// even if the contract is meant to be used without proxy
modifier proxied() {
address proxyAdminAddress = _proxyAdmin();
// With hardhat-deploy proxies
// the proxyAdminAddress is zero only for the implementation contract
// if the implementation contract want to be used as a standalone/immutable contract
// it simply has to execute the `proxied` function
// This ensure the proxyAdminAddress is never zero post deployment
// And allow you to keep the same code for both proxied contract and immutable contract
if (proxyAdminAddress == address(0)) {
// ensure can not be called twice when used outside of proxy : no admin
// solhint-disable-next-line security/no-inline-assembly
assembly {
sstore(
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
)
}
} else {
require(msg.sender == proxyAdminAddress);
}
_;
}
modifier onlyProxyAdmin() {
require(msg.sender == _proxyAdmin(), "NOT_AUTHORIZED");
_;
}
function _proxyAdmin() internal view returns (address adminAddress) {
// solhint-disable-next-line security/no-inline-assembly
assembly {
adminAddress := sload(
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
)
}
}
}
File 6 of 6: PythUpdater
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./PythStructs.sol";
import "./IPythEvents.sol";
/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth is IPythEvents {
/// @notice Returns the price of a price feed without any sanity checks.
/// @dev This function returns the most recent price update in this contract without any recency checks.
/// This function is unsafe as the returned price update may be arbitrarily far in the past.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use `getPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price that is no older than `age` seconds of the current time.
/// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
/// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
/// However, if the price is not recent this function returns the latest available price.
///
/// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
/// the returned price is recent or useful for any particular application.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
/// of the current time.
/// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Update price feeds with given update messages.
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
/// Prices will be updated if they are more recent than the current stored prices.
/// The call will succeed even if the update is not the most recent.
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
function updatePriceFeeds(bytes[] calldata updateData) external payable;
/// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
/// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
/// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
/// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
/// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
/// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
/// Otherwise, it calls updatePriceFeeds method to update the prices.
///
/// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
function updatePriceFeedsIfNecessary(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64[] calldata publishTimes
) external payable;
/// @notice Returns the required fee to update an array of price updates.
/// @param updateData Array of price update data.
/// @return feeAmount The required fee in Wei.
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint feeAmount);
/// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
/// within `minPublishTime` and `maxPublishTime`.
///
/// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
/// otherwise, please consider using `updatePriceFeeds`. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdates(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
/// @notice Similar to `parsePriceFeedUpdates` but ensures the updates returned are
/// the first updates published in minPublishTime. That is, if there are multiple updates for a given timestamp,
/// this method will return the first update. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range and uniqueness condition.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdatesUnique(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @title IPythEvents contains the events that Pyth contract emits.
/// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
interface IPythEvents {
/// @dev Emitted when the price feed with `id` has received a fresh update.
/// @param id The Pyth Price Feed ID.
/// @param publishTime Publish time of the given price update.
/// @param price Price of the given price update.
/// @param conf Confidence interval of the given price update.
event PriceFeedUpdate(
bytes32 indexed id,
uint64 publishTime,
int64 price,
uint64 conf
);
}
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
contract PythStructs {
// A price with a degree of uncertainty, represented as a price +- a confidence interval.
//
// The confidence interval roughly corresponds to the standard error of a normal distribution.
// Both the price and confidence are stored in a fixed-point numeric representation,
// `x * (10^expo)`, where `expo` is the exponent.
//
// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how
// to how this price safely.
struct Price {
// Price
int64 price;
// Confidence interval around the price
uint64 conf;
// Price exponent
int32 expo;
// Unix timestamp describing when the price was published
uint publishTime;
}
// PriceFeed represents a current aggregate price from pyth publisher feeds.
struct PriceFeed {
// The price ID.
bytes32 id;
// Latest available price
Price price;
// Latest available exponentially-weighted moving average price
Price emaPrice;
}
}
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import {IPyth} from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
/**
* @title A port of a chainlink aggregator powered by pyth network feeds
* @author Deadshot Ryker
* @notice This does not store any roundId information on-chain. Please review the code before using this implementation.
*/
contract PythUpdater {
IPyth public pyth;
constructor(address _pyth) {
pyth = IPyth(_pyth);
}
function updateFeeds(bytes[] calldata priceUpdateData) public payable {
// Update the prices to the latest available values and pay the required fee for it. The `priceUpdateData` data
// should be retrieved from our off-chain Price Service API using the `pyth-evm-js` package.
// See section "How Pyth Works on EVM Chains" below for more information.
uint fee = pyth.getUpdateFee(priceUpdateData);
pyth.updatePriceFeeds{value: fee}(priceUpdateData);
}
receive() external payable {
// do nothing
}
}