ETH Price: $3,970.52 (-3.18%)

Transaction Decoder

Block:
23561377 at Oct-12-2025 11:34:23 AM +UTC
Transaction Fee:
0.00445410104403288 ETH $17.69
Gas Used:
2,928,080 Gas / 1.521167811 Gwei

Emitted Events:

83 PositionNFT.Transfer( from=[Sender] 0x7822159ee394d14745cde63a706f965fb73c7ac8, to=0x5bdeb2152f185bf59f2de027cbbc05355cc965bd, tokenId=13415595383038600035140745001195761463798945797809123092877826486633240727524 )
84 0x5bdeb2152f185bf59f2de027cbbc05355cc965bd.0xe1971091f8cd4abc65419e3b56c5a5b1542ce392786599db6e069e80b8f269cc( 0xe1971091f8cd4abc65419e3b56c5a5b1542ce392786599db6e069e80b8f269cc, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x0000000000000000000000007822159ee394d14745cde63a706f965fb73c7ac8 )
85 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000098c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c, 0x000000000000000000000000ab515542d621574f9b5212d50593cd0c07e641bd, 0000000000000000000000000000000000000000000000000000000337b437fc )
86 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000ab515542d621574f9b5212d50593cd0c07e641bd, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 0000000000000000000000000000000000000000000000000000000337b437fc )
87 ERC1967Proxy.0x8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a7( 0x8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a7, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0000000000000000000000000000000000000000000000000000000337b437fc )
88 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 0x0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e, 0000000000000000000000000000000000000000000000000000000337b437fc )
89 ERC1967Proxy.0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58( 0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e, 0000000000000000000000000000000000000000000000000000000337b437fc )
90 AdaptiveCurveIrm.BorrowRateUpdate( id=4005BA6EB7D2221FE58102BD320AA6D83C47B212771BC950AB71C5074D9AB0EC, avgBorrowRate=2247270162, rateAtTarget=2277285738 )
91 Morpho.AccrueInterest( id=4005BA6EB7D2221FE58102BD320AA6D83C47B212771BC950AB71C5074D9AB0EC, prevBorrowRate=2247270162, interest=38265469, feeShares=0 )
92 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e, 0x00000000000000000000000055191784e1e0f77a94a2da1bf98f7d70e7baca4d, 0000000000000000000000000000000000000000000000000000000337b437fc )
93 Morpho.Repay( id=4005BA6EB7D2221FE58102BD320AA6D83C47B212771BC950AB71C5074D9AB0EC, caller=0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d, onBehalf=0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d, assets=13819459580, shares=13363590754624113 )
94 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000055191784e1e0f77a94a2da1bf98f7d70e7baca4d, 0x000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb, 0000000000000000000000000000000000000000000000000000000337b437fc )
95 0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d.0x01e7ee7e76483485fd1d9e5b1c6a72af05e18dac7fc43f767d6897ef153bef86( 0x01e7ee7e76483485fd1d9e5b1c6a72af05e18dac7fc43f767d6897ef153bef86, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0000000000000000000000000000000000000000000000000000000337b437fc, 000000000000000000000000000000000000000000000000000000193d2403b2 )
96 ERC1967Proxy.0xe389f9a675d18cd405aef115ed4e77a5be06982c4202af0a99204b5c61d6565b( 0xe389f9a675d18cd405aef115ed4e77a5be06982c4202af0a99204b5c61d6565b, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0000000000000000000000000000000000000000000000000000000000000002, 0000000000000000000000000000000000000000000000000000000337b437fc, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
97 Morpho.WithdrawCollateral( id=4005BA6EB7D2221FE58102BD320AA6D83C47B212771BC950AB71C5074D9AB0EC, caller=0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d, onBehalf=0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d, receiver=ERC1967Proxy, assets=17500000000000000000000 )
98 0x2ca5f2c4300450d53214b00546795c1c07b89acb.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb, 0x0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e, 0000000000000000000000000000000000000000000003b4ad496106b7f00000 )
99 0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d.0xad382ae4a0a8c252d70b902c9cc346f3e881ad505df3f728122a01006bb88f09( 0xad382ae4a0a8c252d70b902c9cc346f3e881ad505df3f728122a01006bb88f09, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x0000000000000000000000002ca5f2c4300450d53214b00546795c1c07b89acb, 0000000000000000000000000000000000000000000003b4ad496106b7f00000, 000000000000000000000000000000000000000000001a27df762de2b7ed1c1e )
100 0x2ca5f2c4300450d53214b00546795c1c07b89acb.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 0000000000000000000000000000000000000000000003b4ad496106b7f00000 )
101 ERC1967Proxy.0x8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a7( 0x8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a7, 0x0000000000000000000000002ca5f2c4300450d53214b00546795c1c07b89acb, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0000000000000000000000000000000000000000000003b4ad496106b7f00000 )
102 ERC1967Proxy.0xe389f9a675d18cd405aef115ed4e77a5be06982c4202af0a99204b5c61d6565b( 0xe389f9a675d18cd405aef115ed4e77a5be06982c4202af0a99204b5c61d6565b, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0000000000000000000000000000000000000000000000000000000000000001, fffffffffffffffffffffffffffffffffffffffffffffc4b52b69ef948100000, fffffffffffffffffffffffffffffffffffffffffffffc4b52b69ef948100000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
103 0x2ca5f2c4300450d53214b00546795c1c07b89acb.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 0x000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 0000000000000000000000000000000000000000000003b4ad496106b7f00000 )
104 ERC1967Proxy.0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58( 0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58, 0x0000000000000000000000002ca5f2c4300450d53214b00546795c1c07b89acb, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 0000000000000000000000000000000000000000000003b4ad496106b7f00000 )
105 0x2ca5f2c4300450d53214b00546795c1c07b89acb.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 0000000000000000000000000000000000000000000003b4ad496106b7f00000 )
106 0x2ca5f2c4300450d53214b00546795c1c07b89acb.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 0000000000000000000000000000000000000000000000000000000000000000 )
107 0x2ca5f2c4300450d53214b00546795c1c07b89acb.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 0x00000000000000000000000047247749e976c54c6db2a9db68c5cadb05482e9f, 0000000000000000000000000000000000000000000003b4ad496106b7f00000 )
108 0x2ca5f2c4300450d53214b00546795c1c07b89acb.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000047247749e976c54c6db2a9db68c5cadb05482e9f, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000003b4ad496106b7f00000 )
109 0x47247749e976c54c6db2a9db68c5cadb05482e9f.0x71475f2f645813fdbebf53a58968008bff11ee21a58f01c5a9cc263d0bc4703d( 0x71475f2f645813fdbebf53a58968008bff11ee21a58f01c5a9cc263d0bc4703d, 0x0000000000000000000000000000000000000000000000000f4ea637f4401efb )
110 0x0a9b2c09f8756c743b0702973ed33970d16cf6f9.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000047247749e976c54c6db2a9db68c5cadb05482e9f, 0x0000000000000000000000000a9b2c09f8756c743b0702973ed33970d16cf6f9, 00000000000000000000000000000000000000000000035c1625aefbf36066c2 )
111 0x47247749e976c54c6db2a9db68c5cadb05482e9f.0x5d624aa9c148153ab3446c1b154f660ee7701e549fe9b62dab7171b1c80e6fa2( 0x5d624aa9c148153ab3446c1b154f660ee7701e549fe9b62dab7171b1c80e6fa2, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 0x0000000000000000000000000a9b2c09f8756c743b0702973ed33970d16cf6f9, 0000000000000000000000000000000000000000000003b4ad496106b7f00000, 00000000000000000000000000000000000000000000035c1625aefbf36066c2 )
112 0x0a9b2c09f8756c743b0702973ed33970d16cf6f9.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000a9b2c09f8756c743b0702973ed33970d16cf6f9, 0x0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000035c1625aefbf36066c2 )
113 StakedlvlUSD.Transfer( from=0x0a9b2c09f8756c743b0702973ed33970d16cf6f9, to=ERC1967Proxy, value=15865795777430330304194 )
114 0x0a9b2c09f8756c743b0702973ed33970d16cf6f9.0xaee47cdf925cf525fdae94f9777ee5a06cac37e1c41220d0a8a89ed154f62d1c( 0xaee47cdf925cf525fdae94f9777ee5a06cac37e1c41220d0a8a89ed154f62d1c, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 0x000000000000000000000000d4f480965d2347d421f1bec7f545682e5ec2151d, 0x0000000000000000000000004737d9b4592b40d51e110b94c9c043c6654067ae, 00000000000000000000000000000000000000000000035c1625aefbf36066c2, 00000000000000000000000000000000000000000000035c1625aefbf36066c2 )
115 StakedlvlUSD.Transfer( from=ERC1967Proxy, to=0x6E4141d33021b52C91c28608403db4A0FFB50Ec6, value=15865795777430330304194 )
116 StakedlvlUSD.Transfer( from=0x6E4141d33021b52C91c28608403db4A0FFB50Ec6, to=CurveTwocryptoOptimized, value=15865795777430330304194 )
117 lvlUSD.Transfer( from=CurveTwocryptoOptimized, to=0x6E4141d33021b52C91c28608403db4A0FFB50Ec6, value=17393777950834670122609 )
118 CurveTwocryptoOptimized.TokenExchange( buyer=0x6E4141d33021b52C91c28608403db4A0FFB50Ec6, sold_id=1, tokens_sold=15865795777430330304194, bought_id=0, tokens_bought=17393777950834670122609, fee=14179012472441254200, packed_price_scale=1101795306340440876 )
119 0x6e4141d33021b52c91c28608403db4a0ffb50ec6.0xddac40937f35385a34f721af292e5a83fc5b840f722bff57c2fc71adba708c48( 0xddac40937f35385a34f721af292e5a83fc5b840f722bff57c2fc71adba708c48, 000000000000000000000000f244324fbb57f09f0606ff088bc894b051d632eb, 0000000000000000000000000000000000000000000003aeeb28da19525f3671, 0000000000000000000000007c1156e515aa1a2e851674120074968c905aaf37 )
120 lvlUSD.Transfer( from=0x6E4141d33021b52C91c28608403db4A0FFB50Ec6, to=CurveStableSwapNG, value=17393777950834670122609 )
121 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000001220868672d5b10f3e1cb9ab519e4d0b08545ea4, 0x0000000000000000000000006e4141d33021b52c91c28608403db4a0ffb50ec6, 000000000000000000000000000000000000000000000000000000040bc5ab45 )
122 CurveStableSwapNG.TokenExchange( buyer=0x6E4141d33021b52C91c28608403db4A0FFB50Ec6, sold_id=1, tokens_sold=17393777950834670122609, bought_id=0, tokens_bought=17377372997 )
123 0x6e4141d33021b52c91c28608403db4a0ffb50ec6.0xddac40937f35385a34f721af292e5a83fc5b840f722bff57c2fc71adba708c48( 0xddac40937f35385a34f721af292e5a83fc5b840f722bff57c2fc71adba708c48, 0000000000000000000000001220868672d5b10f3e1cb9ab519e4d0b08545ea4, 000000000000000000000000000000000000000000000000000000040bc5ab45, 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 )
124 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000006e4141d33021b52c91c28608403db4a0ffb50ec6, 0x0000000000000000000000004f82e73edb06d29ff62c91ec8f5ff06571bdeb29, 000000000000000000000000000000000000000000000000000000000022a8ac )
125 0x6e4141d33021b52c91c28608403db4a0ffb50ec6.0x4bc8151c051441255339d01fbaeb38cf109cbfd75e9a5c62fb8f1dfb37fe6fd6( 0x4bc8151c051441255339d01fbaeb38cf109cbfd75e9a5c62fb8f1dfb37fe6fd6, 0x0000000000000000000000004f82e73edb06d29ff62c91ec8f5ff06571bdeb29, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000000022a8ac, 0000000000000000000000000000000000000000000000000000000000000000 )
126 0x6e4141d33021b52c91c28608403db4a0ffb50ec6.0xbf402572f7d269fcae3a56e497d9fc9459e32213d9286c383ad57fa2b532fa8f( 0xbf402572f7d269fcae3a56e497d9fc9459e32213d9286c383ad57fa2b532fa8f, 0000000000000000000000000000000000000000000000000000000000000000 )
127 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000006e4141d33021b52c91c28608403db4a0ffb50ec6, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 000000000000000000000000000000000000000000000000000000040ba30299 )
128 MetaAggregationRouterV2.Swapped( sender=ERC1967Proxy, srcToken=StakedlvlUSD, dstToken=[Receiver] FiatTokenProxy, dstReceiver=PendleRouterV4, spentAmount=15865795777430330304194, returnAmount=17375101593 )
129 MetaAggregationRouterV2.Exchange( pair=0x6E4141d33021b52C91c28608403db4A0FFB50Ec6, amountOut=17375101593, output=[Receiver] FiatTokenProxy )
130 MetaAggregationRouterV2.ClientData( clientData=0x7B22536F75726365223A2250656E646C65222C22416D6F756E74496E555344223A2231373739382E3330323039353538323433222C22416D6F756E744F7574555344223A2231373733322E323038373733333833393437222C22526566657272616C223A22222C22466C616773223A302C22416D6F756E744F7574223A223137373232363033363236222C2254696D657374616D70223A313736303236383831352C22526F7574654944223A2233646535633762642D303963392D343665312D383164322D6634633462346466373936653A64643463653662312D656663372D343635362D393632632D383433373732356562616332222C22496E74656772697479496E666F223A7B224B65794944223A2231222C225369676E6174757265223A22415871337437316833517A33346431504B684B5449707A656E577A6B7573454A466F386C35563138794B426A2B7651674B786D6C42346D367A4F6D42754233634E61774635374963314E4F736D79704E6A6B4D4A6A656C30613053472F445A674F6E484F52574751594E7742566A7665364F5936472B706752623331335255336954316D4D712F44574E68536D597A586735704D486142586E446E34736B35586269774E727748435965736B366F4E6C4F4D6E63576637306139486D7735504E5131684A5330557031735343394A4E316F4167306C7A645844496D644667626C5856434C4C5131696C5855692B5530457546554D324C717035707269356C4B6662557A77544E456476504B71463031766C6E38644C53307876375165703576387656455438737437396E4F6B635873644C6B62556973437A6B4D79536D4147587A4B4667635661625A577032465864576B57477459673D3D227D7D )
131 ERC1967Proxy.0x1d8c50a59805451ff93bb2e438559a86b75386bcac2a591d3181d79e7e8346fd( 0x1d8c50a59805451ff93bb2e438559a86b75386bcac2a591d3181d79e7e8346fd, 0x0000000000000000000000000000000000000000000000000000000000000001, 0x0000000000000000000000004737d9b4592b40d51e110b94c9c043c6654067ae, 00000000000000000000000000000000000000000000035c1625aefbf36066c2 )
132 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 0x000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 000000000000000000000000000000000000000000000000000000040ba30299 )
133 PendleRouterV4.0x5f2e0499a3b6a21fd5e1fac44ac47c9aa7c3afa39076d67162a4993411d496da( 0x5f2e0499a3b6a21fd5e1fac44ac47c9aa7c3afa39076d67162a4993411d496da, 0x000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x00000000000000000000000047247749e976c54c6db2a9db68c5cadb05482e9f, 000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 0000000000000000000000000000000000000000000003b4ad496106b7f00000, 000000000000000000000000000000000000000000000000000000040ba30299, 00000000000000000000000000000000000000000000035c1625aefbf36066c2 )
134 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000dfea386f471d15d8b2ddb931f5e57713974c7899, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 000000000000000000000000000000000000000000000000000000040ba30299 )
135 0xdfea386f471d15d8b2ddb931f5e57713974c7899.0xdd36740e2a012d93061a0d99eaa9107860955de4e90027d3cf465a055026c407( 0xdd36740e2a012d93061a0d99eaa9107860955de4e90027d3cf465a055026c407, 0x0000000000000000000000002ca5f2c4300450d53214b00546795c1c07b89acb, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0000000000000000000000000000000000000000000003b4ad496106b7f00000, 000000000000000000000000000000000000000000000000000000040ba30299 )
136 0x5bdeb2152f185bf59f2de027cbbc05355cc965bd.0xdde2f3711ab09cdddcfee16ca03e54d21fb8cf3fa647b9797913c950d38ad693( 0xdde2f3711ab09cdddcfee16ca03e54d21fb8cf3fa647b9797913c950d38ad693, 0x0000000000000000000000007822159ee394d14745cde63a706f965fb73c7ac8, 0000000000000000000000002ca5f2c4300450d53214b00546795c1c07b89acb, 0000000000000000000000000000000000000000000003b4ad496106b7f00000, 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 000000000000000000000000000000000000000000000000000000040ba30299 )
137 ERC1967Proxy.0x8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a7( 0x8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a7, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 000000000000000000000000000000000000000000000000000000040ba30299 )
138 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 0x000000000000000000000000ab515542d621574f9b5212d50593cd0c07e641bd, 0000000000000000000000000000000000000000000000000000000337b437fc )
139 ERC1967Proxy.0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58( 0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x000000000000000000000000ab515542d621574f9b5212d50593cd0c07e641bd, 0000000000000000000000000000000000000000000000000000000337b437fc )
140 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 0x000000000000000000000000fee97c6f9bce786a08b1252eac9223057508c760, 00000000000000000000000000000000000000000000000000000000006422c4 )
141 ERC1967Proxy.0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58( 0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x000000000000000000000000fee97c6f9bce786a08b1252eac9223057508c760, 00000000000000000000000000000000000000000000000000000000006422c4 )
142 0x5bdeb2152f185bf59f2de027cbbc05355cc965bd.0xf875488a7062029dc23176e0ab3cafdbe4071d23a093e7dc7d5d9f0208ff7228( 0xf875488a7062029dc23176e0ab3cafdbe4071d23a093e7dc7d5d9f0208ff7228, 0x0000000000000000000000007822159ee394d14745cde63a706f965fb73c7ac8, 0x466565436f6c6c65637465640000000000000000000000000000000000000000, 1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000060, 00000000000000000000000000000000000000000000000000000000000000a0, 000000000000000000000000fee97c6f9bce786a08b1252eac9223057508c760, 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 000000000000000000000000fee97c6f9bce786a08b1252eac9223057508c760, 00000000000000000000000000000000000000000000000000000000006422c4, 0000000000000000000000000000000000000000000000000000000000000025 )
143 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 0x0000000000000000000000007822159ee394d14745cde63a706f965fb73c7ac8, 000000000000000000000000000000000000000000000000000000007f454187 )
144 ERC1967Proxy.0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58( 0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x0000000000000000000000007822159ee394d14745cde63a706f965fb73c7ac8, 000000000000000000000000000000000000000000000000000000007f454187 )
145 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003f37c7d8e61c000085aac0515775b06a3412f36b, 0x0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e, 0000000000000000000000000000000000000000000000000000000054456652 )
146 ERC1967Proxy.0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58( 0x889d364b5102aee044ff370e2e6a836584e5618a16dce4f5b191459cf3903e58, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e, 0000000000000000000000000000000000000000000000000000000054456652 )
147 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000006cae28b3d09d8f8fc74ccd496ac986fc84c0c24e, 0x00000000000000000000000055191784e1e0f77a94a2da1bf98f7d70e7baca4d, 0000000000000000000000000000000000000000000000000000000054456652 )
148 Morpho.Repay( id=4005BA6EB7D2221FE58102BD320AA6D83C47B212771BC950AB71C5074D9AB0EC, caller=0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d, onBehalf=0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d, assets=1413834322, shares=1367195523433735 )
149 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000055191784e1e0f77a94a2da1bf98f7d70e7baca4d, 0x000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb, 0000000000000000000000000000000000000000000000000000000054456652 )
150 0x55191784e1e0f77a94a2da1bf98f7d70e7baca4d.0x01e7ee7e76483485fd1d9e5b1c6a72af05e18dac7fc43f767d6897ef153bef86( 0x01e7ee7e76483485fd1d9e5b1c6a72af05e18dac7fc43f767d6897ef153bef86, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0000000000000000000000000000000000000000000000000000000054456652, 00000000000000000000000000000000000000000000000000000016056fcbb6 )
151 ERC1967Proxy.0xe389f9a675d18cd405aef115ed4e77a5be06982c4202af0a99204b5c61d6565b( 0xe389f9a675d18cd405aef115ed4e77a5be06982c4202af0a99204b5c61d6565b, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0x0000000000000000000000005bdeb2152f185bf59f2de027cbbc05355cc965bd, 0000000000000000000000000000000000000000000000000000000000000002, 0000000000000000000000000000000000000000000000000000000054456652, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
152 FiatTokenProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000ab515542d621574f9b5212d50593cd0c07e641bd, 0x00000000000000000000000087870bca3f3fd6335c3f4ce8392d69350b4fa4e2, 0000000000000000000000000000000000000000000000000000000337b437fc )
153 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0000000000000000000000000000000000000000001dc6537505fa80c07d475d, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000002bf8931b192f7ea58037c0, 000000000000000000000000000000000000000003b5b72af0151b0adc34a970, 000000000000000000000000000000000000000003de99e6577df62e5605aa59 )
154 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000ab515542d621574f9b5212d50593cd0c07e641bd, 0x00000000000000000000000098c23e9d8f34fefb1b7bd6a91b7ff122f4e16f5c, 0000000000000000000000000000000000000000000000000000000337b437fc )
155 InitializableImmutableAdminUpgradeabilityProxy.0xefefaba5e921573100900a3ad9cf29f222d995fb3b6045797eaea7521bd8d6f0( 0xefefaba5e921573100900a3ad9cf29f222d995fb3b6045797eaea7521bd8d6f0, 0x000000000000000000000000ab515542d621574f9b5212d50593cd0c07e641bd, 0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48, 0x0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000ab515542d621574f9b5212d50593cd0c07e641bd, 0000000000000000000000000000000000000000000000000000000337b437fc, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
156 PositionNFT.Transfer( from=0x5bdeb2152f185bf59f2de027cbbc05355cc965bd, to=[Sender] 0x7822159ee394d14745cde63a706f965fb73c7ac8, tokenId=13415595383038600035140745001195761463798945797809123092877826486633240727524 )
157 0x5bdeb2152f185bf59f2de027cbbc05355cc965bd.0xe95aa63b1883254a4494c9e600f7509ec1a5a78dc2f6aef8214b6038d3c45834( 0xe95aa63b1883254a4494c9e600f7509ec1a5a78dc2f6aef8214b6038d3c45834, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x0000000000000000000000007822159ee394d14745cde63a706f965fb73c7ac8 )
158 0x5bdeb2152f185bf59f2de027cbbc05355cc965bd.0xe95aa63b1883254a4494c9e600f7509ec1a5a78dc2f6aef8214b6038d3c45834( 0xe95aa63b1883254a4494c9e600f7509ec1a5a78dc2f6aef8214b6038d3c45834, 0x1da8f53919586c574af918ace37f670808ffffffff0000000151000000000fe4, 0x0000000000000000000000007822159ee394d14745cde63a706f965fb73c7ac8 )

Account State Difference:

  Address   Before After State Difference Code
0x0a9B2C09...0d16cF6f9
0x12208686...B08545ea4
0x2CA5f2C4...c07B89acB
0x47247749...b05482E9F
0x4737D9b4...6654067Ae
(Titan Builder)
12.141512293762100841 Eth12.145611605762100841 Eth0.004099312
0x7822159e...fB73c7Ac8
0.255415348117766065 Eth
Nonce: 5324
0.250961247073733185 Eth
Nonce: 5325
0.00445410104403288
0x7C1156E5...C905aAF37
0x870aC11D...F79Ba00BC
(Morpho: Adaptive Curve Irm)
0x87870Bca...50B4fA4E2
(Aave: Pool V3)
0xA0b86991...E3606eB48
0xBBBBBbbB...C37EEFFCb
(Morpho: Morpho)
0xF244324F...051d632Eb

Execution Trace

ERC1967Proxy.ac9650d8( )
  • Maestro.multicall( data=[92h25R2o9TkZWGxXSvkYrON/ZwgI/////wAAAAFRAAAAAA/kAAAAAAAAAAAAAAAAW96yFS8YW/WfLeAny7wFNVzJZb0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAq1FVQtYhV0+bUhLVBZPNDAfmQb0AAAAAAAAAAAAAAACguGmRxiGLNsHRnUounrDONgbrSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM3tDf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60j//////////////////////////////////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYB2o9TkZWGxXSvkYrON/ZwgI/////wAAAAFRAAAAAA/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADN7Q3/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYB2o9TkZWGxXSvkYrON/ZwgI/////wAAAAFRAAAAAA/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7StSWEGt/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAALKXyxDAEUNUyFLAFRnlcHAe4mssAAAAAAAAAAAAAAACguGmRxiGLNsHRnUounrDONgbrSAAAAAAAAAAAAAAAAIiIiIiIiXWPducQPGy/I6u/WPlGAAAAAAAAAAAAAAAAiIiIiIiJdY925xA8bL8jq79Y+UYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7StSWEGt/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIh6QgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJEfx3iIAAAAAAAAAAAAAAADf6jhvRx0V2LLduTH15XcTl0x4mQAAAAAAAAAAAAAAAEckd0npdsVMbbKp22jFytsFSC6fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO0rUlhBrfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAKC4aZHGIYs2wdGdSi6esM42ButIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6QStYoAAAAAAAAAAAAAAABHN9m0WStA1R4RC5TJwEPGZUBnrgAAAAAAAAAAAAAAANT0gJZdI0fUIfG+x/VFaC5ewhUdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAGExtfrhnqT52WTqwECORAi2Yze1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApE4h/Q6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAbkFB0zAhtSyRwoYIQD20oP+1DsYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAEc32bRZK0DVHhELlMnAQ8ZlQGeuAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAAAAAAAAAAAAAACIiIiIiIl1j3bnEDxsvyOrv1j5RgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDZDOSRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAA8kQyT7tX8J8GBv8Ii8iUsFHWMusAAAAAAAAAAAAAAABHN9m0WStA1R4RC5TJwEPGZUBnrgAAAAAAAAAAAAAAAHwRVuUVqhouhRZ0EgB0loyQWq83AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbUnKRva/7JHGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA2QzkkQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAABIghoZy1bEPPhy5q1GeTQsIVF6kAAAAAAAAAAAAAAAAfBFW5RWqGi6FFnQSAHSWjJBarzcAAAAAAAAAAAAAAACguGmRxiGLNsHRnUounrDONgbrSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8GnpbwEN1PxTQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAARToAAAAAAAAAAAAAAAQgWXhqAAAAAAAAAAAAAAAARzfZtFkrQNUeEQuUycBDxmVAZ64AAAAAAAAAAAAAAACguGmRxiGLNsHRnUounrDONgbrSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAiIiIiIiJdY925xA8bL8jq79Y+UYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA21Jykb2v+yRxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO2tuxfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAbkFB0zAhtSyRwoYIQD20oP+1DsYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAADbUnKRva/7JHGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn17IlNvdXJjZSI6IlBlbmRsZSIsIkFtb3VudEluVVNEIjoiMTc3OTguMzAyMDk1NTgyNDMiLCJBbW91bnRPdXRVU0QiOiIxNzczMi4yMDg3NzMzODM5NDciLCJSZWZlcnJhbCI6IiIsIkZsYWdzIjowLCJBbW91bnRPdXQiOiIxNzcyMjYwMzYyNiIsIlRpbWVzdGFtcCI6MTc2MDI2ODgxNSwiUm91dGVJRCI6IjNkZTVjN2JkLTA5YzktNDZlMS04MWQyLWY0YzRiNGRmNzk2ZTpkZDRjZTZiMS1lZmM3LTQ2NTYtOTYyYy04NDM3NzI1ZWJhYzIiLCJJbnRlZ3JpdHlJbmZvIjp7IktleUlEIjoiMSIsIlNpZ25hdHVyZSI6IkFYcTN0NzFoM1F6MzRkMVBLaEtUSXB6ZW5Xemt1c0VKRm84bDVWMTh5S0JqK3ZRZ0t4bWxCNG02ek9tQnVCM2NOYXdGNTdJYzFOT3NteXBOamtNSmplbDBhMFNHL0RaZ09uSE9SV0dRWU53QlZqdmU2T1k2RytwZ1JiMzEzUlUzaVQxbU1xL0RXTmhTbVl6WGc1cE1IYUJYbkRuNHNrNVhiaXdOcndIQ1llc2s2b05sT01uY1dmNzBhOUhtdzVQTlExaEpTMFVwMXNTQzlKTjFvQWcwbHpkWERJbWRGZ2JsWFZDTExRMWlsWFVpK1UwRXVGVU0yTHFwNXByaTVsS2ZiVXp3VE5FZHZQS3FGMDF2bG44ZExTMHh2N1FlcDV2OHZWRVQ4c3Q3OW5Pa2NYc2RMa2JVaXNDemtNeVNtQUdYektGZ2NWYWJaV3AyRlhkV2tXR3RZZz09In19AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAACguGmRxiGLNsHRnUounrDONgbrSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM3tDf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAoLhpkcYhizbB0Z1KLp6wzjYG60gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQixAAAAAAAAAAAAAAAAP7pfG+bznhqCLElLqySIwV1CMdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEARmVlQ29sbGVjdGVkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAA/ul8b5vOeGoIsSUurJIjBXUIx2AAAAAAAAAAAAAAAACguGmRxiGLNsHRnUounrDONgbrSAAAAAAAAAAAAAAAAP7pfG+bznhqCLElLqySIwV1CMdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkIsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAKC4aZHGIYs2wdGdSi6esM42ButIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9FQYcAAAAAAAAAAAAAAAB4IhWe45TRR0XN5jpwb5Zftzx6yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYB2o9TkZWGxXSvkYrON/ZwgI/////wAAAAFRAAAAAA/kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//////////////////////////////////////////g==] ) => ( results=[] )
    • ERC1967Proxy.f76876e5( )
      • Maestro.transferPosition( positionId=1DA8F53919586C574AF918ACE37F670808FFFFFFFF0000000151000000000FE4, to=0x5BDeB2152f185BF59f2dE027CBBC05355cc965Bd, data=0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002A00000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000011E00000000000000000000000000000000000000000000000000000000000001280000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000014A00000000000000000000000000000000000000000000000000000000000001560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000AB515542D621574F9B5212D50593CD0C07E641BD000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB480000000000000000000000000000000000000000000000000000000337B437FC000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000601DA8F53919586C574AF918ACE37F670808FFFFFFFF0000000151000000000FE400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000337B437FC0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000601DA8F53919586C574AF918ACE37F670808FFFFFFFF0000000151000000000FE400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000003B4AD496106B7F00000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000D6000000000000000000000000000000000000000000000000000000000000000600000000000000000000000002CA5F2C4300450D53214B00546795C1C07B89ACB000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000888888888889758F76E7103C6CBF23ABBF58F946000000000000000000000000888888888889758F76E7103C6CBF23ABBF58F9460000000000000000000000000000000000000000000003B4AD496106B7F00000000000000000000000000000000000000000000000000000000000040887A42000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000C2447F1DE22000000000000000000000000DFEA386F471D15D8B2DDB931F5E57713974C789900000000000000000000000047247749E976C54C6DB2A9DB68C5CADB05482E9F0000000000000000000000000000000000000000000003B4AD496106B7F000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB4800000000000000000000000000000000000000000000000000000003A412B58A0000000000000000000000004737D9B4592B40D51E110B94C9C043C6654067AE000000000000000000000000D4F480965D2347D421F1BEC7F545682E5EC2151D00000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131B5FAE19EA4F9D964EAC0408E4408B66337B5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000A44E21FD0E900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006E4141D33021B52C91C28608403DB4A0FFB50EC6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000054000000000000000000000000000000000000000000000000000000000000007800000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000004737D9B4592B40D51E110B94C9C043C6654067AE000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000888888888889758F76E7103C6CBF23ABBF58F946000000000000000000000000000000000000000000000000000000007FFFFFFF0000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001A00000000000000000000000000000000000000000000000000000000000000040D90CE491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000F244324FBB57F09F0606FF088BC894B051D632EB0000000000000000000000004737D9B4592B40D51E110B94C9C043C6654067AE0000000000000000000000007C1156E515AA1A2E851674120074968C905AAF370000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036D49CA46F6BFEC91C6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040D90CE4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000001220868672D5B10F3E1CB9AB519E4D0B08545EA40000000000000000000000007C1156E515AA1A2E851674120074968C905AAF37000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C1A7A5BC043753F14D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000453A0000000000000000000000042059786A0000000000000000000000004737D9B4592B40D51E110B94C9C043C6654067AE000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001A000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758F76E7103C6CBF23ABBF58F94600000000000000000000000000000000000000000000036D49CA46F6BFEC91C600000000000000000000000000000000000000000000000000000003B6B6EC5F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006E4141D33021B52C91C28608403DB4A0FFB50EC6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000036D49CA46F6BFEC91C6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027D7B22536F75726365223A2250656E646C65222C22416D6F756E74496E555344223A2231373739382E3330323039353538323433222C22416D6F756E744F7574555344223A2231373733322E323038373733333833393437222C22526566657272616C223A22222C22466C616773223A302C22416D6F756E744F7574223A223137373232363033363236222C2254696D657374616D70223A313736303236383831352C22526F7574654944223A2233646535633762642D303963392D343665312D383164322D6634633462346466373936653A64643463653662312D656663372D343635362D393632632D383433373732356562616332222C22496E74656772697479496E666F223A7B224B65794944223A2231222C225369676E6174757265223A22415871337437316833517A33346431504B684B5449707A656E577A6B7573454A466F386C35563138794B426A2B7651674B786D6C42346D367A4F6D42754233634E61774635374963314E4F736D79704E6A6B4D4A6A656C30613053472F445A674F6E484F52574751594E7742566A7665364F5936472B706752623331335255336954316D4D712F44574E68536D597A586735704D486142586E446E34736B35586269774E727748435965736B366F4E6C4F4D6E63576637306139486D7735504E5131684A5330557031735343394A4E316F4167306C7A645844496D644667626C5856434C4C5131696C5855692B5530457546554D324C717035707269356C4B6662557A77544E456476504B71463031766C6E38644C53307876375165703576387656455438737437396E4F6B635873644C6B62556973437A6B4D79536D4147587A4B4667635661625A577032465864576B57477459673D3D227D7D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB480000000000000000000000000000000000000000000000000000000337B437FC000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB4800000000000000000000000000000000000000000000000000000000006422C4000000000000000000000000FEE97C6F9BCE786A08B1252EAC9223057508C760000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100466565436F6C6C65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000FEE97C6F9BCE786A08B1252EAC9223057508C760000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000FEE97C6F9BCE786A08B1252EAC9223057508C76000000000000000000000000000000000000000000000000000000000006422C40000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000007F4541870000000000000000000000007822159EE394D14745CDE63A706F965FB73C7AC80000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000601DA8F53919586C574AF918ACE37F670808FFFFFFFF0000000151000000000FE40000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE )
        • PositionNFT.safeTransferFrom( from=0x7822159ee394D14745Cde63a706F965fB73c7Ac8, to=0x5BDeB2152f185BF59f2dE027CBBC05355cc965Bd, tokenId=13415595383038600035140745001195761463798945797809123092877826486633240727524, data=0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002A00000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000011E00000000000000000000000000000000000000000000000000000000000001280000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000014A00000000000000000000000000000000000000000000000000000000000001560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000AB515542D621574F9B5212D50593CD0C07E641BD000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB480000000000000000000000000000000000000000000000000000000337B437FC000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000601DA8F53919586C574AF918ACE37F670808FFFFFFFF0000000151000000000FE400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000337B437FC0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000601DA8F53919586C574AF918ACE37F670808FFFFFFFF0000000151000000000FE400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000003B4AD496106B7F00000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000D6000000000000000000000000000000000000000000000000000000000000000600000000000000000000000002CA5F2C4300450D53214B00546795C1C07B89ACB000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000888888888889758F76E7103C6CBF23ABBF58F946000000000000000000000000888888888889758F76E7103C6CBF23ABBF58F9460000000000000000000000000000000000000000000003B4AD496106B7F00000000000000000000000000000000000000000000000000000000000040887A42000000000000000000000000000000000000000000000000000000000000000A00000000000000000000000000000000000000000000000000000000000000C2447F1DE22000000000000000000000000DFEA386F471D15D8B2DDB931F5E57713974C789900000000000000000000000047247749E976C54C6DB2A9DB68C5CADB05482E9F0000000000000000000000000000000000000000000003B4AD496106B7F000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB4800000000000000000000000000000000000000000000000000000003A412B58A0000000000000000000000004737D9B4592B40D51E110B94C9C043C6654067AE000000000000000000000000D4F480965D2347D421F1BEC7F545682E5EC2151D00000000000000000000000000000000000000000000000000000000000000A000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006131B5FAE19EA4F9D964EAC0408E4408B66337B5000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000A44E21FD0E900000000000000000000000000000000000000000000000000000000000000200000000000000000000000006E4141D33021B52C91C28608403DB4A0FFB50EC6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000000000000000000000000000000000000000054000000000000000000000000000000000000000000000000000000000000007800000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000C00000000000000000000000004737D9B4592B40D51E110B94C9C043C6654067AE000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000888888888889758F76E7103C6CBF23ABBF58F946000000000000000000000000000000000000000000000000000000007FFFFFFF0000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001A00000000000000000000000000000000000000000000000000000000000000040D90CE491000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100000000000000000000000000F244324FBB57F09F0606FF088BC894B051D632EB0000000000000000000000004737D9B4592B40D51E110B94C9C043C6654067AE0000000000000000000000007C1156E515AA1A2E851674120074968C905AAF370000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000036D49CA46F6BFEC91C6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040D90CE4910000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000001000000000000000000000000001220868672D5B10F3E1CB9AB519E4D0B08545EA40000000000000000000000007C1156E515AA1A2E851674120074968C905AAF37000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003C1A7A5BC043753F14D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000453A0000000000000000000000042059786A0000000000000000000000004737D9B4592B40D51E110B94C9C043C6654067AE000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001A000000000000000000000000000000000000000000000000000000000000001E00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000888888888889758F76E7103C6CBF23ABBF58F94600000000000000000000000000000000000000000000036D49CA46F6BFEC91C600000000000000000000000000000000000000000000000000000003B6B6EC5F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000010000000000000000000000006E4141D33021B52C91C28608403DB4A0FFB50EC6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000036D49CA46F6BFEC91C6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027D7B22536F75726365223A2250656E646C65222C22416D6F756E74496E555344223A2231373739382E3330323039353538323433222C22416D6F756E744F7574555344223A2231373733322E323038373733333833393437222C22526566657272616C223A22222C22466C616773223A302C22416D6F756E744F7574223A223137373232363033363236222C2254696D657374616D70223A313736303236383831352C22526F7574654944223A2233646535633762642D303963392D343665312D383164322D6634633462346466373936653A64643463653662312D656663372D343635362D393632632D383433373732356562616332222C22496E74656772697479496E666F223A7B224B65794944223A2231222C225369676E6174757265223A22415871337437316833517A33346431504B684B5449707A656E577A6B7573454A466F386C35563138794B426A2B7651674B786D6C42346D367A4F6D42754233634E61774635374963314E4F736D79704E6A6B4D4A6A656C30613053472F445A674F6E484F52574751594E7742566A7665364F5936472B706752623331335255336954316D4D712F44574E68536D597A586735704D486142586E446E34736B35586269774E727748435965736B366F4E6C4F4D6E63576637306139486D7735504E5131684A5330557031735343394A4E316F4167306C7A645844496D644667626C5856434C4C5131696C5855692B5530457546554D324C717035707269356C4B6662557A77544E456476504B71463031766C6E38644C53307876375165703576387656455438737437396E4F6B635873644C6B62556973437A6B4D79536D4147587A4B4667635661625A577032465864576B57477459673D3D227D7D0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB480000000000000000000000000000000000000000000000000000000337B437FC000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB4800000000000000000000000000000000000000000000000000000000006422C4000000000000000000000000FEE97C6F9BCE786A08B1252EAC9223057508C760000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100466565436F6C6C65637465640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000FEE97C6F9BCE786A08B1252EAC9223057508C760000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000FEE97C6F9BCE786A08B1252EAC9223057508C76000000000000000000000000000000000000000000000000000000000006422C40000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000A0B86991C6218B36C1D19D4A2E9EB0CE3606EB48000000000000000000000000000000000000000000000000000000007F4541870000000000000000000000007822159EE394D14745CDE63A706F965FB73C7AC80000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000601DA8F53919586C574AF918ACE37F670808FFFFFFFF0000000151000000000FE40000000000000000000000000000000000000000000000000000000000000001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE )
          • 0x5bdeb2152f185bf59f2de027cbbc05355cc965bd.150b7a02( )
            • 0xb2da5c7bb828d4bcec8e5d5c0e06f42962ed2d1e.150b7a02( )
              File 1 of 16: ERC1967Proxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./ERC1967Upgrade.sol";
              /**
               * @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 initializing the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
              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 internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      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 overridden 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 internal 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 overridden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../interfaces/IERC1967.sol";
              import "../../interfaces/draft-IERC1822.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @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 is IERC1967 {
                  // 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 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 {
                      _upgradeTo(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 _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @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 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 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 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);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               *
               * _Available since v4.8.3._
               */
              interface IERC1967 {
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Emitted when the beacon is changed.
                   */
                  event BeaconUpgraded(address indexed beacon);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822Proxiable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @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:
               * ```solidity
               * 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`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              

              File 2 of 16: PositionNFT
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.20;
              import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
              import "@openzeppelin/contracts/access/AccessControl.sol";
              import "../libraries/DataTypes.sol";
              import "../libraries/Errors.sol";
              import "../libraries/Roles.sol";
              /// @title PositionNFT
              /// @notice An ERC721 NFT that represents ownership of each position created through the protocol
              /// @dev Instances can only be minted by other contango contracts
              contract PositionNFT is ERC721, AccessControl {
                  event ContangoContractSet(address indexed contractAddr, bool indexed enabled);
                  uint256 public counter = 1;
                  mapping(address contractAddr => bool enabled) public contangoContracts;
                  constructor(Timelock timelock) ERC721("Contango Position", "CTGP") {
                      // Grant the admin role to the timelock by default
                      _grantRole(DEFAULT_ADMIN_ROLE, Timelock.unwrap(timelock));
                  }
                  /// @notice creates a new position in the protocol by minting a new NFT instance
                  /// @param positionId positionId of the new position without the number component set
                  /// @param to The would be owner of the newly minted position
                  /// @return positionId_ The newly created positionId
                  function mint(PositionId positionId, address to) external onlyRole(MINTER_ROLE) returns (PositionId positionId_) {
                      positionId_ = positionId.withNumber(counter++);
                      _safeMint(to, uint256(PositionId.unwrap(positionId_)));
                  }
                  /// @notice closes a position in the protocol by burning the NFT instance
                  /// @param positionId positionId of the closed position
                  function burn(PositionId positionId) external onlyRole(MINTER_ROLE) {
                      _burn(uint256(PositionId.unwrap(positionId)));
                  }
                  function positionOwner(PositionId positionId) public view returns (address) {
                      return ownerOf(uint256(PositionId.unwrap(positionId)));
                  }
                  function exists(PositionId positionId) external view returns (bool) {
                      return _exists(uint256(PositionId.unwrap(positionId)));
                  }
                  function setContangoContract(address contractAddr, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
                      contangoContracts[contractAddr] = enabled;
                      emit ContangoContractSet(contractAddr, enabled);
                  }
                  function isApprovedForAll(address owner, address operator) public view override returns (bool) {
                      return owner == operator || contangoContracts[operator] || super.isApprovedForAll(owner, operator);
                  }
                  /// @inheritdoc ERC721
                  function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControl, ERC721) returns (bool) {
                      return super.supportsInterface(interfaceId) || AccessControl.supportsInterface(interfaceId);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
              pragma solidity ^0.8.0;
              import "./IERC721.sol";
              import "./IERC721Receiver.sol";
              import "./extensions/IERC721Metadata.sol";
              import "../../utils/Address.sol";
              import "../../utils/Context.sol";
              import "../../utils/Strings.sol";
              import "../../utils/introspection/ERC165.sol";
              /**
               * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
               * the Metadata extension, but not including the Enumerable extension, which is available separately as
               * {ERC721Enumerable}.
               */
              contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
                  using Address for address;
                  using Strings for uint256;
                  // Token name
                  string private _name;
                  // Token symbol
                  string private _symbol;
                  // Mapping from token ID to owner address
                  mapping(uint256 => address) private _owners;
                  // Mapping owner address to token count
                  mapping(address => uint256) private _balances;
                  // Mapping from token ID to approved address
                  mapping(uint256 => address) private _tokenApprovals;
                  // Mapping from owner to operator approvals
                  mapping(address => mapping(address => bool)) private _operatorApprovals;
                  /**
                   * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                      return
                          interfaceId == type(IERC721).interfaceId ||
                          interfaceId == type(IERC721Metadata).interfaceId ||
                          super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev See {IERC721-balanceOf}.
                   */
                  function balanceOf(address owner) public view virtual override returns (uint256) {
                      require(owner != address(0), "ERC721: address zero is not a valid owner");
                      return _balances[owner];
                  }
                  /**
                   * @dev See {IERC721-ownerOf}.
                   */
                  function ownerOf(uint256 tokenId) public view virtual override returns (address) {
                      address owner = _ownerOf(tokenId);
                      require(owner != address(0), "ERC721: invalid token ID");
                      return owner;
                  }
                  /**
                   * @dev See {IERC721Metadata-name}.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev See {IERC721Metadata-symbol}.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev See {IERC721Metadata-tokenURI}.
                   */
                  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                      _requireMinted(tokenId);
                      string memory baseURI = _baseURI();
                      return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
                  }
                  /**
                   * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                   * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                   * by default, can be overridden in child contracts.
                   */
                  function _baseURI() internal view virtual returns (string memory) {
                      return "";
                  }
                  /**
                   * @dev See {IERC721-approve}.
                   */
                  function approve(address to, uint256 tokenId) public virtual override {
                      address owner = ERC721.ownerOf(tokenId);
                      require(to != owner, "ERC721: approval to current owner");
                      require(
                          _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                          "ERC721: approve caller is not token owner or approved for all"
                      );
                      _approve(to, tokenId);
                  }
                  /**
                   * @dev See {IERC721-getApproved}.
                   */
                  function getApproved(uint256 tokenId) public view virtual override returns (address) {
                      _requireMinted(tokenId);
                      return _tokenApprovals[tokenId];
                  }
                  /**
                   * @dev See {IERC721-setApprovalForAll}.
                   */
                  function setApprovalForAll(address operator, bool approved) public virtual override {
                      _setApprovalForAll(_msgSender(), operator, approved);
                  }
                  /**
                   * @dev See {IERC721-isApprovedForAll}.
                   */
                  function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
                  /**
                   * @dev See {IERC721-transferFrom}.
                   */
                  function transferFrom(address from, address to, uint256 tokenId) public virtual override {
                      //solhint-disable-next-line max-line-length
                      require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                      _transfer(from, to, tokenId);
                  }
                  /**
                   * @dev See {IERC721-safeTransferFrom}.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
                      safeTransferFrom(from, to, tokenId, "");
                  }
                  /**
                   * @dev See {IERC721-safeTransferFrom}.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
                      require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                      _safeTransfer(from, to, tokenId, data);
                  }
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * `data` is additional data, it has no specified format and it is sent in call to `to`.
                   *
                   * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
                   * implement alternative mechanisms to perform token transfer, such as signature-based.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
                      _transfer(from, to, tokenId);
                      require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
                  }
                  /**
                   * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
                   */
                  function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
                      return _owners[tokenId];
                  }
                  /**
                   * @dev Returns whether `tokenId` exists.
                   *
                   * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
                   *
                   * Tokens start existing when they are minted (`_mint`),
                   * and stop existing when they are burned (`_burn`).
                   */
                  function _exists(uint256 tokenId) internal view virtual returns (bool) {
                      return _ownerOf(tokenId) != address(0);
                  }
                  /**
                   * @dev Returns whether `spender` is allowed to manage `tokenId`.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
                      address owner = ERC721.ownerOf(tokenId);
                      return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
                  }
                  /**
                   * @dev Safely mints `tokenId` and transfers it to `to`.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must not exist.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _safeMint(address to, uint256 tokenId) internal virtual {
                      _safeMint(to, tokenId, "");
                  }
                  /**
                   * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
                   * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
                   */
                  function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
                      _mint(to, tokenId);
                      require(
                          _checkOnERC721Received(address(0), to, tokenId, data),
                          "ERC721: transfer to non ERC721Receiver implementer"
                      );
                  }
                  /**
                   * @dev Mints `tokenId` and transfers it to `to`.
                   *
                   * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
                   *
                   * Requirements:
                   *
                   * - `tokenId` must not exist.
                   * - `to` cannot be the zero address.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _mint(address to, uint256 tokenId) internal virtual {
                      require(to != address(0), "ERC721: mint to the zero address");
                      require(!_exists(tokenId), "ERC721: token already minted");
                      _beforeTokenTransfer(address(0), to, tokenId, 1);
                      // Check that tokenId was not minted by `_beforeTokenTransfer` hook
                      require(!_exists(tokenId), "ERC721: token already minted");
                      unchecked {
                          // Will not overflow unless all 2**256 token ids are minted to the same owner.
                          // Given that tokens are minted one by one, it is impossible in practice that
                          // this ever happens. Might change if we allow batch minting.
                          // The ERC fails to describe this case.
                          _balances[to] += 1;
                      }
                      _owners[tokenId] = to;
                      emit Transfer(address(0), to, tokenId);
                      _afterTokenTransfer(address(0), to, tokenId, 1);
                  }
                  /**
                   * @dev Destroys `tokenId`.
                   * The approval is cleared when the token is burned.
                   * This is an internal function that does not check if the sender is authorized to operate on the token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _burn(uint256 tokenId) internal virtual {
                      address owner = ERC721.ownerOf(tokenId);
                      _beforeTokenTransfer(owner, address(0), tokenId, 1);
                      // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
                      owner = ERC721.ownerOf(tokenId);
                      // Clear approvals
                      delete _tokenApprovals[tokenId];
                      unchecked {
                          // Cannot overflow, as that would require more tokens to be burned/transferred
                          // out than the owner initially received through minting and transferring in.
                          _balances[owner] -= 1;
                      }
                      delete _owners[tokenId];
                      emit Transfer(owner, address(0), tokenId);
                      _afterTokenTransfer(owner, address(0), tokenId, 1);
                  }
                  /**
                   * @dev Transfers `tokenId` from `from` to `to`.
                   *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _transfer(address from, address to, uint256 tokenId) internal virtual {
                      require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                      require(to != address(0), "ERC721: transfer to the zero address");
                      _beforeTokenTransfer(from, to, tokenId, 1);
                      // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
                      require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                      // Clear approvals from the previous owner
                      delete _tokenApprovals[tokenId];
                      unchecked {
                          // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
                          // `from`'s balance is the number of token held, which is at least one before the current
                          // transfer.
                          // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
                          // all 2**256 token ids to be minted, which in practice is impossible.
                          _balances[from] -= 1;
                          _balances[to] += 1;
                      }
                      _owners[tokenId] = to;
                      emit Transfer(from, to, tokenId);
                      _afterTokenTransfer(from, to, tokenId, 1);
                  }
                  /**
                   * @dev Approve `to` to operate on `tokenId`
                   *
                   * Emits an {Approval} event.
                   */
                  function _approve(address to, uint256 tokenId) internal virtual {
                      _tokenApprovals[tokenId] = to;
                      emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
                  }
                  /**
                   * @dev Approve `operator` to operate on all of `owner` tokens
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
                      require(owner != operator, "ERC721: approve to caller");
                      _operatorApprovals[owner][operator] = approved;
                      emit ApprovalForAll(owner, operator, approved);
                  }
                  /**
                   * @dev Reverts if the `tokenId` has not been minted yet.
                   */
                  function _requireMinted(uint256 tokenId) internal view virtual {
                      require(_exists(tokenId), "ERC721: invalid token ID");
                  }
                  /**
                   * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
                   * The call is not executed if the target address is not a contract.
                   *
                   * @param from address representing the previous owner of the given token ID
                   * @param to target address that will receive the tokens
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param data bytes optional data to send along with the call
                   * @return bool whether the call correctly returned the expected magic value
                   */
                  function _checkOnERC721Received(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory data
                  ) private returns (bool) {
                      if (to.isContract()) {
                          try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                              return retval == IERC721Receiver.onERC721Received.selector;
                          } catch (bytes memory reason) {
                              if (reason.length == 0) {
                                  revert("ERC721: transfer to non ERC721Receiver implementer");
                              } else {
                                  /// @solidity memory-safe-assembly
                                  assembly {
                                      revert(add(32, reason), mload(reason))
                                  }
                              }
                          }
                      } else {
                          return true;
                      }
                  }
                  /**
                   * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
                   * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
                   * - When `from` is zero, the tokens will be minted for `to`.
                   * - When `to` is zero, ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   * - `batchSize` is non-zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
                  /**
                   * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
                   * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
                   * - When `from` is zero, the tokens were minted for `to`.
                   * - When `to` is zero, ``from``'s tokens were burned.
                   * - `from` and `to` are never both zero.
                   * - `batchSize` is non-zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
                  /**
                   * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
                   *
                   * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
                   * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
                   * that `ownerOf(tokenId)` is `a`.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function __unsafe_increaseBalance(address account, uint256 amount) internal {
                      _balances[account] += amount;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControl.sol";
              import "../utils/Context.sol";
              import "../utils/Strings.sol";
              import "../utils/introspection/ERC165.sol";
              /**
               * @dev Contract module that allows children to implement role-based access
               * control mechanisms. This is a lightweight version that doesn't allow enumerating role
               * members except through off-chain means by accessing the contract event logs. Some
               * applications may benefit from on-chain enumerability, for those cases see
               * {AccessControlEnumerable}.
               *
               * Roles are referred to by their `bytes32` identifier. These should be exposed
               * in the external API and be unique. The best way to achieve this is by
               * using `public constant` hash digests:
               *
               * ```solidity
               * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
               * ```
               *
               * Roles can be used to represent a set of permissions. To restrict access to a
               * function call, use {hasRole}:
               *
               * ```solidity
               * function foo() public {
               *     require(hasRole(MY_ROLE, msg.sender));
               *     ...
               * }
               * ```
               *
               * Roles can be granted and revoked dynamically via the {grantRole} and
               * {revokeRole} functions. Each role has an associated admin role, and only
               * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
               *
               * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
               * that only accounts with this role will be able to grant or revoke other
               * roles. More complex role relationships can be created by using
               * {_setRoleAdmin}.
               *
               * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
               * grant and revoke this role. Extra precautions should be taken to secure
               * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
               * to enforce additional security measures for this role.
               */
              abstract contract AccessControl is Context, IAccessControl, ERC165 {
                  struct RoleData {
                      mapping(address => bool) members;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with a standardized message including the required role.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   *
                   * _Available since v4.1._
                   */
                  modifier onlyRole(bytes32 role) {
                      _checkRole(role);
                      _;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                      return _roles[role].members[account];
                  }
                  /**
                   * @dev Revert with a standard message if `_msgSender()` is missing `role`.
                   * Overriding this function changes the behavior of the {onlyRole} modifier.
                   *
                   * Format of the revert message is described in {_checkRole}.
                   *
                   * _Available since v4.6._
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Revert with a standard message if `account` is missing `role`.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "AccessControl: account ",
                                      Strings.toHexString(account),
                                      " is missing role ",
                                      Strings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                      return _roles[role].adminRole;
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been revoked `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address account) public virtual override {
                      require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event. Note that unlike {grantRole}, this function doesn't perform any
                   * checks on the calling account.
                   *
                   * May emit a {RoleGranted} event.
                   *
                   * [WARNING]
                   * ====
                   * This function should only be called from the constructor when setting
                   * up the initial roles for the system.
                   *
                   * Using this function in any other way is effectively circumventing the admin
                   * system imposed by {AccessControl}.
                   * ====
                   *
                   * NOTE: This function is deprecated in favor of {_grantRole}.
                   */
                  function _setupRole(bytes32 role, address account) internal virtual {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Sets `adminRole` as ``role``'s admin role.
                   *
                   * Emits a {RoleAdminChanged} event.
                   */
                  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                      bytes32 previousAdminRole = getRoleAdmin(role);
                      _roles[role].adminRole = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual {
                      if (!hasRole(role, account)) {
                          _roles[role].members[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                      }
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      if (hasRole(role, account)) {
                          _roles[role].members[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                      }
                  }
              }
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.20;
              import "./extensions/PositionIdExt.sol";
              uint256 constant WAD = 1e18;
              uint256 constant RAY = 1e27;
              uint256 constant PERCENTAGE_UNIT = 1e4;
              uint256 constant ONE_HUNDRED_PERCENT = 1e4;
              enum Currency {
                  None,
                  Base,
                  Quote
              }
              type Symbol is bytes16;
              type PositionId is bytes32;
              using { decode, getSymbol, getNumber, getMoneyMarket, getExpiry, isPerp, isExpired, withNumber, getFlags } for PositionId global;
              type OrderId is bytes32;
              type MoneyMarketId is uint8;
              type Timelock is address;
              //SPDX-License-Identifier: MIT
              pragma solidity 0.8.20;
              error SenderIsNotNativeToken(address msgSender, address nativeToken);
              error Unauthorised(address msgSender);
              //SPDX-License-Identifier: MIT
              pragma solidity 0.8.20;
              bytes32 constant EMERGENCY_BREAK_ROLE = keccak256("EMERGENCY_BREAK");
              bytes32 constant OPERATOR_ROLE = keccak256("OPERATOR");
              bytes32 constant CONTANGO_ROLE = keccak256("CONTANGO");
              bytes32 constant BOT_ROLE = keccak256("BOT");
              bytes32 constant MINTER_ROLE = keccak256("MINTER");
              bytes32 constant MODIFIER_ROLE = keccak256("MODIFIER");
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              import "../../utils/introspection/IERC165.sol";
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721 is IERC165 {
                  /**
                   * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                   */
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                   */
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                  /**
                   * @dev Returns the number of tokens in ``owner``'s account.
                   */
                  function balanceOf(address owner) external view returns (uint256 balance);
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) external view returns (address owner);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) external;
                  /**
                   * @dev Transfers `tokenId` token from `from` to `to`.
                   *
                   * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                   * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                   * understand this adds an external call which potentially creates a reentrancy vulnerability.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 tokenId) external;
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) external;
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool approved) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address operator);
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
              pragma solidity ^0.8.0;
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              interface IERC721Receiver {
                  /**
                   * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                   * by `operator` from `from`, this function is called.
                   *
                   * It must return its Solidity selector to confirm the token transfer.
                   * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                   *
                   * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
                   */
                  function onERC721Received(
                      address operator,
                      address from,
                      uint256 tokenId,
                      bytes calldata data
                  ) external returns (bytes4);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC721.sol";
              /**
               * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
               * @dev See https://eips.ethereum.org/EIPS/eip-721
               */
              interface IERC721Metadata is IERC721 {
                  /**
                   * @dev Returns the token collection name.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the token collection symbol.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                   */
                  function tokenURI(uint256 tokenId) external view returns (string memory);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              import "./math/Math.sol";
              import "./math/SignedMath.sol";
              /**
               * @dev String operations.
               */
              library Strings {
                  bytes16 private constant _SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          uint256 length = Math.log10(value) + 1;
                          string memory buffer = new string(length);
                          uint256 ptr;
                          /// @solidity memory-safe-assembly
                          assembly {
                              ptr := add(buffer, add(32, length))
                          }
                          while (true) {
                              ptr--;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                              }
                              value /= 10;
                              if (value == 0) break;
                          }
                          return buffer;
                      }
                  }
                  /**
                   * @dev Converts a `int256` to its ASCII `string` decimal representation.
                   */
                  function toString(int256 value) internal pure returns (string memory) {
                      return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          return toHexString(value, Math.log256(value) + 1);
                      }
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                   */
                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                      bytes memory buffer = new bytes(2 * length + 2);
                      buffer[0] = "0";
                      buffer[1] = "x";
                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                          buffer[i] = _SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
                  /**
                   * @dev Returns true if the two strings are equal.
                   */
                  function equal(string memory a, string memory b) internal pure returns (bool) {
                      return keccak256(bytes(a)) == keccak256(bytes(b));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev External interface of AccessControl declared to support ERC165 detection.
               */
              interface IAccessControl {
                  /**
                   * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                   *
                   * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                   * {RoleAdminChanged} not being emitted signaling this.
                   *
                   * _Available since v3.1._
                   */
                  event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                  /**
                   * @dev Emitted when `account` is granted `role`.
                   *
                   * `sender` is the account that originated the contract call, an admin role
                   * bearer except when using {AccessControl-_setupRole}.
                   */
                  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Emitted when `account` is revoked `role`.
                   *
                   * `sender` is the account that originated the contract call:
                   *   - if using `revokeRole`, it is the admin role bearer
                   *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                   */
                  event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) external view returns (bool);
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) external view returns (bytes32);
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function grantRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function revokeRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been granted `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   */
                  function renounceRole(bytes32 role, address account) external;
              }
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.20;
              import "../DataTypes.sol";
              error InvalidUInt48(uint256 n);
              error InvalidUInt32(uint256 n);
              error InvalidExpiry();
              error InvalidPositionId();
              //  16B   -      1B      -   4B   -  1B   -  4B   -  6B
              // symbol - money market - expiry - flags - empty - number
              function decode(PositionId positionId) pure returns (Symbol symbol, MoneyMarketId mm, uint32 expiry, uint256 number) {
                  bytes32 raw = PositionId.unwrap(positionId);
                  symbol = Symbol.wrap(bytes16(raw));
                  mm = MoneyMarketId.wrap(uint8(uint256(raw >> 120)));
                  expiry = (uint32(uint256(raw >> 88)));
                  number = uint48(uint256(raw));
              }
              function getSymbol(PositionId positionId) pure returns (Symbol) {
                  return Symbol.wrap(bytes16(PositionId.unwrap(positionId)));
              }
              function getNumber(PositionId positionId) pure returns (uint256) {
                  return uint48(uint256(PositionId.unwrap(positionId)));
              }
              function getMoneyMarket(PositionId positionId) pure returns (MoneyMarketId) {
                  return MoneyMarketId.wrap(uint8(uint256(PositionId.unwrap(positionId) >> 120)));
              }
              function getExpiry(PositionId positionId) pure returns (uint32) {
                  return (uint32(uint256(PositionId.unwrap(positionId) >> 88)));
              }
              function isPerp(PositionId positionId) pure returns (bool) {
                  return getExpiry(positionId) == type(uint32).max;
              }
              function isExpired(PositionId positionId) view returns (bool) {
                  return block.timestamp >= getExpiry(positionId);
              }
              function withNumber(PositionId positionId, uint256 number) pure returns (PositionId) {
                  if (uint48(number) != number) revert InvalidUInt48(number);
                  if (getNumber(positionId) != 0) revert InvalidPositionId();
                  return PositionId.wrap(bytes32(uint256(PositionId.unwrap(positionId)) + number));
              }
              function getFlags(PositionId positionId) pure returns (bytes1) {
                  return bytes1(PositionId.unwrap(positionId) << 168);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[EIP].
               *
               * Implementers can declare support of contract interfaces, which can then be
               * queried by others ({ERC165Checker}).
               *
               * For an implementation, see {ERC165}.
               */
              interface IERC165 {
                  /**
                   * @dev Returns true if this contract implements the interface defined by
                   * `interfaceId`. See the corresponding
                   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                   * to learn more about how these ids are created.
                   *
                   * This function call must use less than 30 000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  enum Rounding {
                      Down, // Toward negative infinity
                      Up, // Toward infinity
                      Zero // Toward zero
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds up instead
                   * of rounding down.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                   * with further edits by Uniswap Labs also under MIT license.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod0 := mul(x, y)
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          require(denominator > prod1, "Math: mulDiv overflow");
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                          // See https://cs.stackexchange.com/q/138556/92363.
                          // Does not overflow because the denominator cannot be zero at this stage in the function.
                          uint256 twos = denominator & (~denominator + 1);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                          // in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard signed math utilities missing in the Solidity language.
               */
              library SignedMath {
                  /**
                   * @dev Returns the largest of two signed numbers.
                   */
                  function max(int256 a, int256 b) internal pure returns (int256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two signed numbers.
                   */
                  function min(int256 a, int256 b) internal pure returns (int256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two signed numbers without overflow.
                   * The result is rounded towards zero.
                   */
                  function average(int256 a, int256 b) internal pure returns (int256) {
                      // Formula from the book "Hacker's Delight"
                      int256 x = (a & b) + ((a ^ b) >> 1);
                      return x + (int256(uint256(x) >> 255) & (a ^ b));
                  }
                  /**
                   * @dev Returns the absolute unsigned value of a signed value.
                   */
                  function abs(int256 n) internal pure returns (uint256) {
                      unchecked {
                          // must be unchecked in order to support `n = type(int256).min`
                          return uint256(n >= 0 ? n : -n);
                      }
                  }
              }
              

              File 3 of 16: FiatTokenProxy
              pragma solidity ^0.4.24;
              
              // File: zos-lib/contracts/upgradeability/Proxy.sol
              
              /**
               * @title Proxy
               * @dev Implements delegation of calls to other contracts, with proper
               * forwarding of return values and bubbling of failures.
               * It defines a fallback function that delegates all calls to the address
               * returned by the abstract _implementation() internal function.
               */
              contract Proxy {
                /**
                 * @dev Fallback function.
                 * Implemented entirely in `_fallback`.
                 */
                function () payable external {
                  _fallback();
                }
              
                /**
                 * @return The Address of the implementation.
                 */
                function _implementation() internal view returns (address);
              
                /**
                 * @dev Delegates execution to an implementation contract.
                 * This is a low level function that doesn't return to its internal call site.
                 * It will return to the external caller whatever the implementation returns.
                 * @param implementation Address to delegate.
                 */
                function _delegate(address implementation) internal {
                  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 Function that is run as the first thing in the fallback function.
                 * Can be redefined in derived contracts to add functionality.
                 * Redefinitions must call super._willFallback().
                 */
                function _willFallback() internal {
                }
              
                /**
                 * @dev fallback implementation.
                 * Extracted to enable manual triggering.
                 */
                function _fallback() internal {
                  _willFallback();
                  _delegate(_implementation());
                }
              }
              
              // File: openzeppelin-solidity/contracts/AddressUtils.sol
              
              /**
               * Utility library of inline functions on addresses
               */
              library AddressUtils {
              
                /**
                 * Returns whether the target address is a contract
                 * @dev This function will return false if invoked during the constructor of a contract,
                 * as the code is not actually created until after the constructor finishes.
                 * @param addr address to check
                 * @return whether the target address is a contract
                 */
                function isContract(address addr) internal view returns (bool) {
                  uint256 size;
                  // XXX Currently there is no better way to check if there is a contract in an address
                  // than to check the size of the code at that address.
                  // See https://ethereum.stackexchange.com/a/14016/36603
                  // for more details about how this works.
                  // TODO Check this again before the Serenity release, because all addresses will be
                  // contracts then.
                  // solium-disable-next-line security/no-inline-assembly
                  assembly { size := extcodesize(addr) }
                  return size > 0;
                }
              
              }
              
              // File: zos-lib/contracts/upgradeability/UpgradeabilityProxy.sol
              
              /**
               * @title UpgradeabilityProxy
               * @dev This contract implements a proxy that allows to change the
               * implementation address to which it will delegate.
               * Such a change is called an implementation upgrade.
               */
              contract UpgradeabilityProxy is Proxy {
                /**
                 * @dev Emitted when the implementation is upgraded.
                 * @param implementation Address of the new implementation.
                 */
                event Upgraded(address implementation);
              
                /**
                 * @dev Storage slot with the address of the current implementation.
                 * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is
                 * validated in the constructor.
                 */
                bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3;
              
                /**
                 * @dev Contract constructor.
                 * @param _implementation Address of the initial implementation.
                 */
                constructor(address _implementation) public {
                  assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation"));
              
                  _setImplementation(_implementation);
                }
              
                /**
                 * @dev Returns the current implementation.
                 * @return Address of the current implementation
                 */
                function _implementation() internal view returns (address impl) {
                  bytes32 slot = IMPLEMENTATION_SLOT;
                  assembly {
                    impl := sload(slot)
                  }
                }
              
                /**
                 * @dev Upgrades the proxy to a new implementation.
                 * @param newImplementation Address of the new implementation.
                 */
                function _upgradeTo(address newImplementation) internal {
                  _setImplementation(newImplementation);
                  emit Upgraded(newImplementation);
                }
              
                /**
                 * @dev Sets the implementation address of the proxy.
                 * @param newImplementation Address of the new implementation.
                 */
                function _setImplementation(address newImplementation) private {
                  require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address");
              
                  bytes32 slot = IMPLEMENTATION_SLOT;
              
                  assembly {
                    sstore(slot, newImplementation)
                  }
                }
              }
              
              // File: zos-lib/contracts/upgradeability/AdminUpgradeabilityProxy.sol
              
              /**
               * @title AdminUpgradeabilityProxy
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks.
               * All external functions in this contract must be guarded by the
               * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
               * feature proposal that would enable this to be done automatically.
               */
              contract AdminUpgradeabilityProxy is UpgradeabilityProxy {
                /**
                 * @dev Emitted when the administration has been transferred.
                 * @param previousAdmin Address of the previous admin.
                 * @param newAdmin Address of the new admin.
                 */
                event AdminChanged(address previousAdmin, address newAdmin);
              
                /**
                 * @dev Storage slot with the admin of the contract.
                 * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is
                 * validated in the constructor.
                 */
                bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b;
              
                /**
                 * @dev Modifier to check whether the `msg.sender` is the admin.
                 * If it is, it will run the function. Otherwise, it will delegate the call
                 * to the implementation.
                 */
                modifier ifAdmin() {
                  if (msg.sender == _admin()) {
                    _;
                  } else {
                    _fallback();
                  }
                }
              
                /**
                 * Contract constructor.
                 * It sets the `msg.sender` as the proxy administrator.
                 * @param _implementation address of the initial implementation.
                 */
                constructor(address _implementation) UpgradeabilityProxy(_implementation) public {
                  assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin"));
              
                  _setAdmin(msg.sender);
                }
              
                /**
                 * @return The address of the proxy admin.
                 */
                function admin() external view ifAdmin returns (address) {
                  return _admin();
                }
              
                /**
                 * @return The address of the implementation.
                 */
                function implementation() external view ifAdmin returns (address) {
                  return _implementation();
                }
              
                /**
                 * @dev Changes the admin of the proxy.
                 * Only the current admin can call this function.
                 * @param newAdmin Address to transfer proxy administration to.
                 */
                function changeAdmin(address newAdmin) external ifAdmin {
                  require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
                  emit AdminChanged(_admin(), newAdmin);
                  _setAdmin(newAdmin);
                }
              
                /**
                 * @dev Upgrade the backing implementation of the proxy.
                 * Only the admin can call this function.
                 * @param newImplementation Address of the new implementation.
                 */
                function upgradeTo(address newImplementation) external ifAdmin {
                  _upgradeTo(newImplementation);
                }
              
                /**
                 * @dev Upgrade the backing implementation of the proxy and call a function
                 * on the new implementation.
                 * This is useful to initialize the proxied contract.
                 * @param newImplementation Address of the new implementation.
                 * @param data Data to send as msg.data in the low level call.
                 * It should include the signature and the parameters of the function to be
                 * called, as described in
                 * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding.
                 */
                function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin {
                  _upgradeTo(newImplementation);
                  require(address(this).call.value(msg.value)(data));
                }
              
                /**
                 * @return The admin slot.
                 */
                function _admin() internal view returns (address adm) {
                  bytes32 slot = ADMIN_SLOT;
                  assembly {
                    adm := sload(slot)
                  }
                }
              
                /**
                 * @dev Sets the address of the proxy admin.
                 * @param newAdmin Address of the new proxy admin.
                 */
                function _setAdmin(address newAdmin) internal {
                  bytes32 slot = ADMIN_SLOT;
              
                  assembly {
                    sstore(slot, newAdmin)
                  }
                }
              
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal {
                  require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
                  super._willFallback();
                }
              }
              
              // File: contracts/FiatTokenProxy.sol
              
              /**
              * Copyright CENTRE SECZ 2018
              *
              * Permission is hereby granted, free of charge, to any person obtaining a copy 
              * of this software and associated documentation files (the "Software"), to deal 
              * in the Software without restriction, including without limitation the rights 
              * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
              * copies of the Software, and to permit persons to whom the Software is furnished to 
              * do so, subject to the following conditions:
              *
              * The above copyright notice and this permission notice shall be included in all 
              * copies or substantial portions of the Software.
              *
              * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
              * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
              * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
              * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
              * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
              * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
              */
              
              pragma solidity ^0.4.24;
              
              
              /**
               * @title FiatTokenProxy
               * @dev This contract proxies FiatToken calls and enables FiatToken upgrades
              */ 
              contract FiatTokenProxy is AdminUpgradeabilityProxy {
                  constructor(address _implementation) public AdminUpgradeabilityProxy(_implementation) {
                  }
              }

              File 4 of 16: ERC1967Proxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./ERC1967Upgrade.sol";
              /**
               * @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 initializing the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
              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 internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      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 overridden 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 internal 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 overridden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../interfaces/IERC1967.sol";
              import "../../interfaces/draft-IERC1822.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @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 is IERC1967 {
                  // 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 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 {
                      _upgradeTo(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 _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @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 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 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 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);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               *
               * _Available since v4.8.3._
               */
              interface IERC1967 {
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Emitted when the beacon is changed.
                   */
                  event BeaconUpgraded(address indexed beacon);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822Proxiable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @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:
               * ```solidity
               * 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`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              

              File 5 of 16: AdaptiveCurveIrm
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.19;
              import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol";
              import {IAdaptiveCurveIrm} from "./interfaces/IAdaptiveCurveIrm.sol";
              import {UtilsLib} from "./libraries/UtilsLib.sol";
              import {ErrorsLib} from "./libraries/ErrorsLib.sol";
              import {ExpLib} from "./libraries/adaptive-curve/ExpLib.sol";
              import {MathLib, WAD_INT as WAD} from "./libraries/MathLib.sol";
              import {ConstantsLib} from "./libraries/adaptive-curve/ConstantsLib.sol";
              import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol";
              import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol";
              import {MathLib as MorphoMathLib} from "../lib/morpho-blue/src/libraries/MathLib.sol";
              /// @title AdaptiveCurveIrm
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              contract AdaptiveCurveIrm is IAdaptiveCurveIrm {
                  using MathLib for int256;
                  using UtilsLib for int256;
                  using MorphoMathLib for uint128;
                  using MarketParamsLib for MarketParams;
                  /* EVENTS */
                  /// @notice Emitted when a borrow rate is updated.
                  event BorrowRateUpdate(Id indexed id, uint256 avgBorrowRate, uint256 rateAtTarget);
                  /* IMMUTABLES */
                  /// @inheritdoc IAdaptiveCurveIrm
                  address public immutable MORPHO;
                  /* STORAGE */
                  /// @inheritdoc IAdaptiveCurveIrm
                  mapping(Id => int256) public rateAtTarget;
                  /* CONSTRUCTOR */
                  /// @notice Constructor.
                  /// @param morpho The address of Morpho.
                  constructor(address morpho) {
                      require(morpho != address(0), ErrorsLib.ZERO_ADDRESS);
                      MORPHO = morpho;
                  }
                  /* BORROW RATES */
                  /// @inheritdoc IIrm
                  function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256) {
                      (uint256 avgRate,) = _borrowRate(marketParams.id(), market);
                      return avgRate;
                  }
                  /// @inheritdoc IIrm
                  function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256) {
                      require(msg.sender == MORPHO, ErrorsLib.NOT_MORPHO);
                      Id id = marketParams.id();
                      (uint256 avgRate, int256 endRateAtTarget) = _borrowRate(id, market);
                      rateAtTarget[id] = endRateAtTarget;
                      // Safe "unchecked" cast because endRateAtTarget >= 0.
                      emit BorrowRateUpdate(id, avgRate, uint256(endRateAtTarget));
                      return avgRate;
                  }
                  /// @dev Returns avgRate and endRateAtTarget.
                  /// @dev Assumes that the inputs `marketParams` and `id` match.
                  function _borrowRate(Id id, Market memory market) private view returns (uint256, int256) {
                      // Safe "unchecked" cast because the utilization is smaller than 1 (scaled by WAD).
                      int256 utilization =
                          int256(market.totalSupplyAssets > 0 ? market.totalBorrowAssets.wDivDown(market.totalSupplyAssets) : 0);
                      int256 errNormFactor = utilization > ConstantsLib.TARGET_UTILIZATION
                          ? WAD - ConstantsLib.TARGET_UTILIZATION
                          : ConstantsLib.TARGET_UTILIZATION;
                      int256 err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivToZero(errNormFactor);
                      int256 startRateAtTarget = rateAtTarget[id];
                      int256 avgRateAtTarget;
                      int256 endRateAtTarget;
                      if (startRateAtTarget == 0) {
                          // First interaction.
                          avgRateAtTarget = ConstantsLib.INITIAL_RATE_AT_TARGET;
                          endRateAtTarget = ConstantsLib.INITIAL_RATE_AT_TARGET;
                      } else {
                          // The speed is assumed constant between two updates, but it is in fact not constant because of interest.
                          // So the rate is always underestimated.
                          int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulToZero(err);
                          // market.lastUpdate != 0 because it is not the first interaction with this market.
                          // Safe "unchecked" cast because block.timestamp - market.lastUpdate <= block.timestamp <= type(int256).max.
                          int256 elapsed = int256(block.timestamp - market.lastUpdate);
                          int256 linearAdaptation = speed * elapsed;
                          if (linearAdaptation == 0) {
                              // If linearAdaptation == 0, avgRateAtTarget = endRateAtTarget = startRateAtTarget;
                              avgRateAtTarget = startRateAtTarget;
                              endRateAtTarget = startRateAtTarget;
                          } else {
                              // Formula of the average rate that should be returned to Morpho Blue:
                              // avg = 1/T * ∫_0^T curve(startRateAtTarget*exp(speed*x), err) dx
                              // The integral is approximated with the trapezoidal rule:
                              // avg ~= 1/T * Σ_i=1^N [curve(f((i-1) * T/N), err) + curve(f(i * T/N), err)] / 2 * T/N
                              // Where f(x) = startRateAtTarget*exp(speed*x)
                              // avg ~= Σ_i=1^N [curve(f((i-1) * T/N), err) + curve(f(i * T/N), err)] / (2 * N)
                              // As curve is linear in its first argument:
                              // avg ~= curve([Σ_i=1^N [f((i-1) * T/N) + f(i * T/N)] / (2 * N), err)
                              // avg ~= curve([(f(0) + f(T))/2 + Σ_i=1^(N-1) f(i * T/N)] / N, err)
                              // avg ~= curve([(startRateAtTarget + endRateAtTarget)/2 + Σ_i=1^(N-1) f(i * T/N)] / N, err)
                              // With N = 2:
                              // avg ~= curve([(startRateAtTarget + endRateAtTarget)/2 + startRateAtTarget*exp(speed*T/2)] / 2, err)
                              // avg ~= curve([startRateAtTarget + endRateAtTarget + 2*startRateAtTarget*exp(speed*T/2)] / 4, err)
                              endRateAtTarget = _newRateAtTarget(startRateAtTarget, linearAdaptation);
                              int256 midRateAtTarget = _newRateAtTarget(startRateAtTarget, linearAdaptation / 2);
                              avgRateAtTarget = (startRateAtTarget + endRateAtTarget + 2 * midRateAtTarget) / 4;
                          }
                      }
                      // Safe "unchecked" cast because avgRateAtTarget >= 0.
                      return (uint256(_curve(avgRateAtTarget, err)), endRateAtTarget);
                  }
                  /// @dev Returns the rate for a given `_rateAtTarget` and an `err`.
                  /// The formula of the curve is the following:
                  /// r = ((1-1/C)*err + 1) * rateAtTarget if err < 0
                  ///     ((C-1)*err + 1) * rateAtTarget else.
                  function _curve(int256 _rateAtTarget, int256 err) private pure returns (int256) {
                      // Non negative because 1 - 1/C >= 0, C - 1 >= 0.
                      int256 coeff = err < 0 ? WAD - WAD.wDivToZero(ConstantsLib.CURVE_STEEPNESS) : ConstantsLib.CURVE_STEEPNESS - WAD;
                      // Non negative if _rateAtTarget >= 0 because if err < 0, coeff <= 1.
                      return (coeff.wMulToZero(err) + WAD).wMulToZero(int256(_rateAtTarget));
                  }
                  /// @dev Returns the new rate at target, for a given `startRateAtTarget` and a given `linearAdaptation`.
                  /// The formula is: max(min(startRateAtTarget * exp(linearAdaptation), maxRateAtTarget), minRateAtTarget).
                  function _newRateAtTarget(int256 startRateAtTarget, int256 linearAdaptation) private pure returns (int256) {
                      // Non negative because MIN_RATE_AT_TARGET > 0.
                      return startRateAtTarget.wMulToZero(ExpLib.wExp(linearAdaptation)).bound(
                          ConstantsLib.MIN_RATE_AT_TARGET, ConstantsLib.MAX_RATE_AT_TARGET
                      );
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              import {MarketParams, Market} from "./IMorpho.sol";
              /// @title IIrm
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
              interface IIrm {
                  /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
                  /// @dev Assumes that `market` corresponds to `marketParams`.
                  function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);
                  /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
                  /// storage.
                  /// @dev Assumes that `market` corresponds to `marketParams`.
                  function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.5.0;
              import {IIrm} from "../../lib/morpho-blue/src/interfaces/IIrm.sol";
              import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol";
              /// @title IAdaptiveCurveIrm
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Interface exposed by the AdaptiveCurveIrm.
              interface IAdaptiveCurveIrm is IIrm {
                  /// @notice Address of Morpho.
                  function MORPHO() external view returns (address);
                  /// @notice Rate at target utilization.
                  /// @dev Tells the height of the curve.
                  function rateAtTarget(Id id) external view returns (int256);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /// @title UtilsLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library exposing helpers.
              library UtilsLib {
                  /// @dev Bounds `x` between `low` and `high`.
                  /// @dev Assumes that `low` <= `high`. If it is not the case it returns `low`.
                  function bound(int256 x, int256 low, int256 high) internal pure returns (int256 z) {
                      assembly {
                          // z = min(x, high).
                          z := xor(x, mul(xor(x, high), slt(high, x)))
                          // z = max(z, low).
                          z := xor(z, mul(xor(z, low), sgt(low, z)))
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /// @title ErrorsLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library exposing error messages.
              library ErrorsLib {
                  /// @dev Thrown when passing the zero address.
                  string internal constant ZERO_ADDRESS = "zero address";
                  /// @dev Thrown when the caller is not Morpho.
                  string internal constant NOT_MORPHO = "not Morpho";
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import {WAD_INT} from "../MathLib.sol";
              /// @title ExpLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library to approximate the exponential function.
              library ExpLib {
                  /// @dev ln(2).
                  int256 internal constant LN_2_INT = 0.693147180559945309 ether;
                  /// @dev ln(1e-18).
                  int256 internal constant LN_WEI_INT = -41.446531673892822312 ether;
                  /// @dev Above this bound, `wExp` is clipped to avoid overflowing when multiplied with 1 ether.
                  /// @dev This upper bound corresponds to: ln(type(int256).max / 1e36) (scaled by WAD, floored).
                  int256 internal constant WEXP_UPPER_BOUND = 93.859467695000404319 ether;
                  /// @dev The value of wExp(`WEXP_UPPER_BOUND`).
                  int256 internal constant WEXP_UPPER_VALUE = 57716089161558943949701069502944508345128.422502756744429568 ether;
                  /// @dev Returns an approximation of exp.
                  function wExp(int256 x) internal pure returns (int256) {
                      unchecked {
                          // If x < ln(1e-18) then exp(x) < 1e-18 so it is rounded to zero.
                          if (x < LN_WEI_INT) return 0;
                          // `wExp` is clipped to avoid overflowing when multiplied with 1 ether.
                          if (x >= WEXP_UPPER_BOUND) return WEXP_UPPER_VALUE;
                          // Decompose x as x = q * ln(2) + r with q an integer and -ln(2)/2 <= r <= ln(2)/2.
                          // q = x / ln(2) rounded half toward zero.
                          int256 roundingAdjustment = (x < 0) ? -(LN_2_INT / 2) : (LN_2_INT / 2);
                          // Safe unchecked because x is bounded.
                          int256 q = (x + roundingAdjustment) / LN_2_INT;
                          // Safe unchecked because |q * ln(2) - x| <= ln(2)/2.
                          int256 r = x - q * LN_2_INT;
                          // Compute e^r with a 2nd-order Taylor polynomial.
                          // Safe unchecked because |r| < 1e18.
                          int256 expR = WAD_INT + r + (r * r) / WAD_INT / 2;
                          // Return e^x = 2^q * e^r.
                          if (q >= 0) return expR << uint256(q);
                          else return expR >> uint256(-q);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import {WAD} from "../../lib/morpho-blue/src/libraries/MathLib.sol";
              int256 constant WAD_INT = int256(WAD);
              /// @title MathLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library to manage fixed-point arithmetic on signed integers.
              library MathLib {
                  /// @dev Returns the multiplication of `x` by `y` (in WAD) rounded towards 0.
                  function wMulToZero(int256 x, int256 y) internal pure returns (int256) {
                      return (x * y) / WAD_INT;
                  }
                  /// @dev Returns the division of `x` by `y` (in WAD) rounded towards 0.
                  function wDivToZero(int256 x, int256 y) internal pure returns (int256) {
                      return (x * WAD_INT) / y;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /// @title ConstantsLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              library ConstantsLib {
                  /// @notice Curve steepness (scaled by WAD).
                  /// @dev Curve steepness = 4.
                  int256 public constant CURVE_STEEPNESS = 4 ether;
                  /// @notice Adjustment speed per second (scaled by WAD).
                  /// @dev The speed is per second, so the rate moves at a speed of ADJUSTMENT_SPEED * err each second (while being
                  /// continuously compounded).
                  /// @dev Adjustment speed = 50/year.
                  int256 public constant ADJUSTMENT_SPEED = 50 ether / int256(365 days);
                  /// @notice Target utilization (scaled by WAD).
                  /// @dev Target utilization = 90%.
                  int256 public constant TARGET_UTILIZATION = 0.9 ether;
                  /// @notice Initial rate at target per second (scaled by WAD).
                  /// @dev Initial rate at target = 4% (rate between 1% and 16%).
                  int256 public constant INITIAL_RATE_AT_TARGET = 0.04 ether / int256(365 days);
                  /// @notice Minimum rate at target per second (scaled by WAD).
                  /// @dev Minimum rate at target = 0.1% (minimum rate = 0.025%).
                  int256 public constant MIN_RATE_AT_TARGET = 0.001 ether / int256(365 days);
                  /// @notice Maximum rate at target per second (scaled by WAD).
                  /// @dev Maximum rate at target = 200% (maximum rate = 800%).
                  int256 public constant MAX_RATE_AT_TARGET = 2.0 ether / int256(365 days);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              import {Id, MarketParams} from "../interfaces/IMorpho.sol";
              /// @title MarketParamsLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library to convert a market to its id.
              library MarketParamsLib {
                  /// @notice The length of the data used to compute the id of a market.
                  /// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
                  uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;
                  /// @notice Returns the id of the market `marketParams`.
                  function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
                      assembly ("memory-safe") {
                          marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              type Id is bytes32;
              struct MarketParams {
                  address loanToken;
                  address collateralToken;
                  address oracle;
                  address irm;
                  uint256 lltv;
              }
              /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
              /// accrual.
              struct Position {
                  uint256 supplyShares;
                  uint128 borrowShares;
                  uint128 collateral;
              }
              /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
              /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
              /// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
              /// interest accrual.
              struct Market {
                  uint128 totalSupplyAssets;
                  uint128 totalSupplyShares;
                  uint128 totalBorrowAssets;
                  uint128 totalBorrowShares;
                  uint128 lastUpdate;
                  uint128 fee;
              }
              struct Authorization {
                  address authorizer;
                  address authorized;
                  bool isAuthorized;
                  uint256 nonce;
                  uint256 deadline;
              }
              struct Signature {
                  uint8 v;
                  bytes32 r;
                  bytes32 s;
              }
              /// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
              /// @dev Consider using the IMorpho interface instead of this one.
              interface IMorphoBase {
                  /// @notice The EIP-712 domain separator.
                  /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing
                  /// the same chain id because the domain separator would be the same.
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
                  /// @notice The owner of the contract.
                  /// @dev It has the power to change the owner.
                  /// @dev It has the power to set fees on markets and set the fee recipient.
                  /// @dev It has the power to enable but not disable IRMs and LLTVs.
                  function owner() external view returns (address);
                  /// @notice The fee recipient of all markets.
                  /// @dev The recipient receives the fees of a given market through a supply position on that market.
                  function feeRecipient() external view returns (address);
                  /// @notice Whether the `irm` is enabled.
                  function isIrmEnabled(address irm) external view returns (bool);
                  /// @notice Whether the `lltv` is enabled.
                  function isLltvEnabled(uint256 lltv) external view returns (bool);
                  /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
                  /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
                  function isAuthorized(address authorizer, address authorized) external view returns (bool);
                  /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
                  function nonce(address authorizer) external view returns (uint256);
                  /// @notice Sets `newOwner` as `owner` of the contract.
                  /// @dev Warning: No two-step transfer ownership.
                  /// @dev Warning: The owner can be set to the zero address.
                  function setOwner(address newOwner) external;
                  /// @notice Enables `irm` as a possible IRM for market creation.
                  /// @dev Warning: It is not possible to disable an IRM.
                  function enableIrm(address irm) external;
                  /// @notice Enables `lltv` as a possible LLTV for market creation.
                  /// @dev Warning: It is not possible to disable a LLTV.
                  function enableLltv(uint256 lltv) external;
                  /// @notice Sets the `newFee` for the given market `marketParams`.
                  /// @param newFee The new fee, scaled by WAD.
                  /// @dev Warning: The recipient can be the zero address.
                  function setFee(MarketParams memory marketParams, uint256 newFee) external;
                  /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
                  /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
                  /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
                  /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
                  function setFeeRecipient(address newFeeRecipient) external;
                  /// @notice Creates the market `marketParams`.
                  /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
                  /// Morpho behaves as expected:
                  /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
                  /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
                  /// burn functions are not supported.
                  /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
                  /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
                  /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
                  /// - The IRM should not re-enter Morpho.
                  /// - The oracle should return a price with the correct scaling.
                  /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties
                  /// (funds could get stuck):
                  /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue.
                  /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and
                  /// `toSharesDown` overflow.
                  /// - The IRM can revert on `borrowRate`.
                  /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest`
                  /// overflow.
                  /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and
                  /// `liquidate` from being used under certain market conditions.
                  /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or
                  /// the computation of `assetsRepaid` in `liquidate` overflow.
                  /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
                  /// the point where `totalBorrowShares` is very large and borrowing overflows.
                  function createMarket(MarketParams memory marketParams) external;
                  /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
                  /// `onMorphoSupply` function with the given `data`.
                  /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
                  /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
                  /// amount of shares is given for full compatibility and precision.
                  /// @dev Supplying a large amount can revert for overflow.
                  /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
                  /// Consider using the `assets` parameter to avoid this.
                  /// @param marketParams The market to supply assets to.
                  /// @param assets The amount of assets to supply.
                  /// @param shares The amount of shares to mint.
                  /// @param onBehalf The address that will own the increased supply position.
                  /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
                  /// @return assetsSupplied The amount of assets supplied.
                  /// @return sharesSupplied The amount of shares minted.
                  function supply(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      bytes memory data
                  ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);
                  /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
                  /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
                  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
                  /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
                  /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
                  /// conversion roundings between shares and assets.
                  /// @param marketParams The market to withdraw assets from.
                  /// @param assets The amount of assets to withdraw.
                  /// @param shares The amount of shares to burn.
                  /// @param onBehalf The address of the owner of the supply position.
                  /// @param receiver The address that will receive the withdrawn assets.
                  /// @return assetsWithdrawn The amount of assets withdrawn.
                  /// @return sharesWithdrawn The amount of shares burned.
                  function withdraw(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      address receiver
                  ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);
                  /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
                  /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
                  /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
                  /// given for full compatibility and precision.
                  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
                  /// @dev Borrowing a large amount can revert for overflow.
                  /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
                  /// Consider using the `assets` parameter to avoid this.
                  /// @param marketParams The market to borrow assets from.
                  /// @param assets The amount of assets to borrow.
                  /// @param shares The amount of shares to mint.
                  /// @param onBehalf The address that will own the increased borrow position.
                  /// @param receiver The address that will receive the borrowed assets.
                  /// @return assetsBorrowed The amount of assets borrowed.
                  /// @return sharesBorrowed The amount of shares minted.
                  function borrow(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      address receiver
                  ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);
                  /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
                  /// `onMorphoReplay` function with the given `data`.
                  /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
                  /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
                  /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
                  /// roundings between shares and assets.
                  /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
                  /// @param marketParams The market to repay assets to.
                  /// @param assets The amount of assets to repay.
                  /// @param shares The amount of shares to burn.
                  /// @param onBehalf The address of the owner of the debt position.
                  /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
                  /// @return assetsRepaid The amount of assets repaid.
                  /// @return sharesRepaid The amount of shares burned.
                  function repay(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      bytes memory data
                  ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
                  /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
                  /// `onMorphoSupplyCollateral` function with the given `data`.
                  /// @dev Interest are not accrued since it's not required and it saves gas.
                  /// @dev Supplying a large amount can revert for overflow.
                  /// @param marketParams The market to supply collateral to.
                  /// @param assets The amount of collateral to supply.
                  /// @param onBehalf The address that will own the increased collateral position.
                  /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
                  function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
                      external;
                  /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
                  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
                  /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
                  /// @param marketParams The market to withdraw collateral from.
                  /// @param assets The amount of collateral to withdraw.
                  /// @param onBehalf The address of the owner of the collateral position.
                  /// @param receiver The address that will receive the collateral assets.
                  function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
                      external;
                  /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
                  /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
                  /// `onMorphoLiquidate` function with the given `data`.
                  /// @dev Either `seizedAssets` or `repaidShares` should be zero.
                  /// @dev Seizing more than the collateral balance will underflow and revert without any error message.
                  /// @dev Repaying more than the borrow balance will underflow and revert without any error message.
                  /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
                  /// @param marketParams The market of the position.
                  /// @param borrower The owner of the position.
                  /// @param seizedAssets The amount of collateral to seize.
                  /// @param repaidShares The amount of shares to repay.
                  /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
                  /// @return The amount of assets seized.
                  /// @return The amount of assets repaid.
                  function liquidate(
                      MarketParams memory marketParams,
                      address borrower,
                      uint256 seizedAssets,
                      uint256 repaidShares,
                      bytes memory data
                  ) external returns (uint256, uint256);
                  /// @notice Executes a flash loan.
                  /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
                  /// markets combined, plus donations).
                  /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
                  /// - `flashFee` is zero.
                  /// - `maxFlashLoan` is the token's balance of this contract.
                  /// - The receiver of `assets` is the caller.
                  /// @param token The token to flash loan.
                  /// @param assets The amount of assets to flash loan.
                  /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
                  function flashLoan(address token, uint256 assets, bytes calldata data) external;
                  /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
                  /// @param authorized The authorized address.
                  /// @param newIsAuthorized The new authorization status.
                  function setAuthorization(address authorized, bool newIsAuthorized) external;
                  /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
                  /// @dev Warning: Reverts if the signature has already been submitted.
                  /// @dev The signature is malleable, but it has no impact on the security here.
                  /// @dev The nonce is passed as argument to be able to revert with a different error message.
                  /// @param authorization The `Authorization` struct.
                  /// @param signature The signature.
                  function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;
                  /// @notice Accrues interest for the given market `marketParams`.
                  function accrueInterest(MarketParams memory marketParams) external;
                  /// @notice Returns the data stored on the different `slots`.
                  function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
              }
              /// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
              /// @dev Consider using the IMorpho interface instead of this one.
              interface IMorphoStaticTyping is IMorphoBase {
                  /// @notice The state of the position of `user` on the market corresponding to `id`.
                  /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
                  /// accrual.
                  function position(Id id, address user)
                      external
                      view
                      returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);
                  /// @notice The state of the market corresponding to `id`.
                  /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
                  /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
                  /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
                  /// accrual.
                  function market(Id id)
                      external
                      view
                      returns (
                          uint128 totalSupplyAssets,
                          uint128 totalSupplyShares,
                          uint128 totalBorrowAssets,
                          uint128 totalBorrowShares,
                          uint128 lastUpdate,
                          uint128 fee
                      );
                  /// @notice The market params corresponding to `id`.
                  /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
                  /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
                  function idToMarketParams(Id id)
                      external
                      view
                      returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
              }
              /// @title IMorpho
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
              interface IMorpho is IMorphoBase {
                  /// @notice The state of the position of `user` on the market corresponding to `id`.
                  /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
                  /// accrual.
                  function position(Id id, address user) external view returns (Position memory p);
                  /// @notice The state of the market corresponding to `id`.
                  /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
                  /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
                  /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
                  /// interest accrual.
                  function market(Id id) external view returns (Market memory m);
                  /// @notice The market params corresponding to `id`.
                  /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
                  /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
                  function idToMarketParams(Id id) external view returns (MarketParams memory);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              uint256 constant WAD = 1e18;
              /// @title MathLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library to manage fixed-point arithmetic.
              library MathLib {
                  /// @dev Returns (`x` * `y`) / `WAD` rounded down.
                  function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
                      return mulDivDown(x, y, WAD);
                  }
                  /// @dev Returns (`x` * `WAD`) / `y` rounded down.
                  function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
                      return mulDivDown(x, WAD, y);
                  }
                  /// @dev Returns (`x` * `WAD`) / `y` rounded up.
                  function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
                      return mulDivUp(x, WAD, y);
                  }
                  /// @dev Returns (`x` * `y`) / `d` rounded down.
                  function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
                      return (x * y) / d;
                  }
                  /// @dev Returns (`x` * `y`) / `d` rounded up.
                  function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
                      return (x * y + (d - 1)) / d;
                  }
                  /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
                  /// continuous compound interest rate.
                  function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
                      uint256 firstTerm = x * n;
                      uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
                      uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);
                      return firstTerm + secondTerm + thirdTerm;
                  }
              }
              

              File 6 of 16: Morpho
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.19;
              import {
                  Id,
                  IMorphoStaticTyping,
                  IMorphoBase,
                  MarketParams,
                  Position,
                  Market,
                  Authorization,
                  Signature
              } from "./interfaces/IMorpho.sol";
              import {
                  IMorphoLiquidateCallback,
                  IMorphoRepayCallback,
                  IMorphoSupplyCallback,
                  IMorphoSupplyCollateralCallback,
                  IMorphoFlashLoanCallback
              } from "./interfaces/IMorphoCallbacks.sol";
              import {IIrm} from "./interfaces/IIrm.sol";
              import {IERC20} from "./interfaces/IERC20.sol";
              import {IOracle} from "./interfaces/IOracle.sol";
              import "./libraries/ConstantsLib.sol";
              import {UtilsLib} from "./libraries/UtilsLib.sol";
              import {EventsLib} from "./libraries/EventsLib.sol";
              import {ErrorsLib} from "./libraries/ErrorsLib.sol";
              import {MathLib, WAD} from "./libraries/MathLib.sol";
              import {SharesMathLib} from "./libraries/SharesMathLib.sol";
              import {MarketParamsLib} from "./libraries/MarketParamsLib.sol";
              import {SafeTransferLib} from "./libraries/SafeTransferLib.sol";
              /// @title Morpho
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice The Morpho contract.
              contract Morpho is IMorphoStaticTyping {
                  using MathLib for uint128;
                  using MathLib for uint256;
                  using UtilsLib for uint256;
                  using SharesMathLib for uint256;
                  using SafeTransferLib for IERC20;
                  using MarketParamsLib for MarketParams;
                  /* IMMUTABLES */
                  /// @inheritdoc IMorphoBase
                  bytes32 public immutable DOMAIN_SEPARATOR;
                  /* STORAGE */
                  /// @inheritdoc IMorphoBase
                  address public owner;
                  /// @inheritdoc IMorphoBase
                  address public feeRecipient;
                  /// @inheritdoc IMorphoStaticTyping
                  mapping(Id => mapping(address => Position)) public position;
                  /// @inheritdoc IMorphoStaticTyping
                  mapping(Id => Market) public market;
                  /// @inheritdoc IMorphoBase
                  mapping(address => bool) public isIrmEnabled;
                  /// @inheritdoc IMorphoBase
                  mapping(uint256 => bool) public isLltvEnabled;
                  /// @inheritdoc IMorphoBase
                  mapping(address => mapping(address => bool)) public isAuthorized;
                  /// @inheritdoc IMorphoBase
                  mapping(address => uint256) public nonce;
                  /// @inheritdoc IMorphoStaticTyping
                  mapping(Id => MarketParams) public idToMarketParams;
                  /* CONSTRUCTOR */
                  /// @param newOwner The new owner of the contract.
                  constructor(address newOwner) {
                      require(newOwner != address(0), ErrorsLib.ZERO_ADDRESS);
                      DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, block.chainid, address(this)));
                      owner = newOwner;
                      emit EventsLib.SetOwner(newOwner);
                  }
                  /* MODIFIERS */
                  /// @dev Reverts if the caller is not the owner.
                  modifier onlyOwner() {
                      require(msg.sender == owner, ErrorsLib.NOT_OWNER);
                      _;
                  }
                  /* ONLY OWNER FUNCTIONS */
                  /// @inheritdoc IMorphoBase
                  function setOwner(address newOwner) external onlyOwner {
                      require(newOwner != owner, ErrorsLib.ALREADY_SET);
                      owner = newOwner;
                      emit EventsLib.SetOwner(newOwner);
                  }
                  /// @inheritdoc IMorphoBase
                  function enableIrm(address irm) external onlyOwner {
                      require(!isIrmEnabled[irm], ErrorsLib.ALREADY_SET);
                      isIrmEnabled[irm] = true;
                      emit EventsLib.EnableIrm(irm);
                  }
                  /// @inheritdoc IMorphoBase
                  function enableLltv(uint256 lltv) external onlyOwner {
                      require(!isLltvEnabled[lltv], ErrorsLib.ALREADY_SET);
                      require(lltv < WAD, ErrorsLib.MAX_LLTV_EXCEEDED);
                      isLltvEnabled[lltv] = true;
                      emit EventsLib.EnableLltv(lltv);
                  }
                  /// @inheritdoc IMorphoBase
                  function setFee(MarketParams memory marketParams, uint256 newFee) external onlyOwner {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      require(newFee != market[id].fee, ErrorsLib.ALREADY_SET);
                      require(newFee <= MAX_FEE, ErrorsLib.MAX_FEE_EXCEEDED);
                      // Accrue interest using the previous fee set before changing it.
                      _accrueInterest(marketParams, id);
                      // Safe "unchecked" cast.
                      market[id].fee = uint128(newFee);
                      emit EventsLib.SetFee(id, newFee);
                  }
                  /// @inheritdoc IMorphoBase
                  function setFeeRecipient(address newFeeRecipient) external onlyOwner {
                      require(newFeeRecipient != feeRecipient, ErrorsLib.ALREADY_SET);
                      feeRecipient = newFeeRecipient;
                      emit EventsLib.SetFeeRecipient(newFeeRecipient);
                  }
                  /* MARKET CREATION */
                  /// @inheritdoc IMorphoBase
                  function createMarket(MarketParams memory marketParams) external {
                      Id id = marketParams.id();
                      require(isIrmEnabled[marketParams.irm], ErrorsLib.IRM_NOT_ENABLED);
                      require(isLltvEnabled[marketParams.lltv], ErrorsLib.LLTV_NOT_ENABLED);
                      require(market[id].lastUpdate == 0, ErrorsLib.MARKET_ALREADY_CREATED);
                      // Safe "unchecked" cast.
                      market[id].lastUpdate = uint128(block.timestamp);
                      idToMarketParams[id] = marketParams;
                      emit EventsLib.CreateMarket(id, marketParams);
                      // Call to initialize the IRM in case it is stateful.
                      if (marketParams.irm != address(0)) IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
                  }
                  /* SUPPLY MANAGEMENT */
                  /// @inheritdoc IMorphoBase
                  function supply(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      bytes calldata data
                  ) external returns (uint256, uint256) {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
                      require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS);
                      _accrueInterest(marketParams, id);
                      if (assets > 0) shares = assets.toSharesDown(market[id].totalSupplyAssets, market[id].totalSupplyShares);
                      else assets = shares.toAssetsUp(market[id].totalSupplyAssets, market[id].totalSupplyShares);
                      position[id][onBehalf].supplyShares += shares;
                      market[id].totalSupplyShares += shares.toUint128();
                      market[id].totalSupplyAssets += assets.toUint128();
                      emit EventsLib.Supply(id, msg.sender, onBehalf, assets, shares);
                      if (data.length > 0) IMorphoSupplyCallback(msg.sender).onMorphoSupply(assets, data);
                      IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), assets);
                      return (assets, shares);
                  }
                  /// @inheritdoc IMorphoBase
                  function withdraw(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      address receiver
                  ) external returns (uint256, uint256) {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
                      require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
                      // No need to verify that onBehalf != address(0) thanks to the following authorization check.
                      require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);
                      _accrueInterest(marketParams, id);
                      if (assets > 0) shares = assets.toSharesUp(market[id].totalSupplyAssets, market[id].totalSupplyShares);
                      else assets = shares.toAssetsDown(market[id].totalSupplyAssets, market[id].totalSupplyShares);
                      position[id][onBehalf].supplyShares -= shares;
                      market[id].totalSupplyShares -= shares.toUint128();
                      market[id].totalSupplyAssets -= assets.toUint128();
                      require(market[id].totalBorrowAssets <= market[id].totalSupplyAssets, ErrorsLib.INSUFFICIENT_LIQUIDITY);
                      emit EventsLib.Withdraw(id, msg.sender, onBehalf, receiver, assets, shares);
                      IERC20(marketParams.loanToken).safeTransfer(receiver, assets);
                      return (assets, shares);
                  }
                  /* BORROW MANAGEMENT */
                  /// @inheritdoc IMorphoBase
                  function borrow(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      address receiver
                  ) external returns (uint256, uint256) {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
                      require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
                      // No need to verify that onBehalf != address(0) thanks to the following authorization check.
                      require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);
                      _accrueInterest(marketParams, id);
                      if (assets > 0) shares = assets.toSharesUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
                      else assets = shares.toAssetsDown(market[id].totalBorrowAssets, market[id].totalBorrowShares);
                      position[id][onBehalf].borrowShares += shares.toUint128();
                      market[id].totalBorrowShares += shares.toUint128();
                      market[id].totalBorrowAssets += assets.toUint128();
                      require(_isHealthy(marketParams, id, onBehalf), ErrorsLib.INSUFFICIENT_COLLATERAL);
                      require(market[id].totalBorrowAssets <= market[id].totalSupplyAssets, ErrorsLib.INSUFFICIENT_LIQUIDITY);
                      emit EventsLib.Borrow(id, msg.sender, onBehalf, receiver, assets, shares);
                      IERC20(marketParams.loanToken).safeTransfer(receiver, assets);
                      return (assets, shares);
                  }
                  /// @inheritdoc IMorphoBase
                  function repay(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      bytes calldata data
                  ) external returns (uint256, uint256) {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      require(UtilsLib.exactlyOneZero(assets, shares), ErrorsLib.INCONSISTENT_INPUT);
                      require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS);
                      _accrueInterest(marketParams, id);
                      if (assets > 0) shares = assets.toSharesDown(market[id].totalBorrowAssets, market[id].totalBorrowShares);
                      else assets = shares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
                      position[id][onBehalf].borrowShares -= shares.toUint128();
                      market[id].totalBorrowShares -= shares.toUint128();
                      market[id].totalBorrowAssets = UtilsLib.zeroFloorSub(market[id].totalBorrowAssets, assets).toUint128();
                      // `assets` may be greater than `totalBorrowAssets` by 1.
                      emit EventsLib.Repay(id, msg.sender, onBehalf, assets, shares);
                      if (data.length > 0) IMorphoRepayCallback(msg.sender).onMorphoRepay(assets, data);
                      IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), assets);
                      return (assets, shares);
                  }
                  /* COLLATERAL MANAGEMENT */
                  /// @inheritdoc IMorphoBase
                  function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes calldata data)
                      external
                  {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      require(assets != 0, ErrorsLib.ZERO_ASSETS);
                      require(onBehalf != address(0), ErrorsLib.ZERO_ADDRESS);
                      // Don't accrue interest because it's not required and it saves gas.
                      position[id][onBehalf].collateral += assets.toUint128();
                      emit EventsLib.SupplyCollateral(id, msg.sender, onBehalf, assets);
                      if (data.length > 0) IMorphoSupplyCollateralCallback(msg.sender).onMorphoSupplyCollateral(assets, data);
                      IERC20(marketParams.collateralToken).safeTransferFrom(msg.sender, address(this), assets);
                  }
                  /// @inheritdoc IMorphoBase
                  function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
                      external
                  {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      require(assets != 0, ErrorsLib.ZERO_ASSETS);
                      require(receiver != address(0), ErrorsLib.ZERO_ADDRESS);
                      // No need to verify that onBehalf != address(0) thanks to the following authorization check.
                      require(_isSenderAuthorized(onBehalf), ErrorsLib.UNAUTHORIZED);
                      _accrueInterest(marketParams, id);
                      position[id][onBehalf].collateral -= assets.toUint128();
                      require(_isHealthy(marketParams, id, onBehalf), ErrorsLib.INSUFFICIENT_COLLATERAL);
                      emit EventsLib.WithdrawCollateral(id, msg.sender, onBehalf, receiver, assets);
                      IERC20(marketParams.collateralToken).safeTransfer(receiver, assets);
                  }
                  /* LIQUIDATION */
                  /// @inheritdoc IMorphoBase
                  function liquidate(
                      MarketParams memory marketParams,
                      address borrower,
                      uint256 seizedAssets,
                      uint256 repaidShares,
                      bytes calldata data
                  ) external returns (uint256, uint256) {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      require(UtilsLib.exactlyOneZero(seizedAssets, repaidShares), ErrorsLib.INCONSISTENT_INPUT);
                      _accrueInterest(marketParams, id);
                      {
                          uint256 collateralPrice = IOracle(marketParams.oracle).price();
                          require(!_isHealthy(marketParams, id, borrower, collateralPrice), ErrorsLib.HEALTHY_POSITION);
                          // The liquidation incentive factor is min(maxLiquidationIncentiveFactor, 1/(1 - cursor*(1 - lltv))).
                          uint256 liquidationIncentiveFactor = UtilsLib.min(
                              MAX_LIQUIDATION_INCENTIVE_FACTOR,
                              WAD.wDivDown(WAD - LIQUIDATION_CURSOR.wMulDown(WAD - marketParams.lltv))
                          );
                          if (seizedAssets > 0) {
                              uint256 seizedAssetsQuoted = seizedAssets.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE);
                              repaidShares = seizedAssetsQuoted.wDivUp(liquidationIncentiveFactor).toSharesUp(
                                  market[id].totalBorrowAssets, market[id].totalBorrowShares
                              );
                          } else {
                              seizedAssets = repaidShares.toAssetsDown(market[id].totalBorrowAssets, market[id].totalBorrowShares)
                                  .wMulDown(liquidationIncentiveFactor).mulDivDown(ORACLE_PRICE_SCALE, collateralPrice);
                          }
                      }
                      uint256 repaidAssets = repaidShares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares);
                      position[id][borrower].borrowShares -= repaidShares.toUint128();
                      market[id].totalBorrowShares -= repaidShares.toUint128();
                      market[id].totalBorrowAssets = UtilsLib.zeroFloorSub(market[id].totalBorrowAssets, repaidAssets).toUint128();
                      position[id][borrower].collateral -= seizedAssets.toUint128();
                      uint256 badDebtShares;
                      uint256 badDebtAssets;
                      if (position[id][borrower].collateral == 0) {
                          badDebtShares = position[id][borrower].borrowShares;
                          badDebtAssets = UtilsLib.min(
                              market[id].totalBorrowAssets,
                              badDebtShares.toAssetsUp(market[id].totalBorrowAssets, market[id].totalBorrowShares)
                          );
                          market[id].totalBorrowAssets -= badDebtAssets.toUint128();
                          market[id].totalSupplyAssets -= badDebtAssets.toUint128();
                          market[id].totalBorrowShares -= badDebtShares.toUint128();
                          position[id][borrower].borrowShares = 0;
                      }
                      // `repaidAssets` may be greater than `totalBorrowAssets` by 1.
                      emit EventsLib.Liquidate(
                          id, msg.sender, borrower, repaidAssets, repaidShares, seizedAssets, badDebtAssets, badDebtShares
                      );
                      IERC20(marketParams.collateralToken).safeTransfer(msg.sender, seizedAssets);
                      if (data.length > 0) IMorphoLiquidateCallback(msg.sender).onMorphoLiquidate(repaidAssets, data);
                      IERC20(marketParams.loanToken).safeTransferFrom(msg.sender, address(this), repaidAssets);
                      return (seizedAssets, repaidAssets);
                  }
                  /* FLASH LOANS */
                  /// @inheritdoc IMorphoBase
                  function flashLoan(address token, uint256 assets, bytes calldata data) external {
                      require(assets != 0, ErrorsLib.ZERO_ASSETS);
                      emit EventsLib.FlashLoan(msg.sender, token, assets);
                      IERC20(token).safeTransfer(msg.sender, assets);
                      IMorphoFlashLoanCallback(msg.sender).onMorphoFlashLoan(assets, data);
                      IERC20(token).safeTransferFrom(msg.sender, address(this), assets);
                  }
                  /* AUTHORIZATION */
                  /// @inheritdoc IMorphoBase
                  function setAuthorization(address authorized, bool newIsAuthorized) external {
                      require(newIsAuthorized != isAuthorized[msg.sender][authorized], ErrorsLib.ALREADY_SET);
                      isAuthorized[msg.sender][authorized] = newIsAuthorized;
                      emit EventsLib.SetAuthorization(msg.sender, msg.sender, authorized, newIsAuthorized);
                  }
                  /// @inheritdoc IMorphoBase
                  function setAuthorizationWithSig(Authorization memory authorization, Signature calldata signature) external {
                      /// Do not check whether authorization is already set because the nonce increment is a desired side effect.
                      require(block.timestamp <= authorization.deadline, ErrorsLib.SIGNATURE_EXPIRED);
                      require(authorization.nonce == nonce[authorization.authorizer]++, ErrorsLib.INVALID_NONCE);
                      bytes32 hashStruct = keccak256(abi.encode(AUTHORIZATION_TYPEHASH, authorization));
                      bytes32 digest = keccak256(bytes.concat("\\x19\\x01", DOMAIN_SEPARATOR, hashStruct));
                      address signatory = ecrecover(digest, signature.v, signature.r, signature.s);
                      require(signatory != address(0) && authorization.authorizer == signatory, ErrorsLib.INVALID_SIGNATURE);
                      emit EventsLib.IncrementNonce(msg.sender, authorization.authorizer, authorization.nonce);
                      isAuthorized[authorization.authorizer][authorization.authorized] = authorization.isAuthorized;
                      emit EventsLib.SetAuthorization(
                          msg.sender, authorization.authorizer, authorization.authorized, authorization.isAuthorized
                      );
                  }
                  /// @dev Returns whether the sender is authorized to manage `onBehalf`'s positions.
                  function _isSenderAuthorized(address onBehalf) internal view returns (bool) {
                      return msg.sender == onBehalf || isAuthorized[onBehalf][msg.sender];
                  }
                  /* INTEREST MANAGEMENT */
                  /// @inheritdoc IMorphoBase
                  function accrueInterest(MarketParams memory marketParams) external {
                      Id id = marketParams.id();
                      require(market[id].lastUpdate != 0, ErrorsLib.MARKET_NOT_CREATED);
                      _accrueInterest(marketParams, id);
                  }
                  /// @dev Accrues interest for the given market `marketParams`.
                  /// @dev Assumes that the inputs `marketParams` and `id` match.
                  function _accrueInterest(MarketParams memory marketParams, Id id) internal {
                      uint256 elapsed = block.timestamp - market[id].lastUpdate;
                      if (elapsed == 0) return;
                      if (marketParams.irm != address(0)) {
                          uint256 borrowRate = IIrm(marketParams.irm).borrowRate(marketParams, market[id]);
                          uint256 interest = market[id].totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
                          market[id].totalBorrowAssets += interest.toUint128();
                          market[id].totalSupplyAssets += interest.toUint128();
                          uint256 feeShares;
                          if (market[id].fee != 0) {
                              uint256 feeAmount = interest.wMulDown(market[id].fee);
                              // The fee amount is subtracted from the total supply in this calculation to compensate for the fact
                              // that total supply is already increased by the full interest (including the fee amount).
                              feeShares =
                                  feeAmount.toSharesDown(market[id].totalSupplyAssets - feeAmount, market[id].totalSupplyShares);
                              position[id][feeRecipient].supplyShares += feeShares;
                              market[id].totalSupplyShares += feeShares.toUint128();
                          }
                          emit EventsLib.AccrueInterest(id, borrowRate, interest, feeShares);
                      }
                      // Safe "unchecked" cast.
                      market[id].lastUpdate = uint128(block.timestamp);
                  }
                  /* HEALTH CHECK */
                  /// @dev Returns whether the position of `borrower` in the given market `marketParams` is healthy.
                  /// @dev Assumes that the inputs `marketParams` and `id` match.
                  function _isHealthy(MarketParams memory marketParams, Id id, address borrower) internal view returns (bool) {
                      if (position[id][borrower].borrowShares == 0) return true;
                      uint256 collateralPrice = IOracle(marketParams.oracle).price();
                      return _isHealthy(marketParams, id, borrower, collateralPrice);
                  }
                  /// @dev Returns whether the position of `borrower` in the given market `marketParams` with the given
                  /// `collateralPrice` is healthy.
                  /// @dev Assumes that the inputs `marketParams` and `id` match.
                  /// @dev Rounds in favor of the protocol, so one might not be able to borrow exactly `maxBorrow` but one unit less.
                  function _isHealthy(MarketParams memory marketParams, Id id, address borrower, uint256 collateralPrice)
                      internal
                      view
                      returns (bool)
                  {
                      uint256 borrowed = uint256(position[id][borrower].borrowShares).toAssetsUp(
                          market[id].totalBorrowAssets, market[id].totalBorrowShares
                      );
                      uint256 maxBorrow = uint256(position[id][borrower].collateral).mulDivDown(collateralPrice, ORACLE_PRICE_SCALE)
                          .wMulDown(marketParams.lltv);
                      return maxBorrow >= borrowed;
                  }
                  /* STORAGE VIEW */
                  /// @inheritdoc IMorphoBase
                  function extSloads(bytes32[] calldata slots) external view returns (bytes32[] memory res) {
                      uint256 nSlots = slots.length;
                      res = new bytes32[](nSlots);
                      for (uint256 i; i < nSlots;) {
                          bytes32 slot = slots[i++];
                          assembly ("memory-safe") {
                              mstore(add(res, mul(i, 32)), sload(slot))
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              type Id is bytes32;
              struct MarketParams {
                  address loanToken;
                  address collateralToken;
                  address oracle;
                  address irm;
                  uint256 lltv;
              }
              /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
              /// accrual.
              struct Position {
                  uint256 supplyShares;
                  uint128 borrowShares;
                  uint128 collateral;
              }
              /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
              /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
              /// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
              /// interest accrual.
              struct Market {
                  uint128 totalSupplyAssets;
                  uint128 totalSupplyShares;
                  uint128 totalBorrowAssets;
                  uint128 totalBorrowShares;
                  uint128 lastUpdate;
                  uint128 fee;
              }
              struct Authorization {
                  address authorizer;
                  address authorized;
                  bool isAuthorized;
                  uint256 nonce;
                  uint256 deadline;
              }
              struct Signature {
                  uint8 v;
                  bytes32 r;
                  bytes32 s;
              }
              /// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
              /// @dev Consider using the IMorpho interface instead of this one.
              interface IMorphoBase {
                  /// @notice The EIP-712 domain separator.
                  /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing
                  /// the same chain id because the domain separator would be the same.
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
                  /// @notice The owner of the contract.
                  /// @dev It has the power to change the owner.
                  /// @dev It has the power to set fees on markets and set the fee recipient.
                  /// @dev It has the power to enable but not disable IRMs and LLTVs.
                  function owner() external view returns (address);
                  /// @notice The fee recipient of all markets.
                  /// @dev The recipient receives the fees of a given market through a supply position on that market.
                  function feeRecipient() external view returns (address);
                  /// @notice Whether the `irm` is enabled.
                  function isIrmEnabled(address irm) external view returns (bool);
                  /// @notice Whether the `lltv` is enabled.
                  function isLltvEnabled(uint256 lltv) external view returns (bool);
                  /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
                  /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
                  function isAuthorized(address authorizer, address authorized) external view returns (bool);
                  /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
                  function nonce(address authorizer) external view returns (uint256);
                  /// @notice Sets `newOwner` as `owner` of the contract.
                  /// @dev Warning: No two-step transfer ownership.
                  /// @dev Warning: The owner can be set to the zero address.
                  function setOwner(address newOwner) external;
                  /// @notice Enables `irm` as a possible IRM for market creation.
                  /// @dev Warning: It is not possible to disable an IRM.
                  function enableIrm(address irm) external;
                  /// @notice Enables `lltv` as a possible LLTV for market creation.
                  /// @dev Warning: It is not possible to disable a LLTV.
                  function enableLltv(uint256 lltv) external;
                  /// @notice Sets the `newFee` for the given market `marketParams`.
                  /// @param newFee The new fee, scaled by WAD.
                  /// @dev Warning: The recipient can be the zero address.
                  function setFee(MarketParams memory marketParams, uint256 newFee) external;
                  /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
                  /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
                  /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
                  /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
                  function setFeeRecipient(address newFeeRecipient) external;
                  /// @notice Creates the market `marketParams`.
                  /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
                  /// Morpho behaves as expected:
                  /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
                  /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
                  /// burn functions are not supported.
                  /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
                  /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
                  /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
                  /// - The IRM should not re-enter Morpho.
                  /// - The oracle should return a price with the correct scaling.
                  /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties
                  /// (funds could get stuck):
                  /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue.
                  /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and
                  /// `toSharesDown` overflow.
                  /// - The IRM can revert on `borrowRate`.
                  /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest`
                  /// overflow.
                  /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and
                  /// `liquidate` from being used under certain market conditions.
                  /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or
                  /// the computation of `assetsRepaid` in `liquidate` overflow.
                  /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
                  /// the point where `totalBorrowShares` is very large and borrowing overflows.
                  function createMarket(MarketParams memory marketParams) external;
                  /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
                  /// `onMorphoSupply` function with the given `data`.
                  /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
                  /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
                  /// amount of shares is given for full compatibility and precision.
                  /// @dev Supplying a large amount can revert for overflow.
                  /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
                  /// Consider using the `assets` parameter to avoid this.
                  /// @param marketParams The market to supply assets to.
                  /// @param assets The amount of assets to supply.
                  /// @param shares The amount of shares to mint.
                  /// @param onBehalf The address that will own the increased supply position.
                  /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
                  /// @return assetsSupplied The amount of assets supplied.
                  /// @return sharesSupplied The amount of shares minted.
                  function supply(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      bytes memory data
                  ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);
                  /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
                  /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
                  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
                  /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
                  /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
                  /// conversion roundings between shares and assets.
                  /// @param marketParams The market to withdraw assets from.
                  /// @param assets The amount of assets to withdraw.
                  /// @param shares The amount of shares to burn.
                  /// @param onBehalf The address of the owner of the supply position.
                  /// @param receiver The address that will receive the withdrawn assets.
                  /// @return assetsWithdrawn The amount of assets withdrawn.
                  /// @return sharesWithdrawn The amount of shares burned.
                  function withdraw(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      address receiver
                  ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);
                  /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
                  /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
                  /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
                  /// given for full compatibility and precision.
                  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
                  /// @dev Borrowing a large amount can revert for overflow.
                  /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
                  /// Consider using the `assets` parameter to avoid this.
                  /// @param marketParams The market to borrow assets from.
                  /// @param assets The amount of assets to borrow.
                  /// @param shares The amount of shares to mint.
                  /// @param onBehalf The address that will own the increased borrow position.
                  /// @param receiver The address that will receive the borrowed assets.
                  /// @return assetsBorrowed The amount of assets borrowed.
                  /// @return sharesBorrowed The amount of shares minted.
                  function borrow(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      address receiver
                  ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);
                  /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
                  /// `onMorphoReplay` function with the given `data`.
                  /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
                  /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
                  /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
                  /// roundings between shares and assets.
                  /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
                  /// @param marketParams The market to repay assets to.
                  /// @param assets The amount of assets to repay.
                  /// @param shares The amount of shares to burn.
                  /// @param onBehalf The address of the owner of the debt position.
                  /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
                  /// @return assetsRepaid The amount of assets repaid.
                  /// @return sharesRepaid The amount of shares burned.
                  function repay(
                      MarketParams memory marketParams,
                      uint256 assets,
                      uint256 shares,
                      address onBehalf,
                      bytes memory data
                  ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
                  /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
                  /// `onMorphoSupplyCollateral` function with the given `data`.
                  /// @dev Interest are not accrued since it's not required and it saves gas.
                  /// @dev Supplying a large amount can revert for overflow.
                  /// @param marketParams The market to supply collateral to.
                  /// @param assets The amount of collateral to supply.
                  /// @param onBehalf The address that will own the increased collateral position.
                  /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
                  function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
                      external;
                  /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
                  /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
                  /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
                  /// @param marketParams The market to withdraw collateral from.
                  /// @param assets The amount of collateral to withdraw.
                  /// @param onBehalf The address of the owner of the collateral position.
                  /// @param receiver The address that will receive the collateral assets.
                  function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
                      external;
                  /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
                  /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
                  /// `onMorphoLiquidate` function with the given `data`.
                  /// @dev Either `seizedAssets` or `repaidShares` should be zero.
                  /// @dev Seizing more than the collateral balance will underflow and revert without any error message.
                  /// @dev Repaying more than the borrow balance will underflow and revert without any error message.
                  /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
                  /// @param marketParams The market of the position.
                  /// @param borrower The owner of the position.
                  /// @param seizedAssets The amount of collateral to seize.
                  /// @param repaidShares The amount of shares to repay.
                  /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
                  /// @return The amount of assets seized.
                  /// @return The amount of assets repaid.
                  function liquidate(
                      MarketParams memory marketParams,
                      address borrower,
                      uint256 seizedAssets,
                      uint256 repaidShares,
                      bytes memory data
                  ) external returns (uint256, uint256);
                  /// @notice Executes a flash loan.
                  /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
                  /// markets combined, plus donations).
                  /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
                  /// - `flashFee` is zero.
                  /// - `maxFlashLoan` is the token's balance of this contract.
                  /// - The receiver of `assets` is the caller.
                  /// @param token The token to flash loan.
                  /// @param assets The amount of assets to flash loan.
                  /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
                  function flashLoan(address token, uint256 assets, bytes calldata data) external;
                  /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
                  /// @param authorized The authorized address.
                  /// @param newIsAuthorized The new authorization status.
                  function setAuthorization(address authorized, bool newIsAuthorized) external;
                  /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
                  /// @dev Warning: Reverts if the signature has already been submitted.
                  /// @dev The signature is malleable, but it has no impact on the security here.
                  /// @dev The nonce is passed as argument to be able to revert with a different error message.
                  /// @param authorization The `Authorization` struct.
                  /// @param signature The signature.
                  function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;
                  /// @notice Accrues interest for the given market `marketParams`.
                  function accrueInterest(MarketParams memory marketParams) external;
                  /// @notice Returns the data stored on the different `slots`.
                  function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
              }
              /// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
              /// @dev Consider using the IMorpho interface instead of this one.
              interface IMorphoStaticTyping is IMorphoBase {
                  /// @notice The state of the position of `user` on the market corresponding to `id`.
                  /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
                  /// accrual.
                  function position(Id id, address user)
                      external
                      view
                      returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);
                  /// @notice The state of the market corresponding to `id`.
                  /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
                  /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
                  /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
                  /// accrual.
                  function market(Id id)
                      external
                      view
                      returns (
                          uint128 totalSupplyAssets,
                          uint128 totalSupplyShares,
                          uint128 totalBorrowAssets,
                          uint128 totalBorrowShares,
                          uint128 lastUpdate,
                          uint128 fee
                      );
                  /// @notice The market params corresponding to `id`.
                  /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
                  /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
                  function idToMarketParams(Id id)
                      external
                      view
                      returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
              }
              /// @title IMorpho
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
              interface IMorpho is IMorphoBase {
                  /// @notice The state of the position of `user` on the market corresponding to `id`.
                  /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
                  /// accrual.
                  function position(Id id, address user) external view returns (Position memory p);
                  /// @notice The state of the market corresponding to `id`.
                  /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
                  /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
                  /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
                  /// interest accrual.
                  function market(Id id) external view returns (Market memory m);
                  /// @notice The market params corresponding to `id`.
                  /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
                  /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
                  function idToMarketParams(Id id) external view returns (MarketParams memory);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title IMorphoLiquidateCallback
              /// @notice Interface that liquidators willing to use `liquidate`'s callback must implement.
              interface IMorphoLiquidateCallback {
                  /// @notice Callback called when a liquidation occurs.
                  /// @dev The callback is called only if data is not empty.
                  /// @param repaidAssets The amount of repaid assets.
                  /// @param data Arbitrary data passed to the `liquidate` function.
                  function onMorphoLiquidate(uint256 repaidAssets, bytes calldata data) external;
              }
              /// @title IMorphoRepayCallback
              /// @notice Interface that users willing to use `repay`'s callback must implement.
              interface IMorphoRepayCallback {
                  /// @notice Callback called when a repayment occurs.
                  /// @dev The callback is called only if data is not empty.
                  /// @param assets The amount of repaid assets.
                  /// @param data Arbitrary data passed to the `repay` function.
                  function onMorphoRepay(uint256 assets, bytes calldata data) external;
              }
              /// @title IMorphoSupplyCallback
              /// @notice Interface that users willing to use `supply`'s callback must implement.
              interface IMorphoSupplyCallback {
                  /// @notice Callback called when a supply occurs.
                  /// @dev The callback is called only if data is not empty.
                  /// @param assets The amount of supplied assets.
                  /// @param data Arbitrary data passed to the `supply` function.
                  function onMorphoSupply(uint256 assets, bytes calldata data) external;
              }
              /// @title IMorphoSupplyCollateralCallback
              /// @notice Interface that users willing to use `supplyCollateral`'s callback must implement.
              interface IMorphoSupplyCollateralCallback {
                  /// @notice Callback called when a supply of collateral occurs.
                  /// @dev The callback is called only if data is not empty.
                  /// @param assets The amount of supplied collateral.
                  /// @param data Arbitrary data passed to the `supplyCollateral` function.
                  function onMorphoSupplyCollateral(uint256 assets, bytes calldata data) external;
              }
              /// @title IMorphoFlashLoanCallback
              /// @notice Interface that users willing to use `flashLoan`'s callback must implement.
              interface IMorphoFlashLoanCallback {
                  /// @notice Callback called when a flash loan occurs.
                  /// @dev The callback is called only if data is not empty.
                  /// @param assets The amount of assets that was flash loaned.
                  /// @param data Arbitrary data passed to the `flashLoan` function.
                  function onMorphoFlashLoan(uint256 assets, bytes calldata data) external;
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              import {MarketParams, Market} from "./IMorpho.sol";
              /// @title IIrm
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
              interface IIrm {
                  /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
                  /// @dev Assumes that `market` corresponds to `marketParams`.
                  function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);
                  /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
                  /// storage.
                  /// @dev Assumes that `market` corresponds to `marketParams`.
                  function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title IERC20
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @dev Empty because we only call library functions. It prevents calling transfer (transferFrom) instead of
              /// safeTransfer (safeTransferFrom).
              interface IERC20 {}
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title IOracle
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Interface that oracles used by Morpho must implement.
              /// @dev It is the user's responsibility to select markets with safe oracles.
              interface IOracle {
                  /// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36.
                  /// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in
                  /// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals`
                  /// decimals of precision.
                  function price() external view returns (uint256);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              /// @dev The maximum fee a market can have (25%).
              uint256 constant MAX_FEE = 0.25e18;
              /// @dev Oracle price scale.
              uint256 constant ORACLE_PRICE_SCALE = 1e36;
              /// @dev Liquidation cursor.
              uint256 constant LIQUIDATION_CURSOR = 0.3e18;
              /// @dev Max liquidation incentive factor.
              uint256 constant MAX_LIQUIDATION_INCENTIVE_FACTOR = 1.15e18;
              /// @dev The EIP-712 typeHash for EIP712Domain.
              bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
              /// @dev The EIP-712 typeHash for Authorization.
              bytes32 constant AUTHORIZATION_TYPEHASH =
                  keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)");
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              import {ErrorsLib} from "../libraries/ErrorsLib.sol";
              /// @title UtilsLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library exposing helpers.
              /// @dev Inspired by https://github.com/morpho-org/morpho-utils.
              library UtilsLib {
                  /// @dev Returns true if there is exactly one zero among `x` and `y`.
                  function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
                      assembly {
                          z := xor(iszero(x), iszero(y))
                      }
                  }
                  /// @dev Returns the min of `x` and `y`.
                  function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
                      assembly {
                          z := xor(x, mul(xor(x, y), lt(y, x)))
                      }
                  }
                  /// @dev Returns `x` safely cast to uint128.
                  function toUint128(uint256 x) internal pure returns (uint128) {
                      require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED);
                      return uint128(x);
                  }
                  /// @dev Returns max(0, x - y).
                  function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
                      assembly {
                          z := mul(gt(x, y), sub(x, y))
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              import {Id, MarketParams} from "../interfaces/IMorpho.sol";
              /// @title EventsLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library exposing events.
              library EventsLib {
                  /// @notice Emitted when setting a new owner.
                  /// @param newOwner The new owner of the contract.
                  event SetOwner(address indexed newOwner);
                  /// @notice Emitted when setting a new fee.
                  /// @param id The market id.
                  /// @param newFee The new fee.
                  event SetFee(Id indexed id, uint256 newFee);
                  /// @notice Emitted when setting a new fee recipient.
                  /// @param newFeeRecipient The new fee recipient.
                  event SetFeeRecipient(address indexed newFeeRecipient);
                  /// @notice Emitted when enabling an IRM.
                  /// @param irm The IRM that was enabled.
                  event EnableIrm(address indexed irm);
                  /// @notice Emitted when enabling an LLTV.
                  /// @param lltv The LLTV that was enabled.
                  event EnableLltv(uint256 lltv);
                  /// @notice Emitted when creating a market.
                  /// @param id The market id.
                  /// @param marketParams The market that was created.
                  event CreateMarket(Id indexed id, MarketParams marketParams);
                  /// @notice Emitted on supply of assets.
                  /// @dev Warning: `feeRecipient` receives some shares during interest accrual without any supply event emitted.
                  /// @param id The market id.
                  /// @param caller The caller.
                  /// @param onBehalf The owner of the modified position.
                  /// @param assets The amount of assets supplied.
                  /// @param shares The amount of shares minted.
                  event Supply(Id indexed id, address indexed caller, address indexed onBehalf, uint256 assets, uint256 shares);
                  /// @notice Emitted on withdrawal of assets.
                  /// @param id The market id.
                  /// @param caller The caller.
                  /// @param onBehalf The owner of the modified position.
                  /// @param receiver The address that received the withdrawn assets.
                  /// @param assets The amount of assets withdrawn.
                  /// @param shares The amount of shares burned.
                  event Withdraw(
                      Id indexed id,
                      address caller,
                      address indexed onBehalf,
                      address indexed receiver,
                      uint256 assets,
                      uint256 shares
                  );
                  /// @notice Emitted on borrow of assets.
                  /// @param id The market id.
                  /// @param caller The caller.
                  /// @param onBehalf The owner of the modified position.
                  /// @param receiver The address that received the borrowed assets.
                  /// @param assets The amount of assets borrowed.
                  /// @param shares The amount of shares minted.
                  event Borrow(
                      Id indexed id,
                      address caller,
                      address indexed onBehalf,
                      address indexed receiver,
                      uint256 assets,
                      uint256 shares
                  );
                  /// @notice Emitted on repayment of assets.
                  /// @param id The market id.
                  /// @param caller The caller.
                  /// @param onBehalf The owner of the modified position.
                  /// @param assets The amount of assets repaid. May be 1 over the corresponding market's `totalBorrowAssets`.
                  /// @param shares The amount of shares burned.
                  event Repay(Id indexed id, address indexed caller, address indexed onBehalf, uint256 assets, uint256 shares);
                  /// @notice Emitted on supply of collateral.
                  /// @param id The market id.
                  /// @param caller The caller.
                  /// @param onBehalf The owner of the modified position.
                  /// @param assets The amount of collateral supplied.
                  event SupplyCollateral(Id indexed id, address indexed caller, address indexed onBehalf, uint256 assets);
                  /// @notice Emitted on withdrawal of collateral.
                  /// @param id The market id.
                  /// @param caller The caller.
                  /// @param onBehalf The owner of the modified position.
                  /// @param receiver The address that received the withdrawn collateral.
                  /// @param assets The amount of collateral withdrawn.
                  event WithdrawCollateral(
                      Id indexed id, address caller, address indexed onBehalf, address indexed receiver, uint256 assets
                  );
                  /// @notice Emitted on liquidation of a position.
                  /// @param id The market id.
                  /// @param caller The caller.
                  /// @param borrower The borrower of the position.
                  /// @param repaidAssets The amount of assets repaid. May be 1 over the corresponding market's `totalBorrowAssets`.
                  /// @param repaidShares The amount of shares burned.
                  /// @param seizedAssets The amount of collateral seized.
                  /// @param badDebtAssets The amount of assets of bad debt realized.
                  /// @param badDebtShares The amount of borrow shares of bad debt realized.
                  event Liquidate(
                      Id indexed id,
                      address indexed caller,
                      address indexed borrower,
                      uint256 repaidAssets,
                      uint256 repaidShares,
                      uint256 seizedAssets,
                      uint256 badDebtAssets,
                      uint256 badDebtShares
                  );
                  /// @notice Emitted on flash loan.
                  /// @param caller The caller.
                  /// @param token The token that was flash loaned.
                  /// @param assets The amount that was flash loaned.
                  event FlashLoan(address indexed caller, address indexed token, uint256 assets);
                  /// @notice Emitted when setting an authorization.
                  /// @param caller The caller.
                  /// @param authorizer The authorizer address.
                  /// @param authorized The authorized address.
                  /// @param newIsAuthorized The new authorization status.
                  event SetAuthorization(
                      address indexed caller, address indexed authorizer, address indexed authorized, bool newIsAuthorized
                  );
                  /// @notice Emitted when setting an authorization with a signature.
                  /// @param caller The caller.
                  /// @param authorizer The authorizer address.
                  /// @param usedNonce The nonce that was used.
                  event IncrementNonce(address indexed caller, address indexed authorizer, uint256 usedNonce);
                  /// @notice Emitted when accruing interest.
                  /// @param id The market id.
                  /// @param prevBorrowRate The previous borrow rate.
                  /// @param interest The amount of interest accrued.
                  /// @param feeShares The amount of shares minted as fee.
                  event AccrueInterest(Id indexed id, uint256 prevBorrowRate, uint256 interest, uint256 feeShares);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              /// @title ErrorsLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library exposing error messages.
              library ErrorsLib {
                  /// @notice Thrown when the caller is not the owner.
                  string internal constant NOT_OWNER = "not owner";
                  /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV.
                  string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded";
                  /// @notice Thrown when the fee to set exceeds the maximum fee.
                  string internal constant MAX_FEE_EXCEEDED = "max fee exceeded";
                  /// @notice Thrown when the value is already set.
                  string internal constant ALREADY_SET = "already set";
                  /// @notice Thrown when the IRM is not enabled at market creation.
                  string internal constant IRM_NOT_ENABLED = "IRM not enabled";
                  /// @notice Thrown when the LLTV is not enabled at market creation.
                  string internal constant LLTV_NOT_ENABLED = "LLTV not enabled";
                  /// @notice Thrown when the market is already created.
                  string internal constant MARKET_ALREADY_CREATED = "market already created";
                  /// @notice Thrown when a token to transfer doesn't have code.
                  string internal constant NO_CODE = "no code";
                  /// @notice Thrown when the market is not created.
                  string internal constant MARKET_NOT_CREATED = "market not created";
                  /// @notice Thrown when not exactly one of the input amount is zero.
                  string internal constant INCONSISTENT_INPUT = "inconsistent input";
                  /// @notice Thrown when zero assets is passed as input.
                  string internal constant ZERO_ASSETS = "zero assets";
                  /// @notice Thrown when a zero address is passed as input.
                  string internal constant ZERO_ADDRESS = "zero address";
                  /// @notice Thrown when the caller is not authorized to conduct an action.
                  string internal constant UNAUTHORIZED = "unauthorized";
                  /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`.
                  string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral";
                  /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`.
                  string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity";
                  /// @notice Thrown when the position to liquidate is healthy.
                  string internal constant HEALTHY_POSITION = "position is healthy";
                  /// @notice Thrown when the authorization signature is invalid.
                  string internal constant INVALID_SIGNATURE = "invalid signature";
                  /// @notice Thrown when the authorization signature is expired.
                  string internal constant SIGNATURE_EXPIRED = "signature expired";
                  /// @notice Thrown when the nonce is invalid.
                  string internal constant INVALID_NONCE = "invalid nonce";
                  /// @notice Thrown when a token transfer reverted.
                  string internal constant TRANSFER_REVERTED = "transfer reverted";
                  /// @notice Thrown when a token transfer returned false.
                  string internal constant TRANSFER_RETURNED_FALSE = "transfer returned false";
                  /// @notice Thrown when a token transferFrom reverted.
                  string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted";
                  /// @notice Thrown when a token transferFrom returned false
                  string internal constant TRANSFER_FROM_RETURNED_FALSE = "transferFrom returned false";
                  /// @notice Thrown when the maximum uint128 is exceeded.
                  string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded";
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              uint256 constant WAD = 1e18;
              /// @title MathLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library to manage fixed-point arithmetic.
              library MathLib {
                  /// @dev Returns (`x` * `y`) / `WAD` rounded down.
                  function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
                      return mulDivDown(x, y, WAD);
                  }
                  /// @dev Returns (`x` * `WAD`) / `y` rounded down.
                  function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
                      return mulDivDown(x, WAD, y);
                  }
                  /// @dev Returns (`x` * `WAD`) / `y` rounded up.
                  function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
                      return mulDivUp(x, WAD, y);
                  }
                  /// @dev Returns (`x` * `y`) / `d` rounded down.
                  function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
                      return (x * y) / d;
                  }
                  /// @dev Returns (`x` * `y`) / `d` rounded up.
                  function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
                      return (x * y + (d - 1)) / d;
                  }
                  /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
                  /// continuous compound interest rate.
                  function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
                      uint256 firstTerm = x * n;
                      uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
                      uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);
                      return firstTerm + secondTerm + thirdTerm;
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              import {MathLib} from "./MathLib.sol";
              /// @title SharesMathLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Shares management library.
              /// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
              /// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
              library SharesMathLib {
                  using MathLib for uint256;
                  /// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
                  /// high precision computations.
                  /// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
                  /// stays low enough not to inflate these assets to a significant value.
                  /// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
                  uint256 internal constant VIRTUAL_SHARES = 1e6;
                  /// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
                  /// empty.
                  uint256 internal constant VIRTUAL_ASSETS = 1;
                  /// @dev Calculates the value of `assets` quoted in shares, rounding down.
                  function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
                      return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
                  }
                  /// @dev Calculates the value of `shares` quoted in assets, rounding down.
                  function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
                      return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
                  }
                  /// @dev Calculates the value of `assets` quoted in shares, rounding up.
                  function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
                      return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
                  }
                  /// @dev Calculates the value of `shares` quoted in assets, rounding up.
                  function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
                      return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              import {Id, MarketParams} from "../interfaces/IMorpho.sol";
              /// @title MarketParamsLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library to convert a market to its id.
              library MarketParamsLib {
                  /// @notice The length of the data used to compute the id of a market.
                  /// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
                  uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;
                  /// @notice Returns the id of the market `marketParams`.
                  function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
                      assembly ("memory-safe") {
                          marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.0;
              import {IERC20} from "../interfaces/IERC20.sol";
              import {ErrorsLib} from "../libraries/ErrorsLib.sol";
              interface IERC20Internal {
                  function transfer(address to, uint256 value) external returns (bool);
                  function transferFrom(address from, address to, uint256 value) external returns (bool);
              }
              /// @title SafeTransferLib
              /// @author Morpho Labs
              /// @custom:contact [email protected]
              /// @notice Library to manage transfers of tokens, even if calls to the transfer or transferFrom functions are not
              /// returning a boolean.
              library SafeTransferLib {
                  function safeTransfer(IERC20 token, address to, uint256 value) internal {
                      require(address(token).code.length > 0, ErrorsLib.NO_CODE);
                      (bool success, bytes memory returndata) =
                          address(token).call(abi.encodeCall(IERC20Internal.transfer, (to, value)));
                      require(success, ErrorsLib.TRANSFER_REVERTED);
                      require(returndata.length == 0 || abi.decode(returndata, (bool)), ErrorsLib.TRANSFER_RETURNED_FALSE);
                  }
                  function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                      require(address(token).code.length > 0, ErrorsLib.NO_CODE);
                      (bool success, bytes memory returndata) =
                          address(token).call(abi.encodeCall(IERC20Internal.transferFrom, (from, to, value)));
                      require(success, ErrorsLib.TRANSFER_FROM_REVERTED);
                      require(returndata.length == 0 || abi.decode(returndata, (bool)), ErrorsLib.TRANSFER_FROM_RETURNED_FALSE);
                  }
              }
              

              File 7 of 16: ERC1967Proxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./ERC1967Upgrade.sol";
              /**
               * @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 initializing the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
              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 internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      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 overridden 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 internal 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 overridden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../interfaces/IERC1967.sol";
              import "../../interfaces/draft-IERC1822.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @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 is IERC1967 {
                  // 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 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 {
                      _upgradeTo(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 _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @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 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 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 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);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               *
               * _Available since v4.8.3._
               */
              interface IERC1967 {
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Emitted when the beacon is changed.
                   */
                  event BeaconUpgraded(address indexed beacon);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822Proxiable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @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:
               * ```solidity
               * 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`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              

              File 8 of 16: ERC1967Proxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822Proxiable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               *
               * _Available since v4.8.3._
               */
              interface IERC1967 {
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Emitted when the beacon is changed.
                   */
                  event BeaconUpgraded(address indexed beacon);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./ERC1967Upgrade.sol";
              /**
               * @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 initializing the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../interfaces/IERC1967.sol";
              import "../../interfaces/draft-IERC1822.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @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 is IERC1967 {
                  // 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 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 {
                      _upgradeTo(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 _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @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 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 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 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);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
              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 internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      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 overridden 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 internal 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 overridden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @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:
               * ```solidity
               * 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`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              

              File 9 of 16: StakedlvlUSD
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity ^0.8.20;
              /* solhint-disable private-vars-leading-underscore */
              import "@openzeppelin-4.9.0/contracts/token/ERC20/extensions/ERC4626.sol";
              import "@openzeppelin-4.9.0/contracts/token/ERC20/utils/SafeERC20.sol";
              import "@openzeppelin-4.9.0/contracts/security/ReentrancyGuard.sol";
              import {ERC20Permit} from "@openzeppelin-4.9.0/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
              import {SingleAdminAccessControl} from "./auth/v4/SingleAdminAccessControl.sol";
              import "./interfaces/IStakedlvlUSDCooldown.sol";
              import {slvlUSDSilo} from "./slvlUSDSilo.sol";
              /**
               * @title StakedlvlUSD
               * @notice The StakedlvlUSD contract allows users to stake lvlUSD tokens to earn dollar-denominated
                         base + AVS yields from onchain lending protocols. The contract also has an optional cooldown
               *         period for withdrawing staked assets.
               * @notice Forked from Ethena's StakedUSDe contract.
               */
              contract StakedlvlUSD is
                  SingleAdminAccessControl,
                  ReentrancyGuard,
                  ERC20Permit,
                  ERC4626,
                  IStakedlvlUSD,
                  IStakedlvlUSDCooldown
              {
                  using SafeERC20 for IERC20;
                  /* ------------- CONSTANTS ------------- */
                  /// @notice The role that is allowed to distribute rewards to this contract
                  bytes32 private constant REWARDER_ROLE = keccak256("REWARDER_ROLE");
                  /// @notice The role that is allowed to blacklist and un-blacklist addresses
                  bytes32 private constant DENYLIST_MANAGER_ROLE =
                      keccak256("DENYLIST_MANAGER_ROLE");
                  /// @notice The role which prevents an address to stake
                  bytes32 private constant SOFT_RESTRICTED_STAKER_ROLE =
                      keccak256("SOFT_RESTRICTED_STAKER_ROLE");
                  /// @notice The role which prevents an address to transfer, stake, or unstake. The owner of the contract can redirect address staking balance if an address is in full restricting mode.
                  bytes32 private constant FULL_RESTRICTED_STAKER_ROLE =
                      keccak256("FULL_RESTRICTED_STAKER_ROLE");
                  /// @notice The vesting period of lastDistributionAmount over which it increasingly becomes available to stakers
                  uint256 private constant VESTING_PERIOD = 8 hours;
                  /// @notice Minimum non-zero shares amount to prevent donation attack
                  uint256 private constant MIN_SHARES = 1 ether;
                  /* ------------- STATE VARIABLES ------------- */
                  mapping(address => UserCooldown) public cooldowns;
                  uint24 public constant MAX_COOLDOWN_DURATION = 90 days;
                  uint24 public cooldownDuration;
                  slvlUSDSilo public silo;
                  /// @notice The amount of the last asset distribution from the controller contract into this
                  /// contract + any unvested remainder at that time
                  uint256 public vestingAmount;
                  /// @notice The timestamp of the last asset distribution from the controller contract into this contract
                  uint256 public lastDistributionTimestamp;
                  /* ------------- MODIFIERS ------------- */
                  /// @notice ensure input amount nonzero
                  modifier notZero(uint256 amount) {
                      if (amount == 0) revert InvalidAmount();
                      _;
                  }
                  /// @notice ensures blacklist target is not owner
                  modifier notOwner(address target) {
                      if (target == owner()) revert CantDenylistOwner();
                      _;
                  }
                  /// @notice ensure cooldownDuration is zero
                  modifier ensureCooldownOff() {
                      if (cooldownDuration != 0) revert OperationNotAllowed();
                      _;
                  }
                  /// @notice ensure cooldownDuration is gt 0
                  modifier ensureCooldownOn() {
                      if (cooldownDuration == 0) revert OperationNotAllowed();
                      _;
                  }
                  /* ------------- CONSTRUCTOR ------------- */
                  /**
                   * @notice Constructor for StakedlvlUSD contract.
                   * @param _asset The address of the lvlUSD token.
                   * @param _initialRewarder The address of the initial rewarder.
                   * @param _owner The address of the admin role.
                   *
                   */
                  constructor(
                      IERC20 _asset,
                      address _initialRewarder,
                      address _owner
                  ) ERC20("Staked lvlUSD", "slvlUSD") ERC4626(_asset) ERC20Permit("slvlUSD") {
                      if (
                          _owner == address(0) ||
                          _initialRewarder == address(0) ||
                          address(_asset) == address(0)
                      ) {
                          revert InvalidZeroAddress();
                      }
                      _grantRole(REWARDER_ROLE, _initialRewarder);
                      _grantRole(DEFAULT_ADMIN_ROLE, _owner);
                      silo = new slvlUSDSilo(address(this), address(_asset));
                      cooldownDuration = MAX_COOLDOWN_DURATION;
                  }
                  /* ------------- EXTERNAL ------------- */
                  /**
                   * @notice Allows the owner to transfer rewards from the controller contract into this contract.
                   * @param amount The amount of rewards to transfer.
                   */
                  function transferInRewards(
                      uint256 amount
                  ) external nonReentrant onlyRole(REWARDER_ROLE) notZero(amount) {
                      _updateVestingAmount(amount);
                      // transfer assets from rewarder to this contract
                      IERC20(asset()).safeTransferFrom(msg.sender, address(this), amount);
                      emit RewardsReceived(amount);
                  }
                  /**
                   * @notice Allows the owner (DEFAULT_ADMIN_ROLE) and blacklist managers to blacklist addresses.
                   * @param target The address to blacklist.
                   * @param isFullDenylisting Soft or full blacklisting level.
                   */
                  function addToDenylist(
                      address target,
                      bool isFullDenylisting
                  ) external onlyRole(DENYLIST_MANAGER_ROLE) notOwner(target) {
                      bytes32 role = isFullDenylisting
                          ? FULL_RESTRICTED_STAKER_ROLE
                          : SOFT_RESTRICTED_STAKER_ROLE;
                      _grantRole(role, target);
                  }
                  /**
                   * @notice Allows the owner (DEFAULT_ADMIN_ROLE) and blacklist managers to un-blacklist addresses.
                   * @param target The address to un-blacklist.
                   * @param isFullDenylisting Soft or full blacklisting level.
                   */
                  function removeFromDenylist(
                      address target,
                      bool isFullDenylisting
                  ) external onlyRole(DENYLIST_MANAGER_ROLE) {
                      bytes32 role = isFullDenylisting
                          ? FULL_RESTRICTED_STAKER_ROLE
                          : SOFT_RESTRICTED_STAKER_ROLE;
                      _revokeRole(role, target);
                  }
                  /**
                   * @notice Allows the owner to rescue tokens accidentally sent to the contract.
                   * Note that the owner cannot rescue lvlUSD tokens because they functionally sit here
                   * and belong to stakers but can rescue staked lvlUSD as they should never actually
                   * sit in this contract and a staker may well transfer them here by accident.
                   * @param token The token to be rescued.
                   * @param amount The amount of tokens to be rescued.
                   * @param to Where to send rescued tokens
                   */
                  function rescueTokens(
                      address token,
                      uint256 amount,
                      address to
                  ) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) {
                      if (address(token) == asset()) revert InvalidToken();
                      IERC20(token).safeTransfer(to, amount);
                  }
                  /**
                   * @dev Burns the full restricted user amount and mints to the desired owner address.
                   * @param from The address to burn the entire balance, with the FULL_RESTRICTED_STAKER_ROLE
                   * @param to The address to mint the entire balance of "from" parameter.
                   */
                  function redistributeLockedAmount(
                      address from,
                      address to
                  ) external nonReentrant onlyRole(DEFAULT_ADMIN_ROLE) {
                      if (
                          hasRole(FULL_RESTRICTED_STAKER_ROLE, from) &&
                          !hasRole(FULL_RESTRICTED_STAKER_ROLE, to)
                      ) {
                          uint256 amountToDistribute = balanceOf(from);
                          uint256 lvlUsdToVest = previewRedeem(amountToDistribute);
                          _burn(from, amountToDistribute);
                          // to address of address(0) enables burning
                          if (to == address(0)) {
                              _updateVestingAmount(lvlUsdToVest);
                          } else {
                              _mint(to, amountToDistribute);
                          }
                          emit LockedAmountRedistributed(from, to, amountToDistribute);
                      } else {
                          revert OperationNotAllowed();
                      }
                  }
                  /// @notice Claim the staking amount after the cooldown has finished. The address can only retire the full amount of assets.
                  /// @dev unstake can be called after cooldown have been set to 0, to let accounts to be able to claim remaining assets locked at Silo
                  /// @param receiver Address to send the assets by the staker
                  function unstake(address receiver) external {
                      UserCooldown storage userCooldown = cooldowns[msg.sender];
                      uint256 assets = userCooldown.underlyingAmount;
                      if (
                          block.timestamp >= userCooldown.cooldownEnd || cooldownDuration == 0
                      ) {
                          userCooldown.cooldownEnd = 0;
                          userCooldown.underlyingAmount = 0;
                          silo.withdraw(receiver, assets);
                      } else {
                          revert InvalidCooldown();
                      }
                  }
                  /// @notice redeem assets and starts a cooldown to claim the converted underlying asset
                  /// @param assets assets to redeem
                  function cooldownAssets(
                      uint256 assets
                  ) external ensureCooldownOn returns (uint256 shares) {
                      if (assets > maxWithdraw(msg.sender)) revert ExcessiveWithdrawAmount();
                      shares = previewWithdraw(assets);
                      cooldowns[msg.sender].cooldownEnd =
                          uint104(block.timestamp) +
                          cooldownDuration;
                      cooldowns[msg.sender].underlyingAmount += uint152(assets);
                      _withdraw(msg.sender, address(silo), msg.sender, assets, shares);
                  }
                  /// @notice redeem shares into assets and starts a cooldown to claim the converted underlying asset
                  /// @param shares shares to redeem
                  function cooldownShares(
                      uint256 shares
                  ) external ensureCooldownOn returns (uint256 assets) {
                      if (shares > maxRedeem(msg.sender)) revert ExcessiveRedeemAmount();
                      assets = previewRedeem(shares);
                      cooldowns[msg.sender].cooldownEnd =
                          uint104(block.timestamp) +
                          cooldownDuration;
                      cooldowns[msg.sender].underlyingAmount += uint152(assets);
                      _withdraw(msg.sender, address(silo), msg.sender, assets, shares);
                  }
                  /// @notice Set cooldown duration. If cooldown duration is set to zero, the StakedlvlUSDV2 behavior changes to follow ERC4626 standard and disables cooldownShares and cooldownAssets methods. If cooldown duration is greater than zero, the ERC4626 withdrawal and redeem functions are disabled, breaking the ERC4626 standard, and enabling the cooldownShares and the cooldownAssets functions.
                  /// @param duration Duration of the cooldown
                  function setCooldownDuration(
                      uint24 duration
                  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                      if (duration > MAX_COOLDOWN_DURATION) {
                          revert InvalidCooldown();
                      }
                      uint24 previousDuration = cooldownDuration;
                      cooldownDuration = duration;
                      emit CooldownDurationUpdated(previousDuration, cooldownDuration);
                  }
                  /* ------------- PUBLIC ------------- */
                  /**
                   * @notice Returns the amount of lvlUSD tokens that are vested in the contract.
                   */
                  function totalAssets() public view override returns (uint256) {
                      return IERC20(asset()).balanceOf(address(this)) - getUnvestedAmount();
                  }
                  /**
                   * @notice Returns the amount of lvlUSD tokens that are unvested in the contract.
                   */
                  function getUnvestedAmount() public view returns (uint256) {
                      uint256 timeSinceLastDistribution = block.timestamp -
                          lastDistributionTimestamp;
                      if (timeSinceLastDistribution >= VESTING_PERIOD) {
                          return 0;
                      }
                      uint256 deltaT;
                      unchecked {
                          deltaT = (VESTING_PERIOD - timeSinceLastDistribution);
                      }
                      return (deltaT * vestingAmount) / VESTING_PERIOD;
                  }
                  /// @dev Necessary because both ERC20 (from ERC20Permit) and ERC4626 declare decimals()
                  function decimals() public pure override(ERC4626, ERC20) returns (uint8) {
                      return 18;
                  }
                  /**
                   * @dev See {IERC4626-withdraw}.
                   */
                  function withdraw(
                      uint256 assets,
                      address receiver,
                      address _owner
                  ) public virtual override ensureCooldownOff returns (uint256) {
                      return super.withdraw(assets, receiver, _owner);
                  }
                  /**
                   * @dev See {IERC4626-redeem}.
                   */
                  function redeem(
                      uint256 shares,
                      address receiver,
                      address _owner
                  ) public virtual override ensureCooldownOff returns (uint256) {
                      return super.redeem(shares, receiver, _owner);
                  }
                  /* ------------- INTERNAL ------------- */
                  /// @notice ensures a small non-zero amount of shares does not remain, exposing to donation attack
                  function _checkMinShares() internal view {
                      uint256 _totalSupply = totalSupply();
                      if (_totalSupply > 0 && _totalSupply < MIN_SHARES)
                          revert MinSharesViolation();
                  }
                  /**
                   * @dev Deposit/mint common workflow.
                   * @param caller sender of assets
                   * @param receiver where to send shares
                   * @param assets assets to deposit
                   * @param shares shares to mint
                   */
                  function _deposit(
                      address caller,
                      address receiver,
                      uint256 assets,
                      uint256 shares
                  ) internal override nonReentrant notZero(assets) notZero(shares) {
                      if (
                          hasRole(SOFT_RESTRICTED_STAKER_ROLE, caller) ||
                          hasRole(SOFT_RESTRICTED_STAKER_ROLE, receiver)
                      ) {
                          revert OperationNotAllowed();
                      }
                      super._deposit(caller, receiver, assets, shares);
                      _checkMinShares();
                  }
                  /**
                   * @dev Withdraw/redeem common workflow.
                   * @param caller tx sender
                   * @param receiver where to send assets
                   * @param _owner where to burn shares from
                   * @param assets asset amount to transfer out
                   * @param shares shares to burn
                   */
                  function _withdraw(
                      address caller,
                      address receiver,
                      address _owner,
                      uint256 assets,
                      uint256 shares
                  ) internal override nonReentrant notZero(assets) notZero(shares) {
                      if (
                          hasRole(FULL_RESTRICTED_STAKER_ROLE, caller) ||
                          hasRole(FULL_RESTRICTED_STAKER_ROLE, receiver) ||
                          hasRole(FULL_RESTRICTED_STAKER_ROLE, _owner)
                      ) {
                          revert OperationNotAllowed();
                      }
                      super._withdraw(caller, receiver, _owner, assets, shares);
                      _checkMinShares();
                  }
                  function _updateVestingAmount(uint256 newVestingAmount) internal {
                      if (getUnvestedAmount() > 0) revert StillVesting();
                      vestingAmount = newVestingAmount;
                      lastDistributionTimestamp = block.timestamp;
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning. Disables transfers from or to of addresses with the FULL_RESTRICTED_STAKER_ROLE role.
                   */
                  function _beforeTokenTransfer(
                      address from,
                      address to,
                      uint256
                  ) internal virtual override {
                      if (hasRole(FULL_RESTRICTED_STAKER_ROLE, from) && to != address(0)) {
                          revert OperationNotAllowed();
                      }
                      if (hasRole(FULL_RESTRICTED_STAKER_ROLE, to)) {
                          revert OperationNotAllowed();
                      }
                  }
                  /**
                   * @dev Remove renounce role access from AccessControl, to prevent users to resign roles.
                   */
                  function renounceRole(bytes32, address) public virtual override {
                      revert OperationNotAllowed();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC4626.sol)
              pragma solidity ^0.8.0;
              import "../ERC20.sol";
              import "../utils/SafeERC20.sol";
              import "../../../interfaces/IERC4626.sol";
              import "../../../utils/math/Math.sol";
              /**
               * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
               * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
               *
               * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
               * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
               * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
               * contract and not the "assets" token which is an independent contract.
               *
               * [CAUTION]
               * ====
               * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
               * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
               * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
               * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
               * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
               * verifying the amount received is as expected, using a wrapper that performs these checks such as
               * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
               *
               * Since v4.9, this implementation uses virtual assets and shares to mitigate that risk. The `_decimalsOffset()`
               * corresponds to an offset in the decimal representation between the underlying asset's decimals and the vault
               * decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which itself
               * determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default offset
               * (0) makes it non-profitable, as a result of the value being captured by the virtual shares (out of the attacker's
               * donation) matching the attacker's expected gains. With a larger offset, the attack becomes orders of magnitude more
               * expensive than it is profitable. More details about the underlying math can be found
               * xref:erc4626.adoc#inflation-attack[here].
               *
               * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
               * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
               * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
               * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
               * `_convertToShares` and `_convertToAssets` functions.
               *
               * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
               * ====
               *
               * _Available since v4.7._
               */
              abstract contract ERC4626 is ERC20, IERC4626 {
                  using Math for uint256;
                  IERC20 private immutable _asset;
                  uint8 private immutable _underlyingDecimals;
                  /**
                   * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
                   */
                  constructor(IERC20 asset_) {
                      (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
                      _underlyingDecimals = success ? assetDecimals : 18;
                      _asset = asset_;
                  }
                  /**
                   * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
                   */
                  function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool, uint8) {
                      (bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
                          abi.encodeWithSelector(IERC20Metadata.decimals.selector)
                      );
                      if (success && encodedDecimals.length >= 32) {
                          uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
                          if (returnedDecimals <= type(uint8).max) {
                              return (true, uint8(returnedDecimals));
                          }
                      }
                      return (false, 0);
                  }
                  /**
                   * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
                   * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
                   * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
                   *
                   * See {IERC20Metadata-decimals}.
                   */
                  function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
                      return _underlyingDecimals + _decimalsOffset();
                  }
                  /** @dev See {IERC4626-asset}. */
                  function asset() public view virtual override returns (address) {
                      return address(_asset);
                  }
                  /** @dev See {IERC4626-totalAssets}. */
                  function totalAssets() public view virtual override returns (uint256) {
                      return _asset.balanceOf(address(this));
                  }
                  /** @dev See {IERC4626-convertToShares}. */
                  function convertToShares(uint256 assets) public view virtual override returns (uint256) {
                      return _convertToShares(assets, Math.Rounding.Down);
                  }
                  /** @dev See {IERC4626-convertToAssets}. */
                  function convertToAssets(uint256 shares) public view virtual override returns (uint256) {
                      return _convertToAssets(shares, Math.Rounding.Down);
                  }
                  /** @dev See {IERC4626-maxDeposit}. */
                  function maxDeposit(address) public view virtual override returns (uint256) {
                      return type(uint256).max;
                  }
                  /** @dev See {IERC4626-maxMint}. */
                  function maxMint(address) public view virtual override returns (uint256) {
                      return type(uint256).max;
                  }
                  /** @dev See {IERC4626-maxWithdraw}. */
                  function maxWithdraw(address owner) public view virtual override returns (uint256) {
                      return _convertToAssets(balanceOf(owner), Math.Rounding.Down);
                  }
                  /** @dev See {IERC4626-maxRedeem}. */
                  function maxRedeem(address owner) public view virtual override returns (uint256) {
                      return balanceOf(owner);
                  }
                  /** @dev See {IERC4626-previewDeposit}. */
                  function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
                      return _convertToShares(assets, Math.Rounding.Down);
                  }
                  /** @dev See {IERC4626-previewMint}. */
                  function previewMint(uint256 shares) public view virtual override returns (uint256) {
                      return _convertToAssets(shares, Math.Rounding.Up);
                  }
                  /** @dev See {IERC4626-previewWithdraw}. */
                  function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
                      return _convertToShares(assets, Math.Rounding.Up);
                  }
                  /** @dev See {IERC4626-previewRedeem}. */
                  function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
                      return _convertToAssets(shares, Math.Rounding.Down);
                  }
                  /** @dev See {IERC4626-deposit}. */
                  function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {
                      require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");
                      uint256 shares = previewDeposit(assets);
                      _deposit(_msgSender(), receiver, assets, shares);
                      return shares;
                  }
                  /** @dev See {IERC4626-mint}.
                   *
                   * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
                   * In this case, the shares will be minted without requiring any assets to be deposited.
                   */
                  function mint(uint256 shares, address receiver) public virtual override returns (uint256) {
                      require(shares <= maxMint(receiver), "ERC4626: mint more than max");
                      uint256 assets = previewMint(shares);
                      _deposit(_msgSender(), receiver, assets, shares);
                      return assets;
                  }
                  /** @dev See {IERC4626-withdraw}. */
                  function withdraw(uint256 assets, address receiver, address owner) public virtual override returns (uint256) {
                      require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");
                      uint256 shares = previewWithdraw(assets);
                      _withdraw(_msgSender(), receiver, owner, assets, shares);
                      return shares;
                  }
                  /** @dev See {IERC4626-redeem}. */
                  function redeem(uint256 shares, address receiver, address owner) public virtual override returns (uint256) {
                      require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");
                      uint256 assets = previewRedeem(shares);
                      _withdraw(_msgSender(), receiver, owner, assets, shares);
                      return assets;
                  }
                  /**
                   * @dev Internal conversion function (from assets to shares) with support for rounding direction.
                   */
                  function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
                      return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
                  }
                  /**
                   * @dev Internal conversion function (from shares to assets) with support for rounding direction.
                   */
                  function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
                      return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
                  }
                  /**
                   * @dev Deposit/mint common workflow.
                   */
                  function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {
                      // If _asset is ERC777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
                      // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
                      // calls the vault, which is assumed not malicious.
                      //
                      // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
                      // assets are transferred and before the shares are minted, which is a valid state.
                      // slither-disable-next-line reentrancy-no-eth
                      SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
                      _mint(receiver, shares);
                      emit Deposit(caller, receiver, assets, shares);
                  }
                  /**
                   * @dev Withdraw/redeem common workflow.
                   */
                  function _withdraw(
                      address caller,
                      address receiver,
                      address owner,
                      uint256 assets,
                      uint256 shares
                  ) internal virtual {
                      if (caller != owner) {
                          _spendAllowance(owner, caller, shares);
                      }
                      // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
                      // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
                      // calls the vault, which is assumed not malicious.
                      //
                      // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
                      // shares are burned and after the assets are transferred, which is a valid state.
                      _burn(owner, shares);
                      SafeERC20.safeTransfer(_asset, receiver, assets);
                      emit Withdraw(caller, receiver, owner, assets, shares);
                  }
                  function _decimalsOffset() internal view virtual returns (uint8) {
                      return 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
              pragma solidity ^0.8.0;
              import "../IERC20.sol";
              import "../extensions/IERC20Permit.sol";
              import "../../../utils/Address.sol";
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using Address for address;
                  /**
                   * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful.
                   */
                  function safeTransfer(IERC20 token, address to, uint256 value) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
                  /**
                   * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                   * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                   */
                  function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                  }
                  /**
                   * @dev Deprecated. This function has issues similar to the ones found in
                   * {IERC20-approve}, and its usage is discouraged.
                   *
                   * Whenever possible, use {safeIncreaseAllowance} and
                   * {safeDecreaseAllowance} instead.
                   */
                  function safeApprove(IERC20 token, address spender, uint256 value) internal {
                      // safeApprove should only be called when setting an initial allowance,
                      // or when resetting it to zero. To increase and decrease it, use
                      // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                      require(
                          (value == 0) || (token.allowance(address(this), spender) == 0),
                          "SafeERC20: approve from non-zero to non-zero allowance"
                      );
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                  }
                  /**
                   * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful.
                   */
                  function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                      uint256 oldAllowance = token.allowance(address(this), spender);
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                  }
                  /**
                   * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful.
                   */
                  function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                      unchecked {
                          uint256 oldAllowance = token.allowance(address(this), spender);
                          require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                      }
                  }
                  /**
                   * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                   * to be set to zero before setting it to a non-zero value, such as USDT.
                   */
                  function forceApprove(IERC20 token, address spender, uint256 value) internal {
                      bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                      if (!_callOptionalReturnBool(token, approvalCall)) {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                          _callOptionalReturn(token, approvalCall);
                      }
                  }
                  /**
                   * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                   * Revert on invalid signature.
                   */
                  function safePermit(
                      IERC20Permit token,
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal {
                      uint256 nonceBefore = token.nonces(owner);
                      token.permit(owner, spender, value, deadline, v, r, s);
                      uint256 nonceAfter = token.nonces(owner);
                      require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                  }
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                      // the target address contains contract code and also asserts for success in the low-level call.
                      bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                      require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   *
                   * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                   */
                  function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                      // and not revert is the subcall reverts.
                      (bool success, bytes memory returndata) = address(token).call(data);
                      return
                          success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Contract module that helps prevent reentrant calls to a function.
               *
               * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
               * available, which can be applied to functions to make sure there are no nested
               * (reentrant) calls to them.
               *
               * Note that because there is a single `nonReentrant` guard, functions marked as
               * `nonReentrant` may not call one another. This can be worked around by making
               * those functions `private`, and then adding `external` `nonReentrant` entry
               * points to them.
               *
               * TIP: If you would like to learn more about reentrancy and alternative ways
               * to protect against it, check out our blog post
               * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
               */
              abstract contract ReentrancyGuard {
                  // Booleans are more expensive than uint256 or any type that takes up a full
                  // word because each write operation emits an extra SLOAD to first read the
                  // slot's contents, replace the bits taken up by the boolean, and then write
                  // back. This is the compiler's defense against contract upgrades and
                  // pointer aliasing, and it cannot be disabled.
                  // The values being non-zero value makes deployment a bit more expensive,
                  // but in exchange the refund on every call to nonReentrant will be lower in
                  // amount. Since refunds are capped to a percentage of the total
                  // transaction's gas, it is best to keep them low in cases like this one, to
                  // increase the likelihood of the full refund coming into effect.
                  uint256 private constant _NOT_ENTERED = 1;
                  uint256 private constant _ENTERED = 2;
                  uint256 private _status;
                  constructor() {
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Prevents a contract from calling itself, directly or indirectly.
                   * Calling a `nonReentrant` function from another `nonReentrant`
                   * function is not supported. It is possible to prevent this from happening
                   * by making the `nonReentrant` function external, and making it call a
                   * `private` function that does the actual work.
                   */
                  modifier nonReentrant() {
                      _nonReentrantBefore();
                      _;
                      _nonReentrantAfter();
                  }
                  function _nonReentrantBefore() private {
                      // On the first call to nonReentrant, _status will be _NOT_ENTERED
                      require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                      // Any calls to nonReentrant after this point will fail
                      _status = _ENTERED;
                  }
                  function _nonReentrantAfter() private {
                      // By storing the original value once again, a refund is triggered (see
                      // https://eips.ethereum.org/EIPS/eip-2200)
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
                   * `nonReentrant` function in the call stack.
                   */
                  function _reentrancyGuardEntered() internal view returns (bool) {
                      return _status == _ENTERED;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-ERC20Permit.sol)
              pragma solidity ^0.8.0;
              // EIP-2612 is Final as of 2022-11-01. This file is deprecated.
              import "./ERC20Permit.sol";
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity >=0.8.19;
              import "@openzeppelin-4.9.0/contracts/access/AccessControl.sol";
              import "@openzeppelin-4.9.0/contracts/interfaces/IERC5313.sol";
              import "../../interfaces/ISingleAdminAccessControl.sol";
              /**
               * @title SingleAdminAccessControl
               * @notice SingleAdminAccessControl is a contract that provides a single admin role
               * @notice This contract is a simplified alternative to OpenZeppelin's AccessControlDefaultAdminRules
               * @dev Changelog: update solidity versions
               */
              abstract contract SingleAdminAccessControl is
                  IERC5313,
                  ISingleAdminAccessControl,
                  AccessControl
              {
                  address private _currentDefaultAdmin;
                  address private _pendingDefaultAdmin;
                  modifier notAdmin(bytes32 role) {
                      if (role == DEFAULT_ADMIN_ROLE) revert InvalidAdminChange();
                      _;
                  }
                  /// @notice Transfer the admin role to a new address
                  /// @notice This can ONLY be executed by the current admin
                  /// @param newAdmin address
                  function transferAdmin(
                      address newAdmin
                  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                      if (newAdmin == msg.sender) revert InvalidAdminChange();
                      _pendingDefaultAdmin = newAdmin;
                      emit AdminTransferRequested(_currentDefaultAdmin, newAdmin);
                  }
                  function acceptAdmin() external {
                      if (msg.sender != _pendingDefaultAdmin) revert NotPendingAdmin();
                      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
                  }
                  /// @notice grant a role
                  /// @notice can only be executed by the current single admin
                  /// @notice admin role cannot be granted externally
                  /// @param role bytes32
                  /// @param account address
                  function grantRole(
                      bytes32 role,
                      address account
                  ) public override onlyRole(DEFAULT_ADMIN_ROLE) notAdmin(role) {
                      _grantRole(role, account);
                  }
                  /// @notice revoke a role
                  /// @notice can only be executed by the current admin
                  /// @notice admin role cannot be revoked
                  /// @param role bytes32
                  /// @param account address
                  function revokeRole(
                      bytes32 role,
                      address account
                  ) public override onlyRole(DEFAULT_ADMIN_ROLE) notAdmin(role) {
                      _revokeRole(role, account);
                  }
                  /// @notice renounce the role of msg.sender
                  /// @notice admin role cannot be renounced
                  /// @param role bytes32
                  /// @param account address
                  function renounceRole(
                      bytes32 role,
                      address account
                  ) public virtual override notAdmin(role) {
                      super.renounceRole(role, account);
                  }
                  /**
                   * @dev See {IERC5313-owner}.
                   */
                  function owner() public view virtual returns (address) {
                      return _currentDefaultAdmin;
                  }
                  /**
                   * @notice no way to change admin without removing old admin first
                   */
                  function _grantRole(bytes32 role, address account) internal override {
                      if (role == DEFAULT_ADMIN_ROLE) {
                          emit AdminTransferred(_currentDefaultAdmin, account);
                          _revokeRole(DEFAULT_ADMIN_ROLE, _currentDefaultAdmin);
                          _currentDefaultAdmin = account;
                          delete _pendingDefaultAdmin;
                      }
                      super._grantRole(role, account);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.20;
              import "./IStakedlvlUSD.sol";
              struct UserCooldown {
                  uint104 cooldownEnd;
                  uint256 underlyingAmount;
              }
              interface IStakedlvlUSDCooldown is IStakedlvlUSD {
                  // Events //
                  /// @notice Event emitted when cooldown duration updates
                  event CooldownDurationUpdated(uint24 previousDuration, uint24 newDuration);
                  /// @notice Event emitted when the silo address updates
                  event SiloUpdated(address previousSilo, address newSilo);
                  // Errors //
                  /// @notice Error emitted when the shares amount to redeem is greater than the shares balance of the owner
                  error ExcessiveRedeemAmount();
                  /// @notice Error emitted when the shares amount to withdraw is greater than the shares balance of the owner
                  error ExcessiveWithdrawAmount();
                  /// @notice Error emitted when cooldown value is invalid
                  error InvalidCooldown();
                  function cooldownAssets(uint256 assets) external returns (uint256 shares);
                  function cooldownShares(uint256 shares) external returns (uint256 assets);
                  function unstake(address receiver) external;
                  function setCooldownDuration(uint24 duration) external;
              }
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity ^0.8.0;
              /* solhint-disable var-name-mixedcase  */
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import "./interfaces/IslvlUSDSiloDefinitions.sol";
              /**
               * @title slvlUSDSilo
               * @notice The Silo allows to store lvlUSD during the stake cooldown process.
               *         Forked from Ethena's USDeSilo contract.
               */
              contract slvlUSDSilo is IslvlUSDSiloDefinitions {
                  address immutable _STAKING_VAULT;
                  IERC20 immutable _lvlUSD;
                  constructor(address stakingVault, address lvlUSD) {
                      _STAKING_VAULT = stakingVault;
                      _lvlUSD = IERC20(lvlUSD);
                  }
                  modifier onlyStakingVault() {
                      if (msg.sender != _STAKING_VAULT) revert OnlyStakingVault();
                      _;
                  }
                  function withdraw(address to, uint256 amount) external onlyStakingVault {
                      _lvlUSD.transfer(to, amount);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "./IERC20.sol";
              import "./extensions/IERC20Metadata.sol";
              import "../../utils/Context.sol";
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               * For a generic mechanism see {ERC20PresetMinterPauser}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * The default value of {decimals} is 18. To change this, you should override
               * this function so it returns a different value.
               *
               * We have followed general OpenZeppelin Contracts guidelines: functions revert
               * instead returning `false` on failure. This behavior is nonetheless
               * conventional and does not conflict with the expectations of ERC20
               * applications.
               *
               * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
               * This allows applications to reconstruct the allowance for all accounts just
               * by listening to said events. Other implementations of the EIP may not emit
               * these events, as it isn't required by the specification.
               *
               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
               * functions have been added to mitigate the well-known issues around setting
               * allowances. See {IERC20-approve}.
               */
              contract ERC20 is Context, IERC20, IERC20Metadata {
                  mapping(address => uint256) private _balances;
                  mapping(address => mapping(address => uint256)) private _allowances;
                  uint256 private _totalSupply;
                  string private _name;
                  string private _symbol;
                  /**
                   * @dev Sets the values for {name} and {symbol}.
                   *
                   * All two of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev Returns the number of decimals used to get its user representation.
                   * For example, if `decimals` equals `2`, a balance of `505` tokens should
                   * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                   *
                   * Tokens usually opt for a value of 18, imitating the relationship between
                   * Ether and Wei. This is the default value returned by this function, unless
                   * it's overridden.
                   *
                   * NOTE: This information is only used for _display_ purposes: it in
                   * no way affects any of the arithmetic of the contract, including
                   * {IERC20-balanceOf} and {IERC20-transfer}.
                   */
                  function decimals() public view virtual override returns (uint8) {
                      return 18;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual override returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address to, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Emits an {Approval} event indicating the updated allowance. This is not
                   * required by the EIP. See the note at the beginning of {ERC20}.
                   *
                   * NOTE: Does not update the allowance if the current allowance
                   * is the maximum `uint256`.
                   *
                   * Requirements:
                   *
                   * - `from` and `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``from``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
                      address spender = _msgSender();
                      _spendAllowance(from, spender, amount);
                      _transfer(from, to, amount);
                      return true;
                  }
                  /**
                   * @dev Atomically increases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, allowance(owner, spender) + addedValue);
                      return true;
                  }
                  /**
                   * @dev Atomically decreases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `spender` must have allowance for the caller of at least
                   * `subtractedValue`.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      uint256 currentAllowance = allowance(owner, spender);
                      require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                      unchecked {
                          _approve(owner, spender, currentAllowance - subtractedValue);
                      }
                      return true;
                  }
                  /**
                   * @dev Moves `amount` of tokens from `from` to `to`.
                   *
                   * This internal function is equivalent to {transfer}, and can be used to
                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                   *
                   * Emits a {Transfer} event.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   */
                  function _transfer(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), "ERC20: transfer from the zero address");
                      require(to != address(0), "ERC20: transfer to the zero address");
                      _beforeTokenTransfer(from, to, amount);
                      uint256 fromBalance = _balances[from];
                      require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                      unchecked {
                          _balances[from] = fromBalance - amount;
                          // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                          // decrementing then incrementing.
                          _balances[to] += amount;
                      }
                      emit Transfer(from, to, amount);
                      _afterTokenTransfer(from, to, amount);
                  }
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
                      _beforeTokenTransfer(address(0), account, amount);
                      _totalSupply += amount;
                      unchecked {
                          // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                          _balances[account] += amount;
                      }
                      emit Transfer(address(0), account, amount);
                      _afterTokenTransfer(address(0), account, amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
                      _beforeTokenTransfer(account, address(0), amount);
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                      unchecked {
                          _balances[account] = accountBalance - amount;
                          // Overflow not possible: amount <= accountBalance <= totalSupply.
                          _totalSupply -= amount;
                      }
                      emit Transfer(account, address(0), amount);
                      _afterTokenTransfer(account, address(0), amount);
                  }
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                   *
                   * This internal function is equivalent to `approve`, and can be used to
                   * e.g. set automatic allowances for certain subsystems, etc.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `owner` cannot be the zero address.
                   * - `spender` cannot be the zero address.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      require(owner != address(0), "ERC20: approve from the zero address");
                      require(spender != address(0), "ERC20: approve to the zero address");
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                  }
                  /**
                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                   *
                   * Does not update the allowance amount in case of infinite allowance.
                   * Revert if not enough allowance is available.
                   *
                   * Might emit an {Approval} event.
                   */
                  function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                      uint256 currentAllowance = allowance(owner, spender);
                      if (currentAllowance != type(uint256).max) {
                          require(currentAllowance >= amount, "ERC20: insufficient allowance");
                          unchecked {
                              _approve(owner, spender, currentAllowance - amount);
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  /**
                   * @dev Hook that is called after any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * has been transferred to `to`.
                   * - when `from` is zero, `amount` tokens have been minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol)
              pragma solidity ^0.8.0;
              import "../token/ERC20/IERC20.sol";
              import "../token/ERC20/extensions/IERC20Metadata.sol";
              /**
               * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
               * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
               *
               * _Available since v4.7._
               */
              interface IERC4626 is IERC20, IERC20Metadata {
                  event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
                  event Withdraw(
                      address indexed sender,
                      address indexed receiver,
                      address indexed owner,
                      uint256 assets,
                      uint256 shares
                  );
                  /**
                   * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
                   *
                   * - MUST be an ERC-20 token contract.
                   * - MUST NOT revert.
                   */
                  function asset() external view returns (address assetTokenAddress);
                  /**
                   * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
                   *
                   * - SHOULD include any compounding that occurs from yield.
                   * - MUST be inclusive of any fees that are charged against assets in the Vault.
                   * - MUST NOT revert.
                   */
                  function totalAssets() external view returns (uint256 totalManagedAssets);
                  /**
                   * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
                   * scenario where all the conditions are met.
                   *
                   * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
                   * - MUST NOT show any variations depending on the caller.
                   * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
                   * - MUST NOT revert.
                   *
                   * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
                   * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
                   * from.
                   */
                  function convertToShares(uint256 assets) external view returns (uint256 shares);
                  /**
                   * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
                   * scenario where all the conditions are met.
                   *
                   * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
                   * - MUST NOT show any variations depending on the caller.
                   * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
                   * - MUST NOT revert.
                   *
                   * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
                   * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
                   * from.
                   */
                  function convertToAssets(uint256 shares) external view returns (uint256 assets);
                  /**
                   * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
                   * through a deposit call.
                   *
                   * - MUST return a limited value if receiver is subject to some deposit limit.
                   * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
                   * - MUST NOT revert.
                   */
                  function maxDeposit(address receiver) external view returns (uint256 maxAssets);
                  /**
                   * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
                   * current on-chain conditions.
                   *
                   * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
                   *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
                   *   in the same transaction.
                   * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
                   *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
                   * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
                   * - MUST NOT revert.
                   *
                   * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
                   * share price or some other type of condition, meaning the depositor will lose assets by depositing.
                   */
                  function previewDeposit(uint256 assets) external view returns (uint256 shares);
                  /**
                   * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
                   *
                   * - MUST emit the Deposit event.
                   * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
                   *   deposit execution, and are accounted for during deposit.
                   * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
                   *   approving enough underlying tokens to the Vault contract, etc).
                   *
                   * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
                   */
                  function deposit(uint256 assets, address receiver) external returns (uint256 shares);
                  /**
                   * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
                   * - MUST return a limited value if receiver is subject to some mint limit.
                   * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
                   * - MUST NOT revert.
                   */
                  function maxMint(address receiver) external view returns (uint256 maxShares);
                  /**
                   * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
                   * current on-chain conditions.
                   *
                   * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
                   *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
                   *   same transaction.
                   * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
                   *   would be accepted, regardless if the user has enough tokens approved, etc.
                   * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
                   * - MUST NOT revert.
                   *
                   * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
                   * share price or some other type of condition, meaning the depositor will lose assets by minting.
                   */
                  function previewMint(uint256 shares) external view returns (uint256 assets);
                  /**
                   * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
                   *
                   * - MUST emit the Deposit event.
                   * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
                   *   execution, and are accounted for during mint.
                   * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
                   *   approving enough underlying tokens to the Vault contract, etc).
                   *
                   * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
                   */
                  function mint(uint256 shares, address receiver) external returns (uint256 assets);
                  /**
                   * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
                   * Vault, through a withdraw call.
                   *
                   * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
                   * - MUST NOT revert.
                   */
                  function maxWithdraw(address owner) external view returns (uint256 maxAssets);
                  /**
                   * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
                   * given current on-chain conditions.
                   *
                   * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
                   *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
                   *   called
                   *   in the same transaction.
                   * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
                   *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
                   * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
                   * - MUST NOT revert.
                   *
                   * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
                   * share price or some other type of condition, meaning the depositor will lose assets by depositing.
                   */
                  function previewWithdraw(uint256 assets) external view returns (uint256 shares);
                  /**
                   * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
                   *
                   * - MUST emit the Withdraw event.
                   * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
                   *   withdraw execution, and are accounted for during withdraw.
                   * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
                   *   not having enough shares, etc).
                   *
                   * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
                   * Those methods should be performed separately.
                   */
                  function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
                  /**
                   * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
                   * through a redeem call.
                   *
                   * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
                   * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
                   * - MUST NOT revert.
                   */
                  function maxRedeem(address owner) external view returns (uint256 maxShares);
                  /**
                   * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
                   * given current on-chain conditions.
                   *
                   * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
                   *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
                   *   same transaction.
                   * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
                   *   redemption would be accepted, regardless if the user has enough shares, etc.
                   * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
                   * - MUST NOT revert.
                   *
                   * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
                   * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
                   */
                  function previewRedeem(uint256 shares) external view returns (uint256 assets);
                  /**
                   * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
                   *
                   * - MUST emit the Withdraw event.
                   * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
                   *   redeem execution, and are accounted for during redeem.
                   * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
                   *   not having enough shares, etc).
                   *
                   * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
                   * Those methods should be performed separately.
                   */
                  function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  enum Rounding {
                      Down, // Toward negative infinity
                      Up, // Toward infinity
                      Zero // Toward zero
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds up instead
                   * of rounding down.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                   * with further edits by Uniswap Labs also under MIT license.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod0 := mul(x, y)
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          require(denominator > prod1, "Math: mulDiv overflow");
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                          // See https://cs.stackexchange.com/q/138556/92363.
                          // Does not overflow because the denominator cannot be zero at this stage in the function.
                          uint256 twos = denominator & (~denominator + 1);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                          // in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 amount) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                   * @dev Moves `amount` tokens from `from` to `to` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 amount) external returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               *
               * ==== Security Considerations
               *
               * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
               * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
               * considered as an intention to spend the allowance in any specific way. The second is that because permits have
               * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
               * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
               * generally recommended is:
               *
               * ```solidity
               * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
               *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
               *     doThing(..., value);
               * }
               *
               * function doThing(..., uint256 value) public {
               *     token.safeTransferFrom(msg.sender, address(this), value);
               *     ...
               * }
               * ```
               *
               * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
               * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
               * {SafeERC20-safeTransferFrom}).
               *
               * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
               * contracts should have entry points that don't rely on permit.
               */
              interface IERC20Permit {
                  /**
                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                   * given ``owner``'s signed approval.
                   *
                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                   * ordering also apply here.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `deadline` must be a timestamp in the future.
                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                   * over the EIP712-formatted function arguments.
                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                   *
                   * For more information on the signature format, see the
                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                   * section].
                   *
                   * CAUTION: See Security Considerations above.
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
                  /**
                   * @dev Returns the current nonce for `owner`. This value must be
                   * included whenever a signature is generated for {permit}.
                   *
                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                   * prevents a signature from being used multiple times.
                   */
                  function nonces(address owner) external view returns (uint256);
                  /**
                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/ERC20Permit.sol)
              pragma solidity ^0.8.0;
              import "./IERC20Permit.sol";
              import "../ERC20.sol";
              import "../../../utils/cryptography/ECDSA.sol";
              import "../../../utils/cryptography/EIP712.sol";
              import "../../../utils/Counters.sol";
              /**
               * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               *
               * _Available since v3.4._
               */
              abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
                  using Counters for Counters.Counter;
                  mapping(address => Counters.Counter) private _nonces;
                  // solhint-disable-next-line var-name-mixedcase
                  bytes32 private constant _PERMIT_TYPEHASH =
                      keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                  /**
                   * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
                   * However, to ensure consistency with the upgradeable transpiler, we will continue
                   * to reserve a slot.
                   * @custom:oz-renamed-from _PERMIT_TYPEHASH
                   */
                  // solhint-disable-next-line var-name-mixedcase
                  bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
                  /**
                   * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
                   *
                   * It's a good idea to use the same `name` that is defined as the ERC20 token name.
                   */
                  constructor(string memory name) EIP712(name, "1") {}
                  /**
                   * @inheritdoc IERC20Permit
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) public virtual override {
                      require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
                      bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
                      bytes32 hash = _hashTypedDataV4(structHash);
                      address signer = ECDSA.recover(hash, v, r, s);
                      require(signer == owner, "ERC20Permit: invalid signature");
                      _approve(owner, spender, value);
                  }
                  /**
                   * @inheritdoc IERC20Permit
                   */
                  function nonces(address owner) public view virtual override returns (uint256) {
                      return _nonces[owner].current();
                  }
                  /**
                   * @inheritdoc IERC20Permit
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view override returns (bytes32) {
                      return _domainSeparatorV4();
                  }
                  /**
                   * @dev "Consume a nonce": return the current value and increment.
                   *
                   * _Available since v4.1._
                   */
                  function _useNonce(address owner) internal virtual returns (uint256 current) {
                      Counters.Counter storage nonce = _nonces[owner];
                      current = nonce.current();
                      nonce.increment();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControl.sol";
              import "../utils/Context.sol";
              import "../utils/Strings.sol";
              import "../utils/introspection/ERC165.sol";
              /**
               * @dev Contract module that allows children to implement role-based access
               * control mechanisms. This is a lightweight version that doesn't allow enumerating role
               * members except through off-chain means by accessing the contract event logs. Some
               * applications may benefit from on-chain enumerability, for those cases see
               * {AccessControlEnumerable}.
               *
               * Roles are referred to by their `bytes32` identifier. These should be exposed
               * in the external API and be unique. The best way to achieve this is by
               * using `public constant` hash digests:
               *
               * ```solidity
               * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
               * ```
               *
               * Roles can be used to represent a set of permissions. To restrict access to a
               * function call, use {hasRole}:
               *
               * ```solidity
               * function foo() public {
               *     require(hasRole(MY_ROLE, msg.sender));
               *     ...
               * }
               * ```
               *
               * Roles can be granted and revoked dynamically via the {grantRole} and
               * {revokeRole} functions. Each role has an associated admin role, and only
               * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
               *
               * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
               * that only accounts with this role will be able to grant or revoke other
               * roles. More complex role relationships can be created by using
               * {_setRoleAdmin}.
               *
               * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
               * grant and revoke this role. Extra precautions should be taken to secure
               * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
               * to enforce additional security measures for this role.
               */
              abstract contract AccessControl is Context, IAccessControl, ERC165 {
                  struct RoleData {
                      mapping(address => bool) members;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with a standardized message including the required role.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   *
                   * _Available since v4.1._
                   */
                  modifier onlyRole(bytes32 role) {
                      _checkRole(role);
                      _;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                      return _roles[role].members[account];
                  }
                  /**
                   * @dev Revert with a standard message if `_msgSender()` is missing `role`.
                   * Overriding this function changes the behavior of the {onlyRole} modifier.
                   *
                   * Format of the revert message is described in {_checkRole}.
                   *
                   * _Available since v4.6._
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Revert with a standard message if `account` is missing `role`.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "AccessControl: account ",
                                      Strings.toHexString(account),
                                      " is missing role ",
                                      Strings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                      return _roles[role].adminRole;
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been revoked `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address account) public virtual override {
                      require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event. Note that unlike {grantRole}, this function doesn't perform any
                   * checks on the calling account.
                   *
                   * May emit a {RoleGranted} event.
                   *
                   * [WARNING]
                   * ====
                   * This function should only be called from the constructor when setting
                   * up the initial roles for the system.
                   *
                   * Using this function in any other way is effectively circumventing the admin
                   * system imposed by {AccessControl}.
                   * ====
                   *
                   * NOTE: This function is deprecated in favor of {_grantRole}.
                   */
                  function _setupRole(bytes32 role, address account) internal virtual {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Sets `adminRole` as ``role``'s admin role.
                   *
                   * Emits a {RoleAdminChanged} event.
                   */
                  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                      bytes32 previousAdminRole = getRoleAdmin(role);
                      _roles[role].adminRole = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual {
                      if (!hasRole(role, account)) {
                          _roles[role].members[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                      }
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      if (hasRole(role, account)) {
                          _roles[role].members[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5313.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface for the Light Contract Ownership Standard.
               *
               * A standardized minimal interface required to identify an account that controls a contract
               *
               * _Available since v4.9._
               */
              interface IERC5313 {
                  /**
                   * @dev Gets the address of the owner.
                   */
                  function owner() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.19;
              interface ISingleAdminAccessControl {
                  error InvalidAdminChange();
                  error NotPendingAdmin();
                  event AdminTransferred(address indexed oldAdmin, address indexed newAdmin);
                  event AdminTransferRequested(
                      address indexed oldAdmin,
                      address indexed newAdmin
                  );
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.20;
              interface IStakedlvlUSD {
                  // Events //
                  /// @notice Event emitted when the rewards are received
                  event RewardsReceived(uint256 indexed amount);
                  /// @notice Event emitted when frozen funds are received
                  event FrozenFundsReceived(uint256 indexed amount);
                  /// @notice Event emitted when the balance from an FULL_RESTRICTED_STAKER_ROLE user are redistributed
                  event LockedAmountRedistributed(
                      address indexed from,
                      address indexed to,
                      uint256 amount
                  );
                  /// @notice Event emitted when a FREEZER_ROLE user freezes an amount of the reserve
                  event FrozenAmountUpdated(uint256 amount);
                  event FrozenAmountWithdrawn(address indexed frozenReceiver, uint256 amount);
                  event FrozenReceiverSet(
                      address indexed oldReceiver,
                      address indexed newReceiver
                  );
                  event FrozenReceiverSettingRenounced();
                  event FreezablePercentageUpdated(
                      uint16 oldFreezablePercentage,
                      uint16 newFreezablePercentage
                  );
                  // Errors //
                  /// @notice Error emitted shares or assets equal zero.
                  error InvalidAmount();
                  /// @notice Error emitted when owner attempts to rescue lvlUSD tokens.
                  error InvalidToken();
                  /// @notice Error emitted when slippage is exceeded on a deposit or withdrawal
                  error SlippageExceeded();
                  /// @notice Error emitted when a small non-zero share amount remains, which risks donations attack
                  error MinSharesViolation();
                  /// @notice Error emitted when owner is not allowed to perform an operation
                  error OperationNotAllowed();
                  /// @notice Error emitted when there is still unvested amount
                  error StillVesting();
                  /// @notice Error emitted when owner or denylist manager attempts to denylist owner
                  error CantDenylistOwner();
                  /// @notice Error emitted when the zero address is given
                  error InvalidZeroAddress();
                  /// @notice Error emitted when there is not enough balance
                  error InsufficientBalance();
                  /// @notice Error emitted when the caller cannot set a freezer
                  error SettingFrozenReceiverDisabled();
                  /// @notice Error emitted when trying to freeze more than max freezable
                  error ExceedsFreezable();
                  function transferInRewards(uint256 amount) external;
                  function rescueTokens(address token, uint256 amount, address to) external;
                  function getUnvestedAmount() external view returns (uint256);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the value of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the value of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
                  /**
                   * @dev Moves a `value` amount of tokens from `from` to `to` using the
                   * allowance mechanism. `value` 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 value) external returns (bool);
              }
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity >=0.8.19;
              interface IslvlUSDSiloDefinitions {
                  /// @notice Error emitted when the staking vault is not the caller
                  error OnlyStakingVault();
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC20.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC20 standard.
               *
               * _Available since v4.1._
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
                  function _contextSuffixLength() internal view virtual returns (uint256) {
                      return 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
              pragma solidity ^0.8.0;
              import "../Strings.sol";
              /**
               * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
               *
               * These functions can be used to verify that a message was signed by the holder
               * of the private keys of a given address.
               */
              library ECDSA {
                  enum RecoverError {
                      NoError,
                      InvalidSignature,
                      InvalidSignatureLength,
                      InvalidSignatureS,
                      InvalidSignatureV // Deprecated in v4.8
                  }
                  function _throwError(RecoverError error) private pure {
                      if (error == RecoverError.NoError) {
                          return; // no error: do nothing
                      } else if (error == RecoverError.InvalidSignature) {
                          revert("ECDSA: invalid signature");
                      } else if (error == RecoverError.InvalidSignatureLength) {
                          revert("ECDSA: invalid signature length");
                      } else if (error == RecoverError.InvalidSignatureS) {
                          revert("ECDSA: invalid signature 's' value");
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature` or error string. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   *
                   * Documentation for signature generation:
                   * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                   * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                      if (signature.length == 65) {
                          bytes32 r;
                          bytes32 s;
                          uint8 v;
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          /// @solidity memory-safe-assembly
                          assembly {
                              r := mload(add(signature, 0x20))
                              s := mload(add(signature, 0x40))
                              v := byte(0, mload(add(signature, 0x60)))
                          }
                          return tryRecover(hash, v, r, s);
                      } else {
                          return (address(0), RecoverError.InvalidSignatureLength);
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature`. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   */
                  function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, signature);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                   *
                   * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
                      bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                      uint8 v = uint8((uint256(vs) >> 255) + 27);
                      return tryRecover(hash, v, r, s);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                   *
                   * _Available since v4.2._
                   */
                  function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
                      // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                      // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                      // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                      // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                      //
                      // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                      // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                      // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                      // these malleable signatures as well.
                      if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                          return (address(0), RecoverError.InvalidSignatureS);
                      }
                      // If the signature is valid (and not malleable), return the signer address
                      address signer = ecrecover(hash, v, r, s);
                      if (signer == address(0)) {
                          return (address(0), RecoverError.InvalidSignature);
                      }
                      return (signer, RecoverError.NoError);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   */
                  function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
                      // 32 is the length in bytes of hash,
                      // enforced by the type signature above
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(0x00, "\\x19Ethereum Signed Message:\
              32")
                          mstore(0x1c, hash)
                          message := keccak256(0x00, 0x3c)
                      }
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from `s`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              ", Strings.toString(s.length), s));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Typed Data, created from a
                   * `domainSeparator` and a `structHash`. This produces hash corresponding
                   * to the one signed with the
                   * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                   * JSON-RPC method as part of EIP-712.
                   *
                   * See {recover}.
                   */
                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          let ptr := mload(0x40)
                          mstore(ptr, "\\x19\\x01")
                          mstore(add(ptr, 0x02), domainSeparator)
                          mstore(add(ptr, 0x22), structHash)
                          data := keccak256(ptr, 0x42)
                      }
                  }
                  /**
                   * @dev Returns an Ethereum Signed Data with intended validator, created from a
                   * `validator` and `data` according to the version 0 of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19\\x00", validator, data));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
              pragma solidity ^0.8.8;
              import "./ECDSA.sol";
              import "../ShortStrings.sol";
              import "../../interfaces/IERC5267.sol";
              /**
               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
               *
               * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
               * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
               * they need in their contracts using a combination of `abi.encode` and `keccak256`.
               *
               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
               * ({_hashTypedDataV4}).
               *
               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
               * the chain id to protect against replay attacks on an eventual fork of the chain.
               *
               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
               *
               * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
               * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
               * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
               *
               * _Available since v3.4._
               *
               * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
               */
              abstract contract EIP712 is IERC5267 {
                  using ShortStrings for *;
                  bytes32 private constant _TYPE_HASH =
                      keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                  // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
                  // invalidate the cached domain separator if the chain id changes.
                  bytes32 private immutable _cachedDomainSeparator;
                  uint256 private immutable _cachedChainId;
                  address private immutable _cachedThis;
                  bytes32 private immutable _hashedName;
                  bytes32 private immutable _hashedVersion;
                  ShortString private immutable _name;
                  ShortString private immutable _version;
                  string private _nameFallback;
                  string private _versionFallback;
                  /**
                   * @dev Initializes the domain separator and parameter caches.
                   *
                   * The meaning of `name` and `version` is specified in
                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                   *
                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                   * - `version`: the current major version of the signing domain.
                   *
                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                   * contract upgrade].
                   */
                  constructor(string memory name, string memory version) {
                      _name = name.toShortStringWithFallback(_nameFallback);
                      _version = version.toShortStringWithFallback(_versionFallback);
                      _hashedName = keccak256(bytes(name));
                      _hashedVersion = keccak256(bytes(version));
                      _cachedChainId = block.chainid;
                      _cachedDomainSeparator = _buildDomainSeparator();
                      _cachedThis = address(this);
                  }
                  /**
                   * @dev Returns the domain separator for the current chain.
                   */
                  function _domainSeparatorV4() internal view returns (bytes32) {
                      if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
                          return _cachedDomainSeparator;
                      } else {
                          return _buildDomainSeparator();
                      }
                  }
                  function _buildDomainSeparator() private view returns (bytes32) {
                      return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
                  }
                  /**
                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                   * function returns the hash of the fully encoded EIP712 message for this domain.
                   *
                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                   *
                   * ```solidity
                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                   *     keccak256("Mail(address to,string contents)"),
                   *     mailTo,
                   *     keccak256(bytes(mailContents))
                   * )));
                   * address signer = ECDSA.recover(digest, signature);
                   * ```
                   */
                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                      return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
                  }
                  /**
                   * @dev See {EIP-5267}.
                   *
                   * _Available since v4.9._
                   */
                  function eip712Domain()
                      public
                      view
                      virtual
                      override
                      returns (
                          bytes1 fields,
                          string memory name,
                          string memory version,
                          uint256 chainId,
                          address verifyingContract,
                          bytes32 salt,
                          uint256[] memory extensions
                      )
                  {
                      return (
                          hex"0f", // 01111
                          _name.toStringWithFallback(_nameFallback),
                          _version.toStringWithFallback(_versionFallback),
                          block.chainid,
                          address(this),
                          bytes32(0),
                          new uint256[](0)
                      );
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
              pragma solidity ^0.8.0;
              /**
               * @title Counters
               * @author Matt Condon (@shrugs)
               * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
               * of elements in a mapping, issuing ERC721 ids, or counting request ids.
               *
               * Include with `using Counters for Counters.Counter;`
               */
              library Counters {
                  struct Counter {
                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                      uint256 _value; // default: 0
                  }
                  function current(Counter storage counter) internal view returns (uint256) {
                      return counter._value;
                  }
                  function increment(Counter storage counter) internal {
                      unchecked {
                          counter._value += 1;
                      }
                  }
                  function decrement(Counter storage counter) internal {
                      uint256 value = counter._value;
                      require(value > 0, "Counter: decrement overflow");
                      unchecked {
                          counter._value = value - 1;
                      }
                  }
                  function reset(Counter storage counter) internal {
                      counter._value = 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev External interface of AccessControl declared to support ERC165 detection.
               */
              interface IAccessControl {
                  /**
                   * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                   *
                   * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                   * {RoleAdminChanged} not being emitted signaling this.
                   *
                   * _Available since v3.1._
                   */
                  event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                  /**
                   * @dev Emitted when `account` is granted `role`.
                   *
                   * `sender` is the account that originated the contract call, an admin role
                   * bearer except when using {AccessControl-_setupRole}.
                   */
                  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Emitted when `account` is revoked `role`.
                   *
                   * `sender` is the account that originated the contract call:
                   *   - if using `revokeRole`, it is the admin role bearer
                   *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                   */
                  event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) external view returns (bool);
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) external view returns (bytes32);
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function grantRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function revokeRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been granted `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   */
                  function renounceRole(bytes32 role, address account) external;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              import "./math/Math.sol";
              import "./math/SignedMath.sol";
              /**
               * @dev String operations.
               */
              library Strings {
                  bytes16 private constant _SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          uint256 length = Math.log10(value) + 1;
                          string memory buffer = new string(length);
                          uint256 ptr;
                          /// @solidity memory-safe-assembly
                          assembly {
                              ptr := add(buffer, add(32, length))
                          }
                          while (true) {
                              ptr--;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                              }
                              value /= 10;
                              if (value == 0) break;
                          }
                          return buffer;
                      }
                  }
                  /**
                   * @dev Converts a `int256` to its ASCII `string` decimal representation.
                   */
                  function toString(int256 value) internal pure returns (string memory) {
                      return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          return toHexString(value, Math.log256(value) + 1);
                      }
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                   */
                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                      bytes memory buffer = new bytes(2 * length + 2);
                      buffer[0] = "0";
                      buffer[1] = "x";
                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                          buffer[i] = _SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
                  /**
                   * @dev Returns true if the two strings are equal.
                   */
                  function equal(string memory a, string memory b) internal pure returns (bool) {
                      return keccak256(bytes(a)) == keccak256(bytes(b));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
              pragma solidity ^0.8.8;
              import "./StorageSlot.sol";
              // | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
              // | length  | 0x                                                              BB |
              type ShortString is bytes32;
              /**
               * @dev This library provides functions to convert short memory strings
               * into a `ShortString` type that can be used as an immutable variable.
               *
               * Strings of arbitrary length can be optimized using this library if
               * they are short enough (up to 31 bytes) by packing them with their
               * length (1 byte) in a single EVM word (32 bytes). Additionally, a
               * fallback mechanism can be used for every other case.
               *
               * Usage example:
               *
               * ```solidity
               * contract Named {
               *     using ShortStrings for *;
               *
               *     ShortString private immutable _name;
               *     string private _nameFallback;
               *
               *     constructor(string memory contractName) {
               *         _name = contractName.toShortStringWithFallback(_nameFallback);
               *     }
               *
               *     function name() external view returns (string memory) {
               *         return _name.toStringWithFallback(_nameFallback);
               *     }
               * }
               * ```
               */
              library ShortStrings {
                  // Used as an identifier for strings longer than 31 bytes.
                  bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
                  error StringTooLong(string str);
                  error InvalidShortString();
                  /**
                   * @dev Encode a string of at most 31 chars into a `ShortString`.
                   *
                   * This will trigger a `StringTooLong` error is the input string is too long.
                   */
                  function toShortString(string memory str) internal pure returns (ShortString) {
                      bytes memory bstr = bytes(str);
                      if (bstr.length > 31) {
                          revert StringTooLong(str);
                      }
                      return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
                  }
                  /**
                   * @dev Decode a `ShortString` back to a "normal" string.
                   */
                  function toString(ShortString sstr) internal pure returns (string memory) {
                      uint256 len = byteLength(sstr);
                      // using `new string(len)` would work locally but is not memory safe.
                      string memory str = new string(32);
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(str, len)
                          mstore(add(str, 0x20), sstr)
                      }
                      return str;
                  }
                  /**
                   * @dev Return the length of a `ShortString`.
                   */
                  function byteLength(ShortString sstr) internal pure returns (uint256) {
                      uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
                      if (result > 31) {
                          revert InvalidShortString();
                      }
                      return result;
                  }
                  /**
                   * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
                   */
                  function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
                      if (bytes(value).length < 32) {
                          return toShortString(value);
                      } else {
                          StorageSlot.getStringSlot(store).value = value;
                          return ShortString.wrap(_FALLBACK_SENTINEL);
                      }
                  }
                  /**
                   * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
                   */
                  function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
                      if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                          return toString(value);
                      } else {
                          return store;
                      }
                  }
                  /**
                   * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
                   *
                   * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
                   * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
                   */
                  function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
                      if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                          return byteLength(value);
                      } else {
                          return bytes(store).length;
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
              pragma solidity ^0.8.0;
              interface IERC5267 {
                  /**
                   * @dev MAY be emitted to signal that the domain could have changed.
                   */
                  event EIP712DomainChanged();
                  /**
                   * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
                   * signature.
                   */
                  function eip712Domain()
                      external
                      view
                      returns (
                          bytes1 fields,
                          string memory name,
                          string memory version,
                          uint256 chainId,
                          address verifyingContract,
                          bytes32 salt,
                          uint256[] memory extensions
                      );
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard signed math utilities missing in the Solidity language.
               */
              library SignedMath {
                  /**
                   * @dev Returns the largest of two signed numbers.
                   */
                  function max(int256 a, int256 b) internal pure returns (int256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two signed numbers.
                   */
                  function min(int256 a, int256 b) internal pure returns (int256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two signed numbers without overflow.
                   * The result is rounded towards zero.
                   */
                  function average(int256 a, int256 b) internal pure returns (int256) {
                      // Formula from the book "Hacker's Delight"
                      int256 x = (a & b) + ((a ^ b) >> 1);
                      return x + (int256(uint256(x) >> 255) & (a ^ b));
                  }
                  /**
                   * @dev Returns the absolute unsigned value of a signed value.
                   */
                  function abs(int256 n) internal pure returns (uint256) {
                      unchecked {
                          // must be unchecked in order to support `n = type(int256).min`
                          return uint256(n >= 0 ? n : -n);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[EIP].
               *
               * Implementers can declare support of contract interfaces, which can then be
               * queried by others ({ERC165Checker}).
               *
               * For an implementation, see {ERC165}.
               */
              interface IERC165 {
                  /**
                   * @dev Returns true if this contract implements the interface defined by
                   * `interfaceId`. See the corresponding
                   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                   * to learn more about how these ids are created.
                   *
                   * This function call must use less than 30 000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @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:
               * ```solidity
               * 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`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              

              File 10 of 16: CurveTwocryptoOptimized
              # pragma version 0.3.10
              # pragma optimize gas
              # pragma evm-version paris
              """
              @title CurveTwocryptoOptimized
              @author Curve.Fi
              @license Copyright (c) Curve.Fi, 2023 - all rights reserved
              @notice A Curve AMM pool for 2 unpegged assets (e.g. WETH, USD).
              @dev All prices in the AMM are with respect to the first token in the pool.
              """
              
              from vyper.interfaces import ERC20
              implements: ERC20  # <--------------------- AMM contract is also the LP token.
              
              # --------------------------------- Interfaces -------------------------------
              
              interface Math:
                  def wad_exp(_power: int256) -> uint256: view
                  def newton_D(
                      ANN: uint256,
                      gamma: uint256,
                      x_unsorted: uint256[N_COINS],
                      K0_prev: uint256
                  ) -> uint256: view
                  def get_y(
                      ANN: uint256,
                      gamma: uint256,
                      x: uint256[N_COINS],
                      D: uint256,
                      i: uint256,
                  ) -> uint256[2]: view
                  def get_p(
                      _xp: uint256[N_COINS],
                      _D: uint256,
                      _A_gamma: uint256[2],
                  ) -> uint256: view
              
              interface Factory:
                  def admin() -> address: view
                  def fee_receiver() -> address: view
                  def views_implementation() -> address: view
              
              interface Views:
                  def calc_token_amount(
                      amounts: uint256[N_COINS], deposit: bool, swap: address
                  ) -> uint256: view
                  def get_dy(
                      i: uint256, j: uint256, dx: uint256, swap: address
                  ) -> uint256: view
                  def get_dx(
                      i: uint256, j: uint256, dy: uint256, swap: address
                  ) -> uint256: view
              
              
              # ------------------------------- Events -------------------------------------
              
              event Transfer:
                  sender: indexed(address)
                  receiver: indexed(address)
                  value: uint256
              
              event Approval:
                  owner: indexed(address)
                  spender: indexed(address)
                  value: uint256
              
              event TokenExchange:
                  buyer: indexed(address)
                  sold_id: uint256
                  tokens_sold: uint256
                  bought_id: uint256
                  tokens_bought: uint256
                  fee: uint256
                  packed_price_scale: uint256
              
              event AddLiquidity:
                  provider: indexed(address)
                  token_amounts: uint256[N_COINS]
                  fee: uint256
                  token_supply: uint256
                  packed_price_scale: uint256
              
              event RemoveLiquidity:
                  provider: indexed(address)
                  token_amounts: uint256[N_COINS]
                  token_supply: uint256
              
              event RemoveLiquidityOne:
                  provider: indexed(address)
                  token_amount: uint256
                  coin_index: uint256
                  coin_amount: uint256
                  approx_fee: uint256
                  packed_price_scale: uint256
              
              event NewParameters:
                  mid_fee: uint256
                  out_fee: uint256
                  fee_gamma: uint256
                  allowed_extra_profit: uint256
                  adjustment_step: uint256
                  ma_time: uint256
              
              event RampAgamma:
                  initial_A: uint256
                  future_A: uint256
                  initial_gamma: uint256
                  future_gamma: uint256
                  initial_time: uint256
                  future_time: uint256
              
              event StopRampA:
                  current_A: uint256
                  current_gamma: uint256
                  time: uint256
              
              event ClaimAdminFee:
                  admin: indexed(address)
                  tokens: uint256[N_COINS]
              
              
              # ----------------------- Storage/State Variables ----------------------------
              
              N_COINS: constant(uint256) = 2
              PRECISION: constant(uint256) = 10**18  # <------- The precision to convert to.
              PRECISIONS: immutable(uint256[N_COINS])
              
              MATH: public(immutable(Math))
              coins: public(immutable(address[N_COINS]))
              factory: public(immutable(Factory))
              
              cached_price_scale: uint256  # <------------------------ Internal price scale.
              cached_price_oracle: uint256  # <------- Price target given by moving average.
              
              last_prices: public(uint256)
              last_timestamp: public(uint256)    # idx 0 is for prices, idx 1 is for xcp.
              
              initial_A_gamma: public(uint256)
              initial_A_gamma_time: public(uint256)
              
              future_A_gamma: public(uint256)
              future_A_gamma_time: public(uint256)  # <------ Time when ramping is finished.
              #         This value is 0 (default) when pool is first deployed, and only gets
              #        populated by block.timestamp + future_time in `ramp_A_gamma` when the
              #                      ramping process is initiated. After ramping is finished
              #      (i.e. self.future_A_gamma_time < block.timestamp), the variable is left
              #                                                            and not set to 0.
              
              balances: public(uint256[N_COINS])
              D: public(uint256)
              xcp_profit: public(uint256)
              xcp_profit_a: public(uint256)  # <--- Full profit at last claim of admin fees.
              
              virtual_price: public(uint256)  # <------ Cached (fast to read) virtual price.
              #                          The cached `virtual_price` is also used internally.
              
              # Params that affect how price_scale get adjusted :
              packed_rebalancing_params: public(uint256)  # <---------- Contains rebalancing
              #               parameters allowed_extra_profit, adjustment_step, and ma_time.
              
              # Fee params that determine dynamic fees:
              packed_fee_params: public(uint256)  # <---- Packs mid_fee, out_fee, fee_gamma.
              
              ADMIN_FEE: public(constant(uint256)) = 5 * 10**9  # <----- 50% of earned fees.
              MIN_FEE: constant(uint256) = 5 * 10**5  # <-------------------------- 0.5 BPS.
              MAX_FEE: constant(uint256) = 10 * 10**9
              NOISE_FEE: constant(uint256) = 10**5  # <---------------------------- 0.1 BPS.
              
              # ----------------------- Admin params ---------------------------------------
              
              last_admin_fee_claim_timestamp: uint256
              admin_lp_virtual_balance: uint256
              
              MIN_RAMP_TIME: constant(uint256) = 86400
              MIN_ADMIN_FEE_CLAIM_INTERVAL: constant(uint256) = 86400
              
              A_MULTIPLIER: constant(uint256) = 10000
              MIN_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER / 10
              MAX_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER * 1000
              MAX_A_CHANGE: constant(uint256) = 10
              MIN_GAMMA: constant(uint256) = 10**10
              MAX_GAMMA: constant(uint256) = 199 * 10**15 # 1.99 * 10**17
              
              # ----------------------- ERC20 Specific vars --------------------------------
              
              name: public(immutable(String[64]))
              symbol: public(immutable(String[32]))
              decimals: public(constant(uint8)) = 18
              version: public(constant(String[8])) = "v2.1.0"
              
              balanceOf: public(HashMap[address, uint256])
              allowance: public(HashMap[address, HashMap[address, uint256]])
              totalSupply: public(uint256)
              nonces: public(HashMap[address, uint256])
              
              EIP712_TYPEHASH: constant(bytes32) = keccak256(
                  "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
              )
              EIP2612_TYPEHASH: constant(bytes32) = keccak256(
                  "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
              )
              VERSION_HASH: constant(bytes32) = keccak256(version)
              NAME_HASH: immutable(bytes32)
              CACHED_CHAIN_ID: immutable(uint256)
              salt: public(immutable(bytes32))
              CACHED_DOMAIN_SEPARATOR: immutable(bytes32)
              
              
              # ----------------------- Contract -------------------------------------------
              
              @external
              def __init__(
                  _name: String[64],
                  _symbol: String[32],
                  _coins: address[N_COINS],
                  _math: address,
                  _salt: bytes32,
                  packed_precisions: uint256,
                  packed_gamma_A: uint256,
                  packed_fee_params: uint256,
                  packed_rebalancing_params: uint256,
                  initial_price: uint256,
              ):
              
                  MATH = Math(_math)
              
                  factory = Factory(msg.sender)
                  name = _name
                  symbol = _symbol
                  coins = _coins
              
                  PRECISIONS = self._unpack_2(packed_precisions)  # <-- Precisions of coins.
              
                  # --------------- Validate A and gamma parameters here and not in factory.
                  gamma_A: uint256[2] = self._unpack_2(packed_gamma_A)  # gamma is at idx 0.
              
                  assert gamma_A[0] > MIN_GAMMA-1
                  assert gamma_A[0] < MAX_GAMMA+1
              
                  assert gamma_A[1] > MIN_A-1
                  assert gamma_A[1] < MAX_A+1
              
                  self.initial_A_gamma = packed_gamma_A
                  self.future_A_gamma = packed_gamma_A
                  # ------------------------------------------------------------------------
              
                  self.packed_rebalancing_params = packed_rebalancing_params  # <-- Contains
                  #               rebalancing params: allowed_extra_profit, adjustment_step,
                  #                                                         and ma_exp_time.
              
                  self.packed_fee_params = packed_fee_params  # <-------------- Contains Fee
                  #                                  params: mid_fee, out_fee and fee_gamma.
              
                  self.cached_price_scale = initial_price
                  self.cached_price_oracle = initial_price
                  self.last_prices = initial_price
                  self.last_timestamp = block.timestamp
                  self.xcp_profit_a = 10**18
              
                  #         Cache DOMAIN_SEPARATOR. If chain.id is not CACHED_CHAIN_ID, then
                  #     DOMAIN_SEPARATOR will be re-calculated each time `permit` is called.
                  #                   Otherwise, it will always use CACHED_DOMAIN_SEPARATOR.
                  #                       see: `_domain_separator()` for its implementation.
                  NAME_HASH = keccak256(name)
                  salt = _salt
                  CACHED_CHAIN_ID = chain.id
                  CACHED_DOMAIN_SEPARATOR = keccak256(
                      _abi_encode(
                          EIP712_TYPEHASH,
                          NAME_HASH,
                          VERSION_HASH,
                          chain.id,
                          self,
                          salt,
                      )
                  )
              
                  log Transfer(empty(address), self, 0)  # <------- Fire empty transfer from
                  #                                       0x0 to self for indexers to catch.
              
              
              # ------------------- Token transfers in and out of the AMM ------------------
              
              
              @internal
              def _transfer_in(
                  _coin_idx: uint256,
                  _dx: uint256,
                  sender: address,
                  expect_optimistic_transfer: bool,
              ) -> uint256:
                  """
                  @notice Transfers `_coin` from `sender` to `self` and calls `callback_sig`
                          if it is not empty.
                  @params _coin_idx uint256 Index of the coin to transfer in.
                  @params dx amount of `_coin` to transfer into the pool.
                  @params sender address to transfer `_coin` from.
                  @params expect_optimistic_transfer bool True if pool expects user to transfer.
                          This is only enabled for exchange_received.
                  @return The amount of tokens received.
                  """
                  coin_balance: uint256 = ERC20(coins[_coin_idx]).balanceOf(self)
              
                  if expect_optimistic_transfer:  # Only enabled in exchange_received:
                      # it expects the caller of exchange_received to have sent tokens to
                      # the pool before calling this method.
              
                      # If someone donates extra tokens to the contract: do not acknowledge.
                      # We only want to know if there are dx amount of tokens. Anything extra,
                      # we ignore. This is why we need to check if received_amounts (which
                      # accounts for coin balances of the contract) is atleast dx.
                      # If we checked for received_amounts == dx, an extra transfer without a
                      # call to exchange_received will break the method.
                      dx: uint256 = coin_balance - self.balances[_coin_idx]
                      assert dx >= _dx  # dev: user didn't give us coins
              
                      # Adjust balances
                      self.balances[_coin_idx] += dx
              
                      return dx
              
                  # ----------------------------------------------- ERC20 transferFrom flow.
              
                  # EXTERNAL CALL
                  assert ERC20(coins[_coin_idx]).transferFrom(
                      sender,
                      self,
                      _dx,
                      default_return_value=True
                  )
              
                  dx: uint256 = ERC20(coins[_coin_idx]).balanceOf(self) - coin_balance
                  self.balances[_coin_idx] += dx
                  return dx
              
              
              @internal
              def _transfer_out(_coin_idx: uint256, _amount: uint256, receiver: address):
                  """
                  @notice Transfer a single token from the pool to receiver.
                  @dev This function is called by `remove_liquidity` and
                       `remove_liquidity_one`, `_claim_admin_fees` and `_exchange` methods.
                  @params _coin_idx uint256 Index of the token to transfer out
                  @params _amount Amount of token to transfer out
                  @params receiver Address to send the tokens to
                  """
              
                  # Adjust balances before handling transfers:
                  self.balances[_coin_idx] -= _amount
              
                  # EXTERNAL CALL
                  assert ERC20(coins[_coin_idx]).transfer(
                      receiver,
                      _amount,
                      default_return_value=True
                  )
              
              
              # -------------------------- AMM Main Functions ------------------------------
              
              
              @external
              @nonreentrant("lock")
              def exchange(
                  i: uint256,
                  j: uint256,
                  dx: uint256,
                  min_dy: uint256,
                  receiver: address = msg.sender
              ) -> uint256:
                  """
                  @notice Exchange using wrapped native token by default
                  @param i Index value for the input coin
                  @param j Index value for the output coin
                  @param dx Amount of input coin being swapped in
                  @param min_dy Minimum amount of output coin to receive
                  @param receiver Address to send the output coin to. Default is msg.sender
                  @return uint256 Amount of tokens at index j received by the `receiver
                  """
                  # _transfer_in updates self.balances here:
                  dx_received: uint256 = self._transfer_in(
                      i,
                      dx,
                      msg.sender,
                      False
                  )
              
                  # No ERC20 token transfers occur here:
                  out: uint256[3] = self._exchange(
                      i,
                      j,
                      dx_received,
                      min_dy,
                  )
              
                  # _transfer_out updates self.balances here. Update to state occurs before
                  # external calls:
                  self._transfer_out(j, out[0], receiver)
              
                  # log:
                  log TokenExchange(msg.sender, i, dx_received, j, out[0], out[1], out[2])
              
                  return out[0]
              
              
              @external
              @nonreentrant('lock')
              def exchange_received(
                  i: uint256,
                  j: uint256,
                  dx: uint256,
                  min_dy: uint256,
                  receiver: address = msg.sender,
              ) -> uint256:
                  """
                  @notice Exchange: but user must transfer dx amount of coin[i] tokens to pool first.
                          Pool will not call transferFrom and will only check if a surplus of
                          coins[i] is greater than or equal to `dx`.
                  @dev Use-case is to reduce the number of redundant ERC20 token
                       transfers in zaps. Primarily for dex-aggregators/arbitrageurs/searchers.
                       Note for users: please transfer + exchange_received in 1 tx.
                  @param i Index value for the input coin
                  @param j Index value for the output coin
                  @param dx Amount of input coin being swapped in
                  @param min_dy Minimum amount of output coin to receive
                  @param receiver Address to send the output coin to
                  @return uint256 Amount of tokens at index j received by the `receiver`
                  """
                  # _transfer_in updates self.balances here:
                  dx_received: uint256 = self._transfer_in(
                      i,
                      dx,
                      msg.sender,
                      True  # <---- expect_optimistic_transfer is set to True here.
                  )
              
                  # No ERC20 token transfers occur here:
                  out: uint256[3] = self._exchange(
                      i,
                      j,
                      dx_received,
                      min_dy,
                  )
              
                  # _transfer_out updates self.balances here. Update to state occurs before
                  # external calls:
                  self._transfer_out(j, out[0], receiver)
              
                  # log:
                  log TokenExchange(msg.sender, i, dx_received, j, out[0], out[1], out[2])
              
                  return out[0]
              
              
              @external
              @nonreentrant("lock")
              def add_liquidity(
                  amounts: uint256[N_COINS],
                  min_mint_amount: uint256,
                  receiver: address = msg.sender
              ) -> uint256:
                  """
                  @notice Adds liquidity into the pool.
                  @param amounts Amounts of each coin to add.
                  @param min_mint_amount Minimum amount of LP to mint.
                  @param receiver Address to send the LP tokens to. Default is msg.sender
                  @return uint256 Amount of LP tokens received by the `receiver
                  """
              
                  A_gamma: uint256[2] = self._A_gamma()
                  xp: uint256[N_COINS] = self.balances
                  amountsp: uint256[N_COINS] = empty(uint256[N_COINS])
                  d_token: uint256 = 0
                  d_token_fee: uint256 = 0
                  old_D: uint256 = 0
              
                  assert amounts[0] + amounts[1] > 0  # dev: no coins to add
              
                  # --------------------- Get prices, balances -----------------------------
              
                  price_scale: uint256 = self.cached_price_scale
              
                  # -------------------------------------- Update balances and calculate xp.
                  xp_old: uint256[N_COINS] = xp
                  amounts_received: uint256[N_COINS] = empty(uint256[N_COINS])
              
                  ########################## TRANSFER IN <-------
              
                  for i in range(N_COINS):
                      if amounts[i] > 0:
                          # Updates self.balances here:
                          amounts_received[i] = self._transfer_in(
                              i,
                              amounts[i],
                              msg.sender,
                              False,  # <--------------------- Disable optimistic transfers.
                          )
                          xp[i] = xp[i] + amounts_received[i]
              
                  xp = [
                      xp[0] * PRECISIONS[0],
                      unsafe_div(xp[1] * price_scale * PRECISIONS[1], PRECISION)
                  ]
                  xp_old = [
                      xp_old[0] * PRECISIONS[0],
                      unsafe_div(xp_old[1] * price_scale * PRECISIONS[1], PRECISION)
                  ]
              
                  for i in range(N_COINS):
                      if amounts_received[i] > 0:
                          amountsp[i] = xp[i] - xp_old[i]
              
                  # -------------------- Calculate LP tokens to mint -----------------------
              
                  if self.future_A_gamma_time > block.timestamp:  # <--- A_gamma is ramping.
              
                      # ----- Recalculate the invariant if A or gamma are undergoing a ramp.
                      old_D = MATH.newton_D(A_gamma[0], A_gamma[1], xp_old, 0)
              
                  else:
              
                      old_D = self.D
              
                  D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
              
                  token_supply: uint256 = self.totalSupply
                  if old_D > 0:
                      d_token = token_supply * D / old_D - token_supply
                  else:
                      d_token = self.get_xcp(D, price_scale)  # <----- Making initial virtual price equal to 1.
              
                  assert d_token > 0  # dev: nothing minted
              
                  if old_D > 0:
              
                      d_token_fee = (
                          self._calc_token_fee(amountsp, xp) * d_token / 10**10 + 1
                      )
              
                      d_token -= d_token_fee
                      token_supply += d_token
                      self.mint(receiver, d_token)
                      self.admin_lp_virtual_balance += unsafe_div(ADMIN_FEE * d_token_fee, 10**10)
              
                      price_scale = self.tweak_price(A_gamma, xp, D, 0)
              
                  else:
              
                      # (re)instatiating an empty pool:
              
                      self.D = D
                      self.virtual_price = 10**18
                      self.xcp_profit = 10**18
                      self.xcp_profit_a = 10**18
              
                      self.mint(receiver, d_token)
              
                  assert d_token >= min_mint_amount, "Slippage"
              
                  # ---------------------------------------------- Log and claim admin fees.
              
                  log AddLiquidity(
                      receiver,
                      amounts_received,
                      d_token_fee,
                      token_supply,
                      price_scale
                  )
              
                  return d_token
              
              
              @external
              @nonreentrant("lock")
              def remove_liquidity(
                  _amount: uint256,
                  min_amounts: uint256[N_COINS],
                  receiver: address = msg.sender,
              ) -> uint256[N_COINS]:
                  """
                  @notice This withdrawal method is very safe, does no complex math since
                          tokens are withdrawn in balanced proportions. No fees are charged.
                  @param _amount Amount of LP tokens to burn
                  @param min_amounts Minimum amounts of tokens to withdraw
                  @param receiver Address to send the withdrawn tokens to
                  @return uint256[3] Amount of pool tokens received by the `receiver`
                  """
                  amount: uint256 = _amount
                  balances: uint256[N_COINS] = self.balances
                  withdraw_amounts: uint256[N_COINS] = empty(uint256[N_COINS])
              
                  # -------------------------------------------------------- Burn LP tokens.
              
                  total_supply: uint256 = self.totalSupply  # <------ Get totalSupply before
                  self.burnFrom(msg.sender, _amount)  # ---- reducing it with self.burnFrom.
              
                  # There are two cases for withdrawing tokens from the pool.
                  #   Case 1. Withdrawal does not empty the pool.
                  #           In this situation, D is adjusted proportional to the amount of
                  #           LP tokens burnt. ERC20 tokens transferred is proportional
                  #           to : (AMM balance * LP tokens in) / LP token total supply
                  #   Case 2. Withdrawal empties the pool.
                  #           In this situation, all tokens are withdrawn and the invariant
                  #           is reset.
              
                  if amount == total_supply:  # <----------------------------------- Case 2.
              
                      for i in range(N_COINS):
              
                          withdraw_amounts[i] = balances[i]
              
                  else:  # <-------------------------------------------------------- Case 1.
              
                      amount -= 1  # <---- To prevent rounding errors, favor LPs a tiny bit.
              
                      for i in range(N_COINS):
              
                          withdraw_amounts[i] = balances[i] * amount / total_supply
                          assert withdraw_amounts[i] >= min_amounts[i]
              
                  D: uint256 = self.D
                  self.D = D - unsafe_div(D * amount, total_supply)  # <----------- Reduce D
                  #      proportional to the amount of tokens leaving. Since withdrawals are
                  #       balanced, this is a simple subtraction. If amount == total_supply,
                  #                                                             D will be 0.
              
                  # ---------------------------------- Transfers ---------------------------
              
                  for i in range(N_COINS):
                      # _transfer_out updates self.balances here. Update to state occurs
                      # before external calls:
                      self._transfer_out(i, withdraw_amounts[i], receiver)
              
                  log RemoveLiquidity(msg.sender, withdraw_amounts, total_supply - _amount)
              
                  return withdraw_amounts
              
              
              @external
              @nonreentrant("lock")
              def remove_liquidity_one_coin(
                  token_amount: uint256,
                  i: uint256,
                  min_amount: uint256,
                  receiver: address = msg.sender
              ) -> uint256:
                  """
                  @notice Withdraw liquidity in a single token.
                          Involves fees (lower than swap fees).
                  @dev This operation also involves an admin fee claim.
                  @param token_amount Amount of LP tokens to burn
                  @param i Index of the token to withdraw
                  @param min_amount Minimum amount of token to withdraw.
                  @param receiver Address to send the withdrawn tokens to
                  @return Amount of tokens at index i received by the `receiver`
                  """
              
                  self._claim_admin_fees()  # <--------- Auto-claim admin fees occasionally.
              
                  A_gamma: uint256[2] = self._A_gamma()
              
                  dy: uint256 = 0
                  D: uint256 = 0
                  p: uint256 = 0
                  xp: uint256[N_COINS] = empty(uint256[N_COINS])
                  approx_fee: uint256 = 0
              
                  # ------------------------------------------------------------------------
              
                  dy, D, xp, approx_fee = self._calc_withdraw_one_coin(
                      A_gamma,
                      token_amount,
                      i,
                      (self.future_A_gamma_time > block.timestamp),  # <------- During ramps
                  )  #                                                  we need to update D.
              
                  assert dy >= min_amount, "Slippage"
              
                  # ---------------------------- State Updates -----------------------------
              
                  # Burn user's tokens:
                  self.burnFrom(msg.sender, token_amount)
              
                  packed_price_scale: uint256 = self.tweak_price(A_gamma, xp, D, 0)
                  #        Safe to use D from _calc_withdraw_one_coin here ---^
              
                  # ------------------------- Transfers ------------------------------------
              
                  # _transfer_out updates self.balances here. Update to state occurs before
                  # external calls:
                  self._transfer_out(i, dy, receiver)
              
                  log RemoveLiquidityOne(
                      msg.sender, token_amount, i, dy, approx_fee, packed_price_scale
                  )
              
                  return dy
              
              
              # -------------------------- Packing functions -------------------------------
              
              
              @internal
              @pure
              def _pack_3(x: uint256[3]) -> uint256:
                  """
                  @notice Packs 3 integers with values <= 10**18 into a uint256
                  @param x The uint256[3] to pack
                  @return uint256 Integer with packed values
                  """
                  return (x[0] << 128) | (x[1] << 64) | x[2]
              
              
              @internal
              @pure
              def _unpack_3(_packed: uint256) -> uint256[3]:
                  """
                  @notice Unpacks a uint256 into 3 integers (values must be <= 10**18)
                  @param val The uint256 to unpack
                  @return uint256[3] A list of length 3 with unpacked integers
                  """
                  return [
                      (_packed >> 128) & 18446744073709551615,
                      (_packed >> 64) & 18446744073709551615,
                      _packed & 18446744073709551615,
                  ]
              
              
              @pure
              @internal
              def _pack_2(p1: uint256, p2: uint256) -> uint256:
                  return p1 | (p2 << 128)
              
              
              @pure
              @internal
              def _unpack_2(packed: uint256) -> uint256[2]:
                  return [packed & (2**128 - 1), packed >> 128]
              
              
              # ---------------------- AMM Internal Functions -------------------------------
              
              
              @internal
              def _exchange(
                  i: uint256,
                  j: uint256,
                  dx_received: uint256,
                  min_dy: uint256,
              ) -> uint256[3]:
              
                  assert i != j  # dev: coin index out of range
                  assert dx_received > 0  # dev: do not exchange 0 coins
              
                  A_gamma: uint256[2] = self._A_gamma()
                  xp: uint256[N_COINS] = self.balances
                  dy: uint256 = 0
              
                  y: uint256 = xp[j]
                  x0: uint256 = xp[i] - dx_received  # old xp[i]
              
                  price_scale: uint256 = self.cached_price_scale
                  xp = [
                      xp[0] * PRECISIONS[0],
                      unsafe_div(xp[1] * price_scale * PRECISIONS[1], PRECISION)
                  ]
              
                  # ----------- Update invariant if A, gamma are undergoing ramps ---------
              
                  t: uint256 = self.future_A_gamma_time
                  if t > block.timestamp:
              
                      x0 *= PRECISIONS[i]
              
                      if i > 0:
                          x0 = unsafe_div(x0 * price_scale, PRECISION)
              
                      x1: uint256 = xp[i]  # <------------------ Back up old value in xp ...
                      xp[i] = x0                                                         # |
                      self.D = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)              # |
                      xp[i] = x1  # <-------------------------------------- ... and restore.
              
                  # ----------------------- Calculate dy and fees --------------------------
              
                  D: uint256 = self.D
                  y_out: uint256[2] = MATH.get_y(A_gamma[0], A_gamma[1], xp, D, j)
                  dy = xp[j] - y_out[0]
                  xp[j] -= dy
                  dy -= 1
              
                  if j > 0:
                      dy = dy * PRECISION / price_scale
                  dy /= PRECISIONS[j]
              
                  fee: uint256 = unsafe_div(self._fee(xp) * dy, 10**10)
                  dy -= fee  # <--------------------- Subtract fee from the outgoing amount.
                  assert dy >= min_dy, "Slippage"
                  y -= dy
              
                  y *= PRECISIONS[j]
                  if j > 0:
                      y = unsafe_div(y * price_scale, PRECISION)
                  xp[j] = y  # <------------------------------------------------- Update xp.
              
                  # ------ Tweak price_scale with good initial guess for newton_D ----------
              
                  price_scale = self.tweak_price(A_gamma, xp, 0, y_out[1])
              
                  return [dy, fee, price_scale]
              
              
              @internal
              def tweak_price(
                  A_gamma: uint256[2],
                  _xp: uint256[N_COINS],
                  new_D: uint256,
                  K0_prev: uint256 = 0,
              ) -> uint256:
                  """
                  @notice Updates price_oracle, last_price and conditionally adjusts
                          price_scale. This is called whenever there is an unbalanced
                          liquidity operation: _exchange, add_liquidity, or
                          remove_liquidity_one_coin.
                  @dev Contains main liquidity rebalancing logic, by tweaking `price_scale`.
                  @param A_gamma Array of A and gamma parameters.
                  @param _xp Array of current balances.
                  @param new_D New D value.
                  @param K0_prev Initial guess for `newton_D`.
                  """
              
                  # ---------------------------- Read storage ------------------------------
              
                  price_oracle: uint256 = self.cached_price_oracle
                  last_prices: uint256 = self.last_prices
                  price_scale: uint256 = self.cached_price_scale
                  rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params)
                  # Contains: allowed_extra_profit, adjustment_step, ma_time. -----^
              
                  total_supply: uint256 = self.totalSupply
                  old_xcp_profit: uint256 = self.xcp_profit
                  old_virtual_price: uint256 = self.virtual_price
              
                  # ------------------ Update Price Oracle if needed -----------------------
              
                  last_timestamp: uint256 = self.last_timestamp
                  alpha: uint256 = 0
                  if last_timestamp < block.timestamp:  # 0th index is for price_oracle.
              
                      #   The moving average price oracle is calculated using the last_price
                      #      of the trade at the previous block, and the price oracle logged
                      #              before that trade. This can happen only once per block.
              
                      # ------------------ Calculate moving average params -----------------
              
                      alpha = MATH.wad_exp(
                          -convert(
                              unsafe_div(
                                  unsafe_sub(block.timestamp, last_timestamp) * 10**18,
                                  rebalancing_params[2]  # <----------------------- ma_time.
                              ),
                              int256,
                          )
                      )
              
                      # ---------------------------------------------- Update price oracles.
              
                      # ----------------- We cap state price that goes into the EMA with
                      #                                                 2 x price_scale.
                      price_oracle = unsafe_div(
                          min(last_prices, 2 * price_scale) * (10**18 - alpha) +
                          price_oracle * alpha,  # ^-------- Cap spot price into EMA.
                          10**18
                      )
              
                      self.cached_price_oracle = price_oracle
                      self.last_timestamp = block.timestamp
              
                  #  `price_oracle` is used further on to calculate its vector distance from
                  # price_scale. This distance is used to calculate the amount of adjustment
                  # to be done to the price_scale.
                  # ------------------------------------------------------------------------
              
                  # ------------------ If new_D is set to 0, calculate it ------------------
              
                  D_unadjusted: uint256 = new_D
                  if new_D == 0:  #  <--------------------------- _exchange sets new_D to 0.
                      D_unadjusted = MATH.newton_D(A_gamma[0], A_gamma[1], _xp, K0_prev)
              
                  # ----------------------- Calculate last_prices --------------------------
              
                  self.last_prices = unsafe_div(
                      MATH.get_p(_xp, D_unadjusted, A_gamma) * price_scale,
                      10**18
                  )
              
                  # ---------- Update profit numbers without price adjustment first --------
              
                  xp: uint256[N_COINS] = [
                      unsafe_div(D_unadjusted, N_COINS),
                      D_unadjusted * PRECISION / (N_COINS * price_scale)  # <------ safediv.
                  ]  #                                                     with price_scale.
              
                  xcp_profit: uint256 = 10**18
                  virtual_price: uint256 = 10**18
              
                  if old_virtual_price > 0:
              
                      xcp: uint256 = isqrt(xp[0] * xp[1])
                      virtual_price = 10**18 * xcp / total_supply
              
                      xcp_profit = unsafe_div(
                          old_xcp_profit * virtual_price,
                          old_virtual_price
                      )  # <---------------- Safu to do unsafe_div as old_virtual_price > 0.
              
                      #       If A and gamma are not undergoing ramps (t < block.timestamp),
                      #         ensure new virtual_price is not less than old virtual_price,
                      #                                        else the pool suffers a loss.
                      if self.future_A_gamma_time < block.timestamp:
                          # this usually reverts when withdrawing a very small amount of LP tokens
                          assert virtual_price > old_virtual_price # dev: virtual price decreased
              
                  self.xcp_profit = xcp_profit
              
                  # ------------ Rebalance liquidity if there's enough profits to adjust it:
                  if virtual_price * 2 - 10**18 > xcp_profit + 2 * rebalancing_params[0]:
                      #                          allowed_extra_profit --------^
              
                      # ------------------- Get adjustment step ----------------------------
              
                      #                Calculate the vector distance between price_scale and
                      #                                                        price_oracle.
                      norm: uint256 = unsafe_div(
                          unsafe_mul(price_oracle, 10**18), price_scale
                      )
                      if norm > 10**18:
                          norm = unsafe_sub(norm, 10**18)
                      else:
                          norm = unsafe_sub(10**18, norm)
                      adjustment_step: uint256 = max(
                          rebalancing_params[1], unsafe_div(norm, 5)
                      )  #           ^------------------------------------- adjustment_step.
              
                      if norm > adjustment_step:  # <---------- We only adjust prices if the
                          #          vector distance between price_oracle and price_scale is
                          #             large enough. This check ensures that no rebalancing
                          #           occurs if the distance is low i.e. the pool prices are
                          #                                     pegged to the oracle prices.
              
                          # ------------------------------------- Calculate new price scale.
              
                          p_new: uint256 = unsafe_div(
                              price_scale * unsafe_sub(norm, adjustment_step) +
                              adjustment_step * price_oracle,
                              norm
                          )  # <---- norm is non-zero and gt adjustment_step; unsafe = safe.
              
                          # ---------------- Update stale xp (using price_scale) with p_new.
              
                          xp = [
                              _xp[0],
                              unsafe_div(_xp[1] * p_new, price_scale)
                          ]
              
                          # ------------------------------------------ Update D with new xp.
                          D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
              
                          # ------------------------------------- Convert xp to real prices.
                          xp = [
                              unsafe_div(D, N_COINS),
                              D * PRECISION / (N_COINS * p_new)
                          ]
              
                          # ---------- Calculate new virtual_price using new xp and D. Reuse
                          #              `old_virtual_price` (but it has new virtual_price).
                          old_virtual_price = unsafe_div(
                              10**18 * isqrt(xp[0] * xp[1]), total_supply
                          )  # <----- unsafe_div because we did safediv before (if vp>1e18)
              
                          # ---------------------------- Proceed if we've got enough profit.
                          if (
                              old_virtual_price > 10**18 and
                              2 * old_virtual_price - 10**18 > xcp_profit
                          ):
              
                              self.D = D
                              self.virtual_price = old_virtual_price
                              self.cached_price_scale = p_new
              
                              return p_new
              
                  # --------- price_scale was not adjusted. Update the profit counter and D.
                  self.D = D_unadjusted
                  self.virtual_price = virtual_price
              
                  return price_scale
              
              
              @internal
              def _claim_admin_fees():
                  """
                  @notice Claims admin fees and sends it to fee_receiver set in the factory.
                  @dev Functionally similar to:
                       1. Calculating admin's share of fees,
                       2. minting LP tokens,
                       3. admin claims underlying tokens via remove_liquidity.
                  """
              
                  # --------------------- Check if fees can be claimed ---------------------
              
                  # Disable fee claiming if:
                  # 1. If time passed since last fee claim is less than
                  #    MIN_ADMIN_FEE_CLAIM_INTERVAL.
                  # 2. Pool parameters are being ramped.
              
                  last_claim_time: uint256 = self.last_admin_fee_claim_timestamp
                  if (
                      unsafe_sub(block.timestamp, last_claim_time) < MIN_ADMIN_FEE_CLAIM_INTERVAL or
                      self.future_A_gamma_time > block.timestamp
                  ):
                      return
              
                  xcp_profit: uint256 = self.xcp_profit  # <---------- Current pool profits.
                  xcp_profit_a: uint256 = self.xcp_profit_a  # <- Profits at previous claim.
                  current_lp_token_supply: uint256 = self.totalSupply
              
                  # Do not claim admin fees if:
                  # 1. insufficient profits accrued since last claim, and
                  # 2. there are less than 10**18 (or 1 unit of) lp tokens, else it can lead
                  #    to manipulated virtual prices.
              
                  if xcp_profit <= xcp_profit_a or current_lp_token_supply < 10**18:
                      return
              
                  # ---------- Conditions met to claim admin fees: compute state. ----------
              
                  A_gamma: uint256[2] = self._A_gamma()
                  D: uint256 = self.D
                  vprice: uint256 = self.virtual_price
                  price_scale: uint256 = self.cached_price_scale
                  fee_receiver: address = factory.fee_receiver()
                  balances: uint256[N_COINS] = self.balances
              
                  #  Admin fees are calculated as follows.
                  #      1. Calculate accrued profit since last claim. `xcp_profit`
                  #         is the current profits. `xcp_profit_a` is the profits
                  #         at the previous claim.
                  #      2. Take out admin's share, which is hardcoded at 5 * 10**9.
                  #         (50% => half of 100% => 10**10 / 2 => 5 * 10**9).
                  #      3. Since half of the profits go to rebalancing the pool, we
                  #         are left with half; so divide by 2.
              
                  fees: uint256 = unsafe_div(
                      unsafe_sub(xcp_profit, xcp_profit_a) * ADMIN_FEE, 2 * 10**10
                  )
              
                  # ------------------------------ Claim admin fees by minting admin's share
                  #                                                of the pool in LP tokens.
              
                  # This is the admin fee tokens claimed in self.add_liquidity. We add it to
                  # the LP token share that the admin needs to claim:
                  admin_share: uint256 = self.admin_lp_virtual_balance
                  frac: uint256 = 0
                  if fee_receiver != empty(address) and fees > 0:
              
                      # -------------------------------- Calculate admin share to be minted.
                      frac = vprice * 10**18 / (vprice - fees) - 10**18
                      admin_share += current_lp_token_supply * frac / 10**18
              
                      # ------ Subtract fees from profits that will be used for rebalancing.
                      xcp_profit -= fees * 2
              
                  # ------------------- Recalculate virtual_price following admin fee claim.
                  total_supply_including_admin_share: uint256 = (
                      current_lp_token_supply + admin_share
                  )
                  vprice = (
                      10**18 * self.get_xcp(D, price_scale) /
                      total_supply_including_admin_share
                  )
              
                  # Do not claim fees if doing so causes virtual price to drop below 10**18.
                  if vprice < 10**18:
                      return
              
                  # ---------------------------- Update State ------------------------------
              
                  # Set admin virtual LP balances to zero because we claimed:
                  self.admin_lp_virtual_balance = 0
              
                  self.xcp_profit = xcp_profit
                  self.last_admin_fee_claim_timestamp = block.timestamp
              
                  # Since we reduce balances: virtual price goes down
                  self.virtual_price = vprice
              
                  # Adjust D after admin seemingly removes liquidity
                  self.D = D - unsafe_div(D * admin_share, total_supply_including_admin_share)
              
                  if xcp_profit > xcp_profit_a:
                      self.xcp_profit_a = xcp_profit  # <-------- Cache last claimed profit.
              
                  # --------------------------- Handle Transfers ---------------------------
              
                  admin_tokens: uint256[N_COINS] = empty(uint256[N_COINS])
                  if admin_share > 0:
              
                      for i in range(N_COINS):
              
                          admin_tokens[i] = (
                              balances[i] * admin_share /
                              total_supply_including_admin_share
                          )
              
                          # _transfer_out tokens to admin and update self.balances. State
                          # update to self.balances occurs before external contract calls:
                          self._transfer_out(i, admin_tokens[i], fee_receiver)
              
                      log ClaimAdminFee(fee_receiver, admin_tokens)
              
              
              @internal
              @pure
              def xp(
                  balances: uint256[N_COINS],
                  price_scale: uint256,
              ) -> uint256[N_COINS]:
              
                  return [
                      balances[0] * PRECISIONS[0],
                      unsafe_div(balances[1] * PRECISIONS[1] * price_scale, PRECISION)
                  ]
              
              
              @view
              @internal
              def _A_gamma() -> uint256[2]:
                  t1: uint256 = self.future_A_gamma_time
              
                  A_gamma_1: uint256 = self.future_A_gamma
                  gamma1: uint256 = A_gamma_1 & 2**128 - 1
                  A1: uint256 = A_gamma_1 >> 128
              
                  if block.timestamp < t1:
              
                      # --------------- Handle ramping up and down of A --------------------
              
                      A_gamma_0: uint256 = self.initial_A_gamma
                      t0: uint256 = self.initial_A_gamma_time
              
                      t1 -= t0
                      t0 = block.timestamp - t0
                      t2: uint256 = t1 - t0
              
                      A1 = ((A_gamma_0 >> 128) * t2 + A1 * t0) / t1
                      gamma1 = ((A_gamma_0 & 2**128 - 1) * t2 + gamma1 * t0) / t1
              
                  return [A1, gamma1]
              
              
              @internal
              @view
              def _fee(xp: uint256[N_COINS]) -> uint256:
              
                  fee_params: uint256[3] = self._unpack_3(self.packed_fee_params)
                  f: uint256 = xp[0] + xp[1]
                  f = fee_params[2] * 10**18 / (
                      fee_params[2] + 10**18 -
                      (10**18 * N_COINS**N_COINS) * xp[0] / f * xp[1] / f
                  )
              
                  return unsafe_div(
                      fee_params[0] * f + fee_params[1] * (10**18 - f),
                      10**18
                  )
              
              
              @internal
              @pure
              def get_xcp(D: uint256, price_scale: uint256) -> uint256:
              
                  x: uint256[N_COINS] = [
                      unsafe_div(D, N_COINS),
                      D * PRECISION / (price_scale * N_COINS)
                  ]
              
                  return isqrt(x[0] * x[1])  # <------------------- Geometric Mean.
              
              
              @view
              @internal
              def _calc_token_fee(amounts: uint256[N_COINS], xp: uint256[N_COINS]) -> uint256:
                  # fee = sum(amounts_i - avg(amounts)) * fee' / sum(amounts)
                  fee: uint256 = unsafe_div(
                      unsafe_mul(self._fee(xp), N_COINS),
                      unsafe_mul(4, unsafe_sub(N_COINS, 1))
                  )
              
                  S: uint256 = 0
                  for _x in amounts:
                      S += _x
              
                  avg: uint256 = unsafe_div(S, N_COINS)
                  Sdiff: uint256 = 0
              
                  for _x in amounts:
                      if _x > avg:
                          Sdiff += unsafe_sub(_x, avg)
                      else:
                          Sdiff += unsafe_sub(avg, _x)
              
                  return fee * Sdiff / S + NOISE_FEE
              
              
              @internal
              @view
              def _calc_withdraw_one_coin(
                  A_gamma: uint256[2],
                  token_amount: uint256,
                  i: uint256,
                  update_D: bool,
              ) -> (uint256, uint256, uint256[N_COINS], uint256):
              
                  token_supply: uint256 = self.totalSupply
                  assert token_amount <= token_supply  # dev: token amount more than supply
                  assert i < N_COINS  # dev: coin out of range
              
                  xx: uint256[N_COINS] = self.balances
                  D0: uint256 = 0
              
                  # -------------------------- Calculate D0 and xp -------------------------
              
                  price_scale_i: uint256 = self.cached_price_scale * PRECISIONS[1]
                  xp: uint256[N_COINS] = [
                      xx[0] * PRECISIONS[0],
                      unsafe_div(xx[1] * price_scale_i, PRECISION)
                  ]
                  if i == 0:
                      price_scale_i = PRECISION * PRECISIONS[0]
              
                  if update_D:  # <-------------- D is updated if pool is undergoing a ramp.
                      D0 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
                  else:
                      D0 = self.D
              
                  D: uint256 = D0
              
                  # -------------------------------- Fee Calc ------------------------------
              
                  # Charge fees on D. Roughly calculate xp[i] after withdrawal and use that
                  # to calculate fee. Precision is not paramount here: we just want a
                  # behavior where the higher the imbalance caused the more fee the AMM
                  # charges.
              
                  # xp is adjusted assuming xp[0] ~= xp[1] ~= x[2], which is usually not the
                  #  case. We charge self._fee(xp), where xp is an imprecise adjustment post
                  #  withdrawal in one coin. If the withdraw is too large: charge max fee by
                  #   default. This is because the fee calculation will otherwise underflow.
              
                  xp_imprecise: uint256[N_COINS] = xp
                  xp_correction: uint256 = xp[i] * N_COINS * token_amount / token_supply
                  fee: uint256 = self._unpack_3(self.packed_fee_params)[1]  # <- self.out_fee.
              
                  if xp_correction < xp_imprecise[i]:
                      xp_imprecise[i] -= xp_correction
                      fee = self._fee(xp_imprecise)
              
                  dD: uint256 = unsafe_div(token_amount * D, token_supply)
                  D_fee: uint256 = fee * dD / (2 * 10**10) + 1  # <------- Actual fee on D.
              
                  # --------- Calculate `approx_fee` (assuming balanced state) in ith token.
                  # -------------------------------- We only need this for fee in the event.
                  approx_fee: uint256 = N_COINS * D_fee * xx[i] / D  # <------------------<---------- TODO: Check math.
              
                  # ------------------------------------------------------------------------
                  D -= (dD - D_fee)  # <----------------------------------- Charge fee on D.
                  # --------------------------------- Calculate `y_out`` with `(D - D_fee)`.
                  y: uint256 = MATH.get_y(A_gamma[0], A_gamma[1], xp, D, i)[0]
                  dy: uint256 = (xp[i] - y) * PRECISION / price_scale_i
                  xp[i] = y
              
                  return dy, D, xp, approx_fee
              
              
              # ------------------------ ERC20 functions -----------------------------------
              
              
              @internal
              def _approve(_owner: address, _spender: address, _value: uint256):
                  self.allowance[_owner][_spender] = _value
              
                  log Approval(_owner, _spender, _value)
              
              
              @internal
              def _transfer(_from: address, _to: address, _value: uint256):
                  assert _to not in [self, empty(address)]
              
                  self.balanceOf[_from] -= _value
                  self.balanceOf[_to] += _value
              
                  log Transfer(_from, _to, _value)
              
              
              @view
              @internal
              def _domain_separator() -> bytes32:
                  if chain.id != CACHED_CHAIN_ID:
                      return keccak256(
                          _abi_encode(
                              EIP712_TYPEHASH,
                              NAME_HASH,
                              VERSION_HASH,
                              chain.id,
                              self,
                              salt,
                          )
                      )
                  return CACHED_DOMAIN_SEPARATOR
              
              
              @external
              def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
                  """
                  @dev Transfer tokens from one address to another.
                  @param _from address The address which you want to send tokens from
                  @param _to address The address which you want to transfer to
                  @param _value uint256 the amount of tokens to be transferred
                  @return bool True on successul transfer. Reverts otherwise.
                  """
                  _allowance: uint256 = self.allowance[_from][msg.sender]
                  if _allowance != max_value(uint256):
                      self._approve(_from, msg.sender, _allowance - _value)
              
                  self._transfer(_from, _to, _value)
                  return True
              
              
              @external
              def transfer(_to: address, _value: uint256) -> bool:
                  """
                  @dev Transfer token for a specified address
                  @param _to The address to transfer to.
                  @param _value The amount to be transferred.
                  @return bool True on successful transfer. Reverts otherwise.
                  """
                  self._transfer(msg.sender, _to, _value)
                  return True
              
              
              @external
              def approve(_spender: address, _value: uint256) -> bool:
                  """
                  @notice Allow `_spender` to transfer up to `_value` amount
                          of tokens from the caller's account.
                  @param _spender The account permitted to spend up to `_value` amount of
                                  caller's funds.
                  @param _value The amount of tokens `_spender` is allowed to spend.
                  @return bool Success
                  """
                  self._approve(msg.sender, _spender, _value)
                  return True
              
              
              @external
              def permit(
                  _owner: address,
                  _spender: address,
                  _value: uint256,
                  _deadline: uint256,
                  _v: uint8,
                  _r: bytes32,
                  _s: bytes32,
              ) -> bool:
                  """
                  @notice Permit `_spender` to spend up to `_value` amount of `_owner`'s
                          tokens via a signature.
                  @dev In the event of a chain fork, replay attacks are prevented as
                       domain separator is recalculated. However, this is only if the
                       resulting chains update their chainId.
                  @param _owner The account which generated the signature and is granting an
                                allowance.
                  @param _spender The account which will be granted an allowance.
                  @param _value The approval amount.
                  @param _deadline The deadline by which the signature must be submitted.
                  @param _v The last byte of the ECDSA signature.
                  @param _r The first 32 bytes of the ECDSA signature.
                  @param _s The second 32 bytes of the ECDSA signature.
                  @return bool Success.
                  """
                  assert _owner != empty(address)  # dev: invalid owner
                  assert block.timestamp <= _deadline  # dev: permit expired
              
                  nonce: uint256 = self.nonces[_owner]
                  digest: bytes32 = keccak256(
                      concat(
                          b"\x19\x01",
                          self._domain_separator(),
                          keccak256(
                              _abi_encode(
                                  EIP2612_TYPEHASH, _owner, _spender, _value, nonce, _deadline
                              )
                          ),
                      )
                  )
                  assert ecrecover(digest, _v, _r, _s) == _owner  # dev: invalid signature
              
                  self.nonces[_owner] = unsafe_add(nonce, 1)  # <-- Unsafe add is safe here.
                  self._approve(_owner, _spender, _value)
                  return True
              
              
              @internal
              def mint(_to: address, _value: uint256) -> bool:
                  """
                  @dev Mint an amount of the token and assigns it to an account.
                       This encapsulates the modification of balances such that the
                       proper events are emitted.
                  @param _to The account that will receive the created tokens.
                  @param _value The amount that will be created.
                  @return bool Success.
                  """
                  self.totalSupply += _value
                  self.balanceOf[_to] += _value
              
                  log Transfer(empty(address), _to, _value)
                  return True
              
              
              @internal
              def burnFrom(_to: address, _value: uint256) -> bool:
                  """
                  @dev Burn an amount of the token from a given account.
                  @param _to The account whose tokens will be burned.
                  @param _value The amount that will be burned.
                  @return bool Success.
                  """
                  self.totalSupply -= _value
                  self.balanceOf[_to] -= _value
              
                  log Transfer(_to, empty(address), _value)
                  return True
              
              
              # ------------------------- AMM View Functions -------------------------------
              
              
              @internal
              @view
              def internal_price_oracle() -> uint256:
                  """
                  @notice Returns the oracle price of the coin at index `k` w.r.t the coin
                          at index 0.
                  @dev The oracle is an exponential moving average, with a periodicity
                       determined by `self.ma_time`. The aggregated prices are cached state
                       prices (dy/dx) calculated AFTER the latest trade.
                  @param k The index of the coin.
                  @return uint256 Price oracle value of kth coin.
                  """
                  price_oracle: uint256 = self.cached_price_oracle
                  price_scale: uint256 = self.cached_price_scale
                  last_prices_timestamp: uint256 = self.last_timestamp
              
                  if last_prices_timestamp < block.timestamp:  # <------------ Update moving
                      #                                                   average if needed.
              
                      last_prices: uint256 = self.last_prices
                      ma_time: uint256 = self._unpack_3(self.packed_rebalancing_params)[2]
                      alpha: uint256 = MATH.wad_exp(
                          -convert(
                              unsafe_sub(block.timestamp, last_prices_timestamp) * 10**18 / ma_time,
                              int256,
                          )
                      )
              
                      # ---- We cap state price that goes into the EMA with 2 x price_scale.
                      return (
                          min(last_prices, 2 * price_scale) * (10**18 - alpha) +
                          price_oracle * alpha
                      ) / 10**18
              
                  return price_oracle
              
              
              @external
              @view
              def fee_receiver() -> address:
                  """
                  @notice Returns the address of the admin fee receiver.
                  @return address Fee receiver.
                  """
                  return factory.fee_receiver()
              
              
              @external
              @view
              def admin() -> address:
                  """
                  @notice Returns the address of the pool's admin.
                  @return address Admin.
                  """
                  return factory.admin()
              
              
              @external
              @view
              def calc_token_amount(amounts: uint256[N_COINS], deposit: bool) -> uint256:
                  """
                  @notice Calculate LP tokens minted or to be burned for depositing or
                          removing `amounts` of coins
                  @dev Includes fee.
                  @param amounts Amounts of tokens being deposited or withdrawn
                  @param deposit True if it is a deposit action, False if withdrawn.
                  @return uint256 Amount of LP tokens deposited or withdrawn.
                  """
                  view_contract: address = factory.views_implementation()
                  return Views(view_contract).calc_token_amount(amounts, deposit, self)
              
              
              @external
              @view
              def get_dy(i: uint256, j: uint256, dx: uint256) -> uint256:
                  """
                  @notice Get amount of coin[j] tokens received for swapping in dx amount of coin[i]
                  @dev Includes fee.
                  @param i index of input token. Check pool.coins(i) to get coin address at ith index
                  @param j index of output token
                  @param dx amount of input coin[i] tokens
                  @return uint256 Exact amount of output j tokens for dx amount of i input tokens.
                  """
                  view_contract: address = factory.views_implementation()
                  return Views(view_contract).get_dy(i, j, dx, self)
              
              
              @external
              @view
              def get_dx(i: uint256, j: uint256, dy: uint256) -> uint256:
                  """
                  @notice Get amount of coin[i] tokens to input for swapping out dy amount
                          of coin[j]
                  @dev This is an approximate method, and returns estimates close to the input
                       amount. Expensive to call on-chain.
                  @param i index of input token. Check pool.coins(i) to get coin address at
                         ith index
                  @param j index of output token
                  @param dy amount of input coin[j] tokens received
                  @return uint256 Approximate amount of input i tokens to get dy amount of j tokens.
                  """
                  view_contract: address = factory.views_implementation()
                  return Views(view_contract).get_dx(i, j, dy, self)
              
              
              @external
              @view
              @nonreentrant("lock")
              def lp_price() -> uint256:
                  """
                  @notice Calculates the current price of the LP token w.r.t coin at the
                          0th index
                  @return uint256 LP price.
                  """
                  return 2 * self.virtual_price * isqrt(self.internal_price_oracle() * 10**18) / 10**18
              
              
              @external
              @view
              @nonreentrant("lock")
              def get_virtual_price() -> uint256:
                  """
                  @notice Calculates the current virtual price of the pool LP token.
                  @dev Not to be confused with `self.virtual_price` which is a cached
                       virtual price.
                  @return uint256 Virtual Price.
                  """
                  return 10**18 * self.get_xcp(self.D, self.cached_price_scale) / self.totalSupply
              
              
              @external
              @view
              @nonreentrant("lock")
              def price_oracle() -> uint256:
                  """
                  @notice Returns the oracle price of the coin at index `k` w.r.t the coin
                          at index 0.
                  @dev The oracle is an exponential moving average, with a periodicity
                       determined by `self.ma_time`. The aggregated prices are cached state
                       prices (dy/dx) calculated AFTER the latest trade.
                  @return uint256 Price oracle value of kth coin.
                  """
                  return self.internal_price_oracle()
              
              
              @external
              @view
              @nonreentrant("lock")
              def price_scale() -> uint256:
                  """
                  @notice Returns the price scale of the coin at index `k` w.r.t the coin
                          at index 0.
                  @dev Price scale determines the price band around which liquidity is
                       concentrated.
                  @return uint256 Price scale of coin.
                  """
                  return self.cached_price_scale
              
              
              @external
              @view
              def fee() -> uint256:
                  """
                  @notice Returns the fee charged by the pool at current state.
                  @dev Not to be confused with the fee charged at liquidity action, since
                       there the fee is calculated on `xp` AFTER liquidity is added or
                       removed.
                  @return uint256 fee bps.
                  """
                  return self._fee(self.xp(self.balances, self.cached_price_scale))
              
              
              @view
              @external
              def calc_withdraw_one_coin(token_amount: uint256, i: uint256) -> uint256:
                  """
                  @notice Calculates output tokens with fee
                  @param token_amount LP Token amount to burn
                  @param i token in which liquidity is withdrawn
                  @return uint256 Amount of ith tokens received for burning token_amount LP tokens.
                  """
              
                  return self._calc_withdraw_one_coin(
                      self._A_gamma(),
                      token_amount,
                      i,
                      (self.future_A_gamma_time > block.timestamp)
                  )[0]
              
              
              @external
              @view
              def calc_token_fee(
                  amounts: uint256[N_COINS], xp: uint256[N_COINS]
              ) -> uint256:
                  """
                  @notice Returns the fee charged on the given amounts for add_liquidity.
                  @param amounts The amounts of coins being added to the pool.
                  @param xp The current balances of the pool multiplied by coin precisions.
                  @return uint256 Fee charged.
                  """
                  return self._calc_token_fee(amounts, xp)
              
              
              @view
              @external
              def A() -> uint256:
                  """
                  @notice Returns the current pool amplification parameter.
                  @return uint256 A param.
                  """
                  return self._A_gamma()[0]
              
              
              @view
              @external
              def gamma() -> uint256:
                  """
                  @notice Returns the current pool gamma parameter.
                  @return uint256 gamma param.
                  """
                  return self._A_gamma()[1]
              
              
              @view
              @external
              def mid_fee() -> uint256:
                  """
                  @notice Returns the current mid fee
                  @return uint256 mid_fee value.
                  """
                  return self._unpack_3(self.packed_fee_params)[0]
              
              
              @view
              @external
              def out_fee() -> uint256:
                  """
                  @notice Returns the current out fee
                  @return uint256 out_fee value.
                  """
                  return self._unpack_3(self.packed_fee_params)[1]
              
              
              @view
              @external
              def fee_gamma() -> uint256:
                  """
                  @notice Returns the current fee gamma
                  @return uint256 fee_gamma value.
                  """
                  return self._unpack_3(self.packed_fee_params)[2]
              
              
              @view
              @external
              def allowed_extra_profit() -> uint256:
                  """
                  @notice Returns the current allowed extra profit
                  @return uint256 allowed_extra_profit value.
                  """
                  return self._unpack_3(self.packed_rebalancing_params)[0]
              
              
              @view
              @external
              def adjustment_step() -> uint256:
                  """
                  @notice Returns the current adjustment step
                  @return uint256 adjustment_step value.
                  """
                  return self._unpack_3(self.packed_rebalancing_params)[1]
              
              
              @view
              @external
              def ma_time() -> uint256:
                  """
                  @notice Returns the current moving average time in seconds
                  @dev To get time in seconds, the parameter is multipled by ln(2)
                       One can expect off-by-one errors here.
                  @return uint256 ma_time value.
                  """
                  return self._unpack_3(self.packed_rebalancing_params)[2] * 694 / 1000
              
              
              @view
              @external
              def precisions() -> uint256[N_COINS]:  # <-------------- For by view contract.
                  """
                  @notice Returns the precisions of each coin in the pool.
                  @return uint256[3] precisions of coins.
                  """
                  return PRECISIONS
              
              
              @external
              @view
              def fee_calc(xp: uint256[N_COINS]) -> uint256:  # <----- For by view contract.
                  """
                  @notice Returns the fee charged by the pool at current state.
                  @param xp The current balances of the pool multiplied by coin precisions.
                  @return uint256 Fee value.
                  """
                  return self._fee(xp)
              
              
              @view
              @external
              def DOMAIN_SEPARATOR() -> bytes32:
                  """
                  @notice EIP712 domain separator.
                  @return bytes32 Domain Separator set for the current chain.
                  """
                  return self._domain_separator()
              
              
              # ------------------------- AMM Admin Functions ------------------------------
              
              
              @external
              def ramp_A_gamma(
                  future_A: uint256, future_gamma: uint256, future_time: uint256
              ):
                  """
                  @notice Initialise Ramping A and gamma parameter values linearly.
                  @dev Only accessible by factory admin, and only
                  @param future_A The future A value.
                  @param future_gamma The future gamma value.
                  @param future_time The timestamp at which the ramping will end.
                  """
                  assert msg.sender == factory.admin()  # dev: only owner
                  assert block.timestamp > self.future_A_gamma_time # dev: ramp undergoing
                  assert future_time > block.timestamp + MIN_RAMP_TIME - 1  # dev: insufficient time
              
                  A_gamma: uint256[2] = self._A_gamma()
                  initial_A_gamma: uint256 = A_gamma[0] << 128
                  initial_A_gamma = initial_A_gamma | A_gamma[1]
              
                  assert future_A > MIN_A - 1
                  assert future_A < MAX_A + 1
                  assert future_gamma > MIN_GAMMA - 1
                  assert future_gamma < MAX_GAMMA + 1
              
                  ratio: uint256 = 10**18 * future_A / A_gamma[0]
                  assert ratio < 10**18 * MAX_A_CHANGE + 1 # dev: A change too high
                  assert ratio > 10**18 / MAX_A_CHANGE - 1 # dev: A change too low
              
                  ratio = 10**18 * future_gamma / A_gamma[1]
                  assert ratio < 10**18 * MAX_A_CHANGE + 1 # dev: gamma change too high
                  assert ratio > 10**18 / MAX_A_CHANGE - 1 # dev: gamma change too low
              
                  self.initial_A_gamma = initial_A_gamma
                  self.initial_A_gamma_time = block.timestamp
              
                  future_A_gamma: uint256 = future_A << 128
                  future_A_gamma = future_A_gamma | future_gamma
                  self.future_A_gamma_time = future_time
                  self.future_A_gamma = future_A_gamma
              
                  log RampAgamma(
                      A_gamma[0],
                      future_A,
                      A_gamma[1],
                      future_gamma,
                      block.timestamp,
                      future_time,
                  )
              
              
              @external
              def stop_ramp_A_gamma():
                  """
                  @notice Stop Ramping A and gamma parameters immediately.
                  @dev Only accessible by factory admin.
                  """
                  assert msg.sender == factory.admin()  # dev: only owner
              
                  A_gamma: uint256[2] = self._A_gamma()
                  current_A_gamma: uint256 = A_gamma[0] << 128
                  current_A_gamma = current_A_gamma | A_gamma[1]
                  self.initial_A_gamma = current_A_gamma
                  self.future_A_gamma = current_A_gamma
                  self.initial_A_gamma_time = block.timestamp
                  self.future_A_gamma_time = block.timestamp
              
                  # ------ Now (block.timestamp < t1) is always False, so we return saved A.
              
                  log StopRampA(A_gamma[0], A_gamma[1], block.timestamp)
              
              
              @external
              @nonreentrant('lock')
              def apply_new_parameters(
                  _new_mid_fee: uint256,
                  _new_out_fee: uint256,
                  _new_fee_gamma: uint256,
                  _new_allowed_extra_profit: uint256,
                  _new_adjustment_step: uint256,
                  _new_ma_time: uint256,
              ):
                  """
                  @notice Commit new parameters.
                  @dev Only accessible by factory admin.
                  @param _new_mid_fee The new mid fee.
                  @param _new_out_fee The new out fee.
                  @param _new_fee_gamma The new fee gamma.
                  @param _new_allowed_extra_profit The new allowed extra profit.
                  @param _new_adjustment_step The new adjustment step.
                  @param _new_ma_time The new ma time. ma_time is time_in_seconds/ln(2).
                  """
                  assert msg.sender == factory.admin()  # dev: only owner
              
                  # ----------------------------- Set fee params ---------------------------
              
                  new_mid_fee: uint256 = _new_mid_fee
                  new_out_fee: uint256 = _new_out_fee
                  new_fee_gamma: uint256 = _new_fee_gamma
              
                  current_fee_params: uint256[3] = self._unpack_3(self.packed_fee_params)
              
                  if new_out_fee < MAX_FEE + 1:
                      assert new_out_fee > MIN_FEE - 1  # dev: fee is out of range
                  else:
                      new_out_fee = current_fee_params[1]
              
                  if new_mid_fee > MAX_FEE:
                      new_mid_fee = current_fee_params[0]
                  assert new_mid_fee <= new_out_fee  # dev: mid-fee is too high
              
                  if new_fee_gamma < 10**18:
                      assert new_fee_gamma > 0  # dev: fee_gamma out of range [1 .. 10**18]
                  else:
                      new_fee_gamma = current_fee_params[2]
              
                  self.packed_fee_params = self._pack_3([new_mid_fee, new_out_fee, new_fee_gamma])
              
                  # ----------------- Set liquidity rebalancing parameters -----------------
              
                  new_allowed_extra_profit: uint256 = _new_allowed_extra_profit
                  new_adjustment_step: uint256 = _new_adjustment_step
                  new_ma_time: uint256 = _new_ma_time
              
                  current_rebalancing_params: uint256[3] = self._unpack_3(self.packed_rebalancing_params)
              
                  if new_allowed_extra_profit > 10**18:
                      new_allowed_extra_profit = current_rebalancing_params[0]
              
                  if new_adjustment_step > 10**18:
                      new_adjustment_step = current_rebalancing_params[1]
              
                  if new_ma_time < 872542:  # <----- Calculated as: 7 * 24 * 60 * 60 / ln(2)
                      assert new_ma_time > 86  # dev: MA time should be longer than 60/ln(2)
                  else:
                      new_ma_time = current_rebalancing_params[2]
              
                  self.packed_rebalancing_params = self._pack_3(
                      [new_allowed_extra_profit, new_adjustment_step, new_ma_time]
                  )
              
                  # ---------------------------------- LOG ---------------------------------
              
                  log NewParameters(
                      new_mid_fee,
                      new_out_fee,
                      new_fee_gamma,
                      new_allowed_extra_profit,
                      new_adjustment_step,
                      new_ma_time,
                  )

              File 11 of 16: lvlUSD
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity >=0.8.19;
              import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
              import "@openzeppelin/contracts/access/Ownable2Step.sol";
              import "./interfaces/IlvlUSDDefinitions.sol";
              import "./SingleAdminAccessControl.sol";
              /**
               * @title lvlUSD
               * @notice lvlUSD contract
               */
              contract lvlUSD is
                  ERC20Burnable,
                  ERC20Permit,
                  IlvlUSDDefinitions,
                  SingleAdminAccessControl
              {
                  /// @notice The role that is allowed to denylist and un-denylist addresses
                  bytes32 private constant DENYLIST_MANAGER_ROLE =
                      keccak256("DENYLIST_MANAGER_ROLE");
                  mapping(address => bool) public denylisted;
                  address public minter;
                  constructor(
                      address admin
                  ) ERC20("Level USD", "lvlUSD") ERC20Permit("Level USD") {
                      if (admin == address(0)) revert ZeroAddressException();
                      _grantRole(DEFAULT_ADMIN_ROLE, admin);
                      _grantRole(DENYLIST_MANAGER_ROLE, admin);
                  }
                  modifier notOwner(address account) {
                      if (hasRole(DEFAULT_ADMIN_ROLE, account)) revert IsOwner();
                      _;
                  }
                  function setMinter(
                      address newMinter
                  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                      emit MinterUpdated(newMinter, minter);
                      minter = newMinter;
                  }
                  function mint(address to, uint256 amount) external {
                      if (msg.sender != minter) revert OnlyMinter();
                      _mint(to, amount);
                  }
                  /**
                   * @dev Remove renounce role access from AccessControl, to prevent users from resigning from roles.
                   */
                  function renounceRole(bytes32, address) public virtual override {
                      revert OperationNotAllowed();
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning. Disables transfers from or to of addresses with the DENYLISTED_ROLE role.
                   */
                  function _beforeTokenTransfer(
                      address from,
                      address to,
                      uint256
                  ) internal virtual override {
                      if (denylisted[from] || denylisted[to] || denylisted[msg.sender]) {
                          revert Denylisted();
                      }
                  }
                  /**
                   * @notice Allows the owner (DEFAULT_ADMIN_ROLE) and denylist managers to denylist addresses.
                   * @param target The address to denylist.
                   */
                  function addToDenylist(
                      address target
                  ) external onlyRole(DENYLIST_MANAGER_ROLE) notOwner(target) {
                      denylisted[target] = true;
                  }
                  /**
                   * @notice Allows denylist managers to remove addresses from the denylist.
                   * @param target The address to remove from the denylist.
                   */
                  function removeFromDenylist(
                      address target
                  ) external onlyRole(DENYLIST_MANAGER_ROLE) {
                      denylisted[target] = false;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "./IERC20.sol";
              import "./extensions/IERC20Metadata.sol";
              import "../../utils/Context.sol";
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               * For a generic mechanism see {ERC20PresetMinterPauser}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * The default value of {decimals} is 18. To change this, you should override
               * this function so it returns a different value.
               *
               * We have followed general OpenZeppelin Contracts guidelines: functions revert
               * instead returning `false` on failure. This behavior is nonetheless
               * conventional and does not conflict with the expectations of ERC20
               * applications.
               *
               * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
               * This allows applications to reconstruct the allowance for all accounts just
               * by listening to said events. Other implementations of the EIP may not emit
               * these events, as it isn't required by the specification.
               *
               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
               * functions have been added to mitigate the well-known issues around setting
               * allowances. See {IERC20-approve}.
               */
              contract ERC20 is Context, IERC20, IERC20Metadata {
                  mapping(address => uint256) private _balances;
                  mapping(address => mapping(address => uint256)) private _allowances;
                  uint256 private _totalSupply;
                  string private _name;
                  string private _symbol;
                  /**
                   * @dev Sets the values for {name} and {symbol}.
                   *
                   * All two of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev Returns the number of decimals used to get its user representation.
                   * For example, if `decimals` equals `2`, a balance of `505` tokens should
                   * be displayed to a user as `5.05` (`505 / 10 ** 2`).
                   *
                   * Tokens usually opt for a value of 18, imitating the relationship between
                   * Ether and Wei. This is the default value returned by this function, unless
                   * it's overridden.
                   *
                   * NOTE: This information is only used for _display_ purposes: it in
                   * no way affects any of the arithmetic of the contract, including
                   * {IERC20-balanceOf} and {IERC20-transfer}.
                   */
                  function decimals() public view virtual override returns (uint8) {
                      return 18;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual override returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address to, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) public virtual override returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Emits an {Approval} event indicating the updated allowance. This is not
                   * required by the EIP. See the note at the beginning of {ERC20}.
                   *
                   * NOTE: Does not update the allowance if the current allowance
                   * is the maximum `uint256`.
                   *
                   * Requirements:
                   *
                   * - `from` and `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``from``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
                      address spender = _msgSender();
                      _spendAllowance(from, spender, amount);
                      _transfer(from, to, amount);
                      return true;
                  }
                  /**
                   * @dev Atomically increases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, allowance(owner, spender) + addedValue);
                      return true;
                  }
                  /**
                   * @dev Atomically decreases the allowance granted to `spender` by the caller.
                   *
                   * This is an alternative to {approve} that can be used as a mitigation for
                   * problems described in {IERC20-approve}.
                   *
                   * Emits an {Approval} event indicating the updated allowance.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `spender` must have allowance for the caller of at least
                   * `subtractedValue`.
                   */
                  function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
                      address owner = _msgSender();
                      uint256 currentAllowance = allowance(owner, spender);
                      require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
                      unchecked {
                          _approve(owner, spender, currentAllowance - subtractedValue);
                      }
                      return true;
                  }
                  /**
                   * @dev Moves `amount` of tokens from `from` to `to`.
                   *
                   * This internal function is equivalent to {transfer}, and can be used to
                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                   *
                   * Emits a {Transfer} event.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   */
                  function _transfer(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), "ERC20: transfer from the zero address");
                      require(to != address(0), "ERC20: transfer to the zero address");
                      _beforeTokenTransfer(from, to, amount);
                      uint256 fromBalance = _balances[from];
                      require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                      unchecked {
                          _balances[from] = fromBalance - amount;
                          // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
                          // decrementing then incrementing.
                          _balances[to] += amount;
                      }
                      emit Transfer(from, to, amount);
                      _afterTokenTransfer(from, to, amount);
                  }
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
                      _beforeTokenTransfer(address(0), account, amount);
                      _totalSupply += amount;
                      unchecked {
                          // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
                          _balances[account] += amount;
                      }
                      emit Transfer(address(0), account, amount);
                      _afterTokenTransfer(address(0), account, amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
                      _beforeTokenTransfer(account, address(0), amount);
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                      unchecked {
                          _balances[account] = accountBalance - amount;
                          // Overflow not possible: amount <= accountBalance <= totalSupply.
                          _totalSupply -= amount;
                      }
                      emit Transfer(account, address(0), amount);
                      _afterTokenTransfer(account, address(0), amount);
                  }
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                   *
                   * This internal function is equivalent to `approve`, and can be used to
                   * e.g. set automatic allowances for certain subsystems, etc.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `owner` cannot be the zero address.
                   * - `spender` cannot be the zero address.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      require(owner != address(0), "ERC20: approve from the zero address");
                      require(spender != address(0), "ERC20: approve to the zero address");
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                  }
                  /**
                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                   *
                   * Does not update the allowance amount in case of infinite allowance.
                   * Revert if not enough allowance is available.
                   *
                   * Might emit an {Approval} event.
                   */
                  function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                      uint256 currentAllowance = allowance(owner, spender);
                      if (currentAllowance != type(uint256).max) {
                          require(currentAllowance >= amount, "ERC20: insufficient allowance");
                          unchecked {
                              _approve(owner, spender, currentAllowance - amount);
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  /**
                   * @dev Hook that is called after any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * has been transferred to `to`.
                   * - when `from` is zero, `amount` tokens have been minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)
              pragma solidity ^0.8.0;
              import "../ERC20.sol";
              import "../../../utils/Context.sol";
              /**
               * @dev Extension of {ERC20} that allows token holders to destroy both their own
               * tokens and those that they have an allowance for, in a way that can be
               * recognized off-chain (via event analysis).
               */
              abstract contract ERC20Burnable is Context, ERC20 {
                  /**
                   * @dev Destroys `amount` tokens from the caller.
                   *
                   * See {ERC20-_burn}.
                   */
                  function burn(uint256 amount) public virtual {
                      _burn(_msgSender(), amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, deducting from the caller's
                   * allowance.
                   *
                   * See {ERC20-_burn} and {ERC20-allowance}.
                   *
                   * Requirements:
                   *
                   * - the caller must have allowance for ``accounts``'s tokens of at least
                   * `amount`.
                   */
                  function burnFrom(address account, uint256 amount) public virtual {
                      _spendAllowance(account, _msgSender(), amount);
                      _burn(account, amount);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/ERC20Permit.sol)
              pragma solidity ^0.8.0;
              import "./IERC20Permit.sol";
              import "../ERC20.sol";
              import "../../../utils/cryptography/ECDSA.sol";
              import "../../../utils/cryptography/EIP712.sol";
              import "../../../utils/Counters.sol";
              /**
               * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               *
               * _Available since v3.4._
               */
              abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
                  using Counters for Counters.Counter;
                  mapping(address => Counters.Counter) private _nonces;
                  // solhint-disable-next-line var-name-mixedcase
                  bytes32 private constant _PERMIT_TYPEHASH =
                      keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                  /**
                   * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`.
                   * However, to ensure consistency with the upgradeable transpiler, we will continue
                   * to reserve a slot.
                   * @custom:oz-renamed-from _PERMIT_TYPEHASH
                   */
                  // solhint-disable-next-line var-name-mixedcase
                  bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT;
                  /**
                   * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
                   *
                   * It's a good idea to use the same `name` that is defined as the ERC20 token name.
                   */
                  constructor(string memory name) EIP712(name, "1") {}
                  /**
                   * @dev See {IERC20Permit-permit}.
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) public virtual override {
                      require(block.timestamp <= deadline, "ERC20Permit: expired deadline");
                      bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));
                      bytes32 hash = _hashTypedDataV4(structHash);
                      address signer = ECDSA.recover(hash, v, r, s);
                      require(signer == owner, "ERC20Permit: invalid signature");
                      _approve(owner, spender, value);
                  }
                  /**
                   * @dev See {IERC20Permit-nonces}.
                   */
                  function nonces(address owner) public view virtual override returns (uint256) {
                      return _nonces[owner].current();
                  }
                  /**
                   * @dev See {IERC20Permit-DOMAIN_SEPARATOR}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view override returns (bytes32) {
                      return _domainSeparatorV4();
                  }
                  /**
                   * @dev "Consume a nonce": return the current value and increment.
                   *
                   * _Available since v4.1._
                   */
                  function _useNonce(address owner) internal virtual returns (uint256 current) {
                      Counters.Counter storage nonce = _nonces[owner];
                      current = nonce.current();
                      nonce.increment();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
              pragma solidity ^0.8.0;
              import "./Ownable.sol";
              /**
               * @dev Contract module which provides 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} and {acceptOwnership}.
               *
               * This module is used through inheritance. It will make available all functions
               * from parent (Ownable).
               */
              abstract contract Ownable2Step is Ownable {
                  address private _pendingOwner;
                  event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Returns the address of the pending owner.
                   */
                  function pendingOwner() public view virtual returns (address) {
                      return _pendingOwner;
                  }
                  /**
                   * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual override onlyOwner {
                      _pendingOwner = newOwner;
                      emit OwnershipTransferStarted(owner(), newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual override {
                      delete _pendingOwner;
                      super._transferOwnership(newOwner);
                  }
                  /**
                   * @dev The new owner accepts the ownership transfer.
                   */
                  function acceptOwnership() public virtual {
                      address sender = _msgSender();
                      require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
                      _transferOwnership(sender);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity >=0.8.19;
              /// @dev Changelog: changed solidity version and name
              interface IlvlUSDDefinitions {
                  /// @notice This event is fired when the minter changes
                  event MinterUpdated(address indexed newMinter, address indexed oldMinter);
                  /// @notice This event is fired when the slasher changes
                  event SlasherUpdated(
                      address indexed newSlasher,
                      address indexed oldSlasher
                  );
                  /// @notice Zero address not allowed
                  error ZeroAddressException();
                  /// @notice It's not possible to renounce the ownership
                  error OperationNotAllowed();
                  /// @notice Only the minter role can perform an action
                  error OnlyMinter();
                  /// @notice Address is denylisted
                  error Denylisted();
                  /// @notice Address is owner
                  error IsOwner();
              }
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity >=0.8.19;
              import "@openzeppelin/contracts/access/AccessControl.sol";
              import "@openzeppelin/contracts/interfaces/IERC5313.sol";
              import "./interfaces/ISingleAdminAccessControl.sol";
              /**
               * @title SingleAdminAccessControl
               * @notice SingleAdminAccessControl is a contract that provides a single admin role
               * @notice This contract is a simplified alternative to OpenZeppelin's AccessControlDefaultAdminRules
               * @dev Changelog: update solidity versions
               */
              abstract contract SingleAdminAccessControl is
                  IERC5313,
                  ISingleAdminAccessControl,
                  AccessControl
              {
                  address private _currentDefaultAdmin;
                  address private _pendingDefaultAdmin;
                  modifier notAdmin(bytes32 role) {
                      if (role == DEFAULT_ADMIN_ROLE) revert InvalidAdminChange();
                      _;
                  }
                  /// @notice Transfer the admin role to a new address
                  /// @notice This can ONLY be executed by the current admin
                  /// @param newAdmin address
                  function transferAdmin(
                      address newAdmin
                  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
                      if (newAdmin == msg.sender) revert InvalidAdminChange();
                      _pendingDefaultAdmin = newAdmin;
                      emit AdminTransferRequested(_currentDefaultAdmin, newAdmin);
                  }
                  function acceptAdmin() external {
                      if (msg.sender != _pendingDefaultAdmin) revert NotPendingAdmin();
                      _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
                  }
                  /// @notice grant a role
                  /// @notice can only be executed by the current single admin
                  /// @notice admin role cannot be granted externally
                  /// @param role bytes32
                  /// @param account address
                  function grantRole(
                      bytes32 role,
                      address account
                  ) public override onlyRole(DEFAULT_ADMIN_ROLE) notAdmin(role) {
                      _grantRole(role, account);
                  }
                  /// @notice revoke a role
                  /// @notice can only be executed by the current admin
                  /// @notice admin role cannot be revoked
                  /// @param role bytes32
                  /// @param account address
                  function revokeRole(
                      bytes32 role,
                      address account
                  ) public override onlyRole(DEFAULT_ADMIN_ROLE) notAdmin(role) {
                      _revokeRole(role, account);
                  }
                  /// @notice renounce the role of msg.sender
                  /// @notice admin role cannot be renounced
                  /// @param role bytes32
                  /// @param account address
                  function renounceRole(
                      bytes32 role,
                      address account
                  ) public virtual override notAdmin(role) {
                      super.renounceRole(role, account);
                  }
                  /**
                   * @dev See {IERC5313-owner}.
                   */
                  function owner() public view virtual returns (address) {
                      return _currentDefaultAdmin;
                  }
                  /**
                   * @notice no way to change admin without removing old admin first
                   */
                  function _grantRole(bytes32 role, address account) internal override {
                      if (role == DEFAULT_ADMIN_ROLE) {
                          emit AdminTransferred(_currentDefaultAdmin, account);
                          _revokeRole(DEFAULT_ADMIN_ROLE, _currentDefaultAdmin);
                          _currentDefaultAdmin = account;
                          delete _pendingDefaultAdmin;
                      }
                      super._grantRole(role, account);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 amount) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                   * @dev Moves `amount` tokens from `from` to `to` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 amount) external returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC20.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC20 standard.
               *
               * _Available since v4.1._
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               */
              interface IERC20Permit {
                  /**
                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                   * given ``owner``'s signed approval.
                   *
                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                   * ordering also apply here.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `deadline` must be a timestamp in the future.
                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                   * over the EIP712-formatted function arguments.
                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                   *
                   * For more information on the signature format, see the
                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                   * section].
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
                  /**
                   * @dev Returns the current nonce for `owner`. This value must be
                   * included whenever a signature is generated for {permit}.
                   *
                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                   * prevents a signature from being used multiple times.
                   */
                  function nonces(address owner) external view returns (uint256);
                  /**
                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
              pragma solidity ^0.8.0;
              import "../Strings.sol";
              /**
               * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
               *
               * These functions can be used to verify that a message was signed by the holder
               * of the private keys of a given address.
               */
              library ECDSA {
                  enum RecoverError {
                      NoError,
                      InvalidSignature,
                      InvalidSignatureLength,
                      InvalidSignatureS,
                      InvalidSignatureV // Deprecated in v4.8
                  }
                  function _throwError(RecoverError error) private pure {
                      if (error == RecoverError.NoError) {
                          return; // no error: do nothing
                      } else if (error == RecoverError.InvalidSignature) {
                          revert("ECDSA: invalid signature");
                      } else if (error == RecoverError.InvalidSignatureLength) {
                          revert("ECDSA: invalid signature length");
                      } else if (error == RecoverError.InvalidSignatureS) {
                          revert("ECDSA: invalid signature 's' value");
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature` or error string. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   *
                   * Documentation for signature generation:
                   * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
                   * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
                      if (signature.length == 65) {
                          bytes32 r;
                          bytes32 s;
                          uint8 v;
                          // ecrecover takes the signature parameters, and the only way to get them
                          // currently is to use assembly.
                          /// @solidity memory-safe-assembly
                          assembly {
                              r := mload(add(signature, 0x20))
                              s := mload(add(signature, 0x40))
                              v := byte(0, mload(add(signature, 0x60)))
                          }
                          return tryRecover(hash, v, r, s);
                      } else {
                          return (address(0), RecoverError.InvalidSignatureLength);
                      }
                  }
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature`. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   */
                  function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, signature);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
                   *
                   * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
                      bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                      uint8 v = uint8((uint256(vs) >> 255) + 27);
                      return tryRecover(hash, v, r, s);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
                   *
                   * _Available since v4.2._
                   */
                  function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, r, vs);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   *
                   * _Available since v4.3._
                   */
                  function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
                      // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
                      // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
                      // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
                      // signatures from current libraries generate a unique signature with an s-value in the lower half order.
                      //
                      // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
                      // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
                      // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
                      // these malleable signatures as well.
                      if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
                          return (address(0), RecoverError.InvalidSignatureS);
                      }
                      // If the signature is valid (and not malleable), return the signer address
                      address signer = ecrecover(hash, v, r, s);
                      if (signer == address(0)) {
                          return (address(0), RecoverError.InvalidSignature);
                      }
                      return (signer, RecoverError.NoError);
                  }
                  /**
                   * @dev Overload of {ECDSA-recover} that receives the `v`,
                   * `r` and `s` signature fields separately.
                   */
                  function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
                      (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
                      _throwError(error);
                      return recovered;
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
                      // 32 is the length in bytes of hash,
                      // enforced by the type signature above
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(0x00, "\\x19Ethereum Signed Message:\
              32")
                          mstore(0x1c, hash)
                          message := keccak256(0x00, 0x3c)
                      }
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from `s`. This
                   * produces hash corresponding to the one signed with the
                   * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
                   * JSON-RPC method as part of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              ", Strings.toString(s.length), s));
                  }
                  /**
                   * @dev Returns an Ethereum Signed Typed Data, created from a
                   * `domainSeparator` and a `structHash`. This produces hash corresponding
                   * to the one signed with the
                   * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
                   * JSON-RPC method as part of EIP-712.
                   *
                   * See {recover}.
                   */
                  function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          let ptr := mload(0x40)
                          mstore(ptr, "\\x19\\x01")
                          mstore(add(ptr, 0x02), domainSeparator)
                          mstore(add(ptr, 0x22), structHash)
                          data := keccak256(ptr, 0x42)
                      }
                  }
                  /**
                   * @dev Returns an Ethereum Signed Data with intended validator, created from a
                   * `validator` and `data` according to the version 0 of EIP-191.
                   *
                   * See {recover}.
                   */
                  function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked("\\x19\\x00", validator, data));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
              pragma solidity ^0.8.8;
              import "./ECDSA.sol";
              import "../ShortStrings.sol";
              import "../../interfaces/IERC5267.sol";
              /**
               * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
               *
               * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
               * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
               * they need in their contracts using a combination of `abi.encode` and `keccak256`.
               *
               * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
               * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
               * ({_hashTypedDataV4}).
               *
               * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
               * the chain id to protect against replay attacks on an eventual fork of the chain.
               *
               * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
               * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
               *
               * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
               * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
               * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
               *
               * _Available since v3.4._
               *
               * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
               */
              abstract contract EIP712 is IERC5267 {
                  using ShortStrings for *;
                  bytes32 private constant _TYPE_HASH =
                      keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
                  // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
                  // invalidate the cached domain separator if the chain id changes.
                  bytes32 private immutable _cachedDomainSeparator;
                  uint256 private immutable _cachedChainId;
                  address private immutable _cachedThis;
                  bytes32 private immutable _hashedName;
                  bytes32 private immutable _hashedVersion;
                  ShortString private immutable _name;
                  ShortString private immutable _version;
                  string private _nameFallback;
                  string private _versionFallback;
                  /**
                   * @dev Initializes the domain separator and parameter caches.
                   *
                   * The meaning of `name` and `version` is specified in
                   * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
                   *
                   * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
                   * - `version`: the current major version of the signing domain.
                   *
                   * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
                   * contract upgrade].
                   */
                  constructor(string memory name, string memory version) {
                      _name = name.toShortStringWithFallback(_nameFallback);
                      _version = version.toShortStringWithFallback(_versionFallback);
                      _hashedName = keccak256(bytes(name));
                      _hashedVersion = keccak256(bytes(version));
                      _cachedChainId = block.chainid;
                      _cachedDomainSeparator = _buildDomainSeparator();
                      _cachedThis = address(this);
                  }
                  /**
                   * @dev Returns the domain separator for the current chain.
                   */
                  function _domainSeparatorV4() internal view returns (bytes32) {
                      if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
                          return _cachedDomainSeparator;
                      } else {
                          return _buildDomainSeparator();
                      }
                  }
                  function _buildDomainSeparator() private view returns (bytes32) {
                      return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
                  }
                  /**
                   * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
                   * function returns the hash of the fully encoded EIP712 message for this domain.
                   *
                   * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
                   *
                   * ```solidity
                   * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
                   *     keccak256("Mail(address to,string contents)"),
                   *     mailTo,
                   *     keccak256(bytes(mailContents))
                   * )));
                   * address signer = ECDSA.recover(digest, signature);
                   * ```
                   */
                  function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
                      return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
                  }
                  /**
                   * @dev See {EIP-5267}.
                   *
                   * _Available since v4.9._
                   */
                  function eip712Domain()
                      public
                      view
                      virtual
                      override
                      returns (
                          bytes1 fields,
                          string memory name,
                          string memory version,
                          uint256 chainId,
                          address verifyingContract,
                          bytes32 salt,
                          uint256[] memory extensions
                      )
                  {
                      return (
                          hex"0f", // 01111
                          _name.toStringWithFallback(_nameFallback),
                          _version.toStringWithFallback(_versionFallback),
                          block.chainid,
                          address(this),
                          bytes32(0),
                          new uint256[](0)
                      );
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
              pragma solidity ^0.8.0;
              /**
               * @title Counters
               * @author Matt Condon (@shrugs)
               * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
               * of elements in a mapping, issuing ERC721 ids, or counting request ids.
               *
               * Include with `using Counters for Counters.Counter;`
               */
              library Counters {
                  struct Counter {
                      // This variable should never be directly accessed by users of the library: interactions must be restricted to
                      // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
                      // this feature: see https://github.com/ethereum/solidity/issues/4637
                      uint256 _value; // default: 0
                  }
                  function current(Counter storage counter) internal view returns (uint256) {
                      return counter._value;
                  }
                  function increment(Counter storage counter) internal {
                      unchecked {
                          counter._value += 1;
                      }
                  }
                  function decrement(Counter storage counter) internal {
                      uint256 value = counter._value;
                      require(value > 0, "Counter: decrement overflow");
                      unchecked {
                          counter._value = value - 1;
                      }
                  }
                  function reset(Counter storage counter) internal {
                      counter._value = 0;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              abstract contract Ownable is Context {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  constructor() {
                      _transferOwnership(_msgSender());
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      _checkOwner();
                      _;
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if the sender is not the owner.
                   */
                  function _checkOwner() internal view virtual {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby disabling any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      _transferOwnership(address(0));
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual {
                      address oldOwner = _owner;
                      _owner = newOwner;
                      emit OwnershipTransferred(oldOwner, newOwner);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControl.sol";
              import "../utils/Context.sol";
              import "../utils/Strings.sol";
              import "../utils/introspection/ERC165.sol";
              /**
               * @dev Contract module that allows children to implement role-based access
               * control mechanisms. This is a lightweight version that doesn't allow enumerating role
               * members except through off-chain means by accessing the contract event logs. Some
               * applications may benefit from on-chain enumerability, for those cases see
               * {AccessControlEnumerable}.
               *
               * Roles are referred to by their `bytes32` identifier. These should be exposed
               * in the external API and be unique. The best way to achieve this is by
               * using `public constant` hash digests:
               *
               * ```solidity
               * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
               * ```
               *
               * Roles can be used to represent a set of permissions. To restrict access to a
               * function call, use {hasRole}:
               *
               * ```solidity
               * function foo() public {
               *     require(hasRole(MY_ROLE, msg.sender));
               *     ...
               * }
               * ```
               *
               * Roles can be granted and revoked dynamically via the {grantRole} and
               * {revokeRole} functions. Each role has an associated admin role, and only
               * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
               *
               * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
               * that only accounts with this role will be able to grant or revoke other
               * roles. More complex role relationships can be created by using
               * {_setRoleAdmin}.
               *
               * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
               * grant and revoke this role. Extra precautions should be taken to secure
               * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
               * to enforce additional security measures for this role.
               */
              abstract contract AccessControl is Context, IAccessControl, ERC165 {
                  struct RoleData {
                      mapping(address => bool) members;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with a standardized message including the required role.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   *
                   * _Available since v4.1._
                   */
                  modifier onlyRole(bytes32 role) {
                      _checkRole(role);
                      _;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                      return _roles[role].members[account];
                  }
                  /**
                   * @dev Revert with a standard message if `_msgSender()` is missing `role`.
                   * Overriding this function changes the behavior of the {onlyRole} modifier.
                   *
                   * Format of the revert message is described in {_checkRole}.
                   *
                   * _Available since v4.6._
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Revert with a standard message if `account` is missing `role`.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "AccessControl: account ",
                                      Strings.toHexString(account),
                                      " is missing role ",
                                      Strings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                      return _roles[role].adminRole;
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been revoked `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address account) public virtual override {
                      require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event. Note that unlike {grantRole}, this function doesn't perform any
                   * checks on the calling account.
                   *
                   * May emit a {RoleGranted} event.
                   *
                   * [WARNING]
                   * ====
                   * This function should only be called from the constructor when setting
                   * up the initial roles for the system.
                   *
                   * Using this function in any other way is effectively circumventing the admin
                   * system imposed by {AccessControl}.
                   * ====
                   *
                   * NOTE: This function is deprecated in favor of {_grantRole}.
                   */
                  function _setupRole(bytes32 role, address account) internal virtual {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Sets `adminRole` as ``role``'s admin role.
                   *
                   * Emits a {RoleAdminChanged} event.
                   */
                  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                      bytes32 previousAdminRole = getRoleAdmin(role);
                      _roles[role].adminRole = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual {
                      if (!hasRole(role, account)) {
                          _roles[role].members[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                      }
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      if (hasRole(role, account)) {
                          _roles[role].members[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5313.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface for the Light Contract Ownership Standard.
               *
               * A standardized minimal interface required to identify an account that controls a contract
               *
               * _Available since v4.9._
               */
              interface IERC5313 {
                  /**
                   * @dev Gets the address of the owner.
                   */
                  function owner() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.8.19;
              interface ISingleAdminAccessControl {
                  error InvalidAdminChange();
                  error NotPendingAdmin();
                  event AdminTransferred(address indexed oldAdmin, address indexed newAdmin);
                  event AdminTransferRequested(
                      address indexed oldAdmin,
                      address indexed newAdmin
                  );
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              import "./math/Math.sol";
              import "./math/SignedMath.sol";
              /**
               * @dev String operations.
               */
              library Strings {
                  bytes16 private constant _SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          uint256 length = Math.log10(value) + 1;
                          string memory buffer = new string(length);
                          uint256 ptr;
                          /// @solidity memory-safe-assembly
                          assembly {
                              ptr := add(buffer, add(32, length))
                          }
                          while (true) {
                              ptr--;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                              }
                              value /= 10;
                              if (value == 0) break;
                          }
                          return buffer;
                      }
                  }
                  /**
                   * @dev Converts a `int256` to its ASCII `string` decimal representation.
                   */
                  function toString(int256 value) internal pure returns (string memory) {
                      return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          return toHexString(value, Math.log256(value) + 1);
                      }
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                   */
                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                      bytes memory buffer = new bytes(2 * length + 2);
                      buffer[0] = "0";
                      buffer[1] = "x";
                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                          buffer[i] = _SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
                  /**
                   * @dev Returns true if the two strings are equal.
                   */
                  function equal(string memory a, string memory b) internal pure returns (bool) {
                      return keccak256(bytes(a)) == keccak256(bytes(b));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
              pragma solidity ^0.8.8;
              import "./StorageSlot.sol";
              // | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
              // | length  | 0x                                                              BB |
              type ShortString is bytes32;
              /**
               * @dev This library provides functions to convert short memory strings
               * into a `ShortString` type that can be used as an immutable variable.
               *
               * Strings of arbitrary length can be optimized using this library if
               * they are short enough (up to 31 bytes) by packing them with their
               * length (1 byte) in a single EVM word (32 bytes). Additionally, a
               * fallback mechanism can be used for every other case.
               *
               * Usage example:
               *
               * ```solidity
               * contract Named {
               *     using ShortStrings for *;
               *
               *     ShortString private immutable _name;
               *     string private _nameFallback;
               *
               *     constructor(string memory contractName) {
               *         _name = contractName.toShortStringWithFallback(_nameFallback);
               *     }
               *
               *     function name() external view returns (string memory) {
               *         return _name.toStringWithFallback(_nameFallback);
               *     }
               * }
               * ```
               */
              library ShortStrings {
                  // Used as an identifier for strings longer than 31 bytes.
                  bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
                  error StringTooLong(string str);
                  error InvalidShortString();
                  /**
                   * @dev Encode a string of at most 31 chars into a `ShortString`.
                   *
                   * This will trigger a `StringTooLong` error is the input string is too long.
                   */
                  function toShortString(string memory str) internal pure returns (ShortString) {
                      bytes memory bstr = bytes(str);
                      if (bstr.length > 31) {
                          revert StringTooLong(str);
                      }
                      return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
                  }
                  /**
                   * @dev Decode a `ShortString` back to a "normal" string.
                   */
                  function toString(ShortString sstr) internal pure returns (string memory) {
                      uint256 len = byteLength(sstr);
                      // using `new string(len)` would work locally but is not memory safe.
                      string memory str = new string(32);
                      /// @solidity memory-safe-assembly
                      assembly {
                          mstore(str, len)
                          mstore(add(str, 0x20), sstr)
                      }
                      return str;
                  }
                  /**
                   * @dev Return the length of a `ShortString`.
                   */
                  function byteLength(ShortString sstr) internal pure returns (uint256) {
                      uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
                      if (result > 31) {
                          revert InvalidShortString();
                      }
                      return result;
                  }
                  /**
                   * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
                   */
                  function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
                      if (bytes(value).length < 32) {
                          return toShortString(value);
                      } else {
                          StorageSlot.getStringSlot(store).value = value;
                          return ShortString.wrap(_FALLBACK_SENTINEL);
                      }
                  }
                  /**
                   * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
                   */
                  function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
                      if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                          return toString(value);
                      } else {
                          return store;
                      }
                  }
                  /**
                   * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
                   *
                   * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
                   * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
                   */
                  function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
                      if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
                          return byteLength(value);
                      } else {
                          return bytes(store).length;
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
              pragma solidity ^0.8.0;
              interface IERC5267 {
                  /**
                   * @dev MAY be emitted to signal that the domain could have changed.
                   */
                  event EIP712DomainChanged();
                  /**
                   * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
                   * signature.
                   */
                  function eip712Domain()
                      external
                      view
                      returns (
                          bytes1 fields,
                          string memory name,
                          string memory version,
                          uint256 chainId,
                          address verifyingContract,
                          bytes32 salt,
                          uint256[] memory extensions
                      );
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev External interface of AccessControl declared to support ERC165 detection.
               */
              interface IAccessControl {
                  /**
                   * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                   *
                   * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                   * {RoleAdminChanged} not being emitted signaling this.
                   *
                   * _Available since v3.1._
                   */
                  event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                  /**
                   * @dev Emitted when `account` is granted `role`.
                   *
                   * `sender` is the account that originated the contract call, an admin role
                   * bearer except when using {AccessControl-_setupRole}.
                   */
                  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Emitted when `account` is revoked `role`.
                   *
                   * `sender` is the account that originated the contract call:
                   *   - if using `revokeRole`, it is the admin role bearer
                   *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                   */
                  event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) external view returns (bool);
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) external view returns (bytes32);
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function grantRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function revokeRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been granted `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   */
                  function renounceRole(bytes32 role, address account) external;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  enum Rounding {
                      Down, // Toward negative infinity
                      Up, // Toward infinity
                      Zero // Toward zero
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds up instead
                   * of rounding down.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                   * with further edits by Uniswap Labs also under MIT license.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod0 := mul(x, y)
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          require(denominator > prod1, "Math: mulDiv overflow");
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                          // See https://cs.stackexchange.com/q/138556/92363.
                          // Does not overflow because the denominator cannot be zero at this stage in the function.
                          uint256 twos = denominator & (~denominator + 1);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                          // in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard signed math utilities missing in the Solidity language.
               */
              library SignedMath {
                  /**
                   * @dev Returns the largest of two signed numbers.
                   */
                  function max(int256 a, int256 b) internal pure returns (int256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two signed numbers.
                   */
                  function min(int256 a, int256 b) internal pure returns (int256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two signed numbers without overflow.
                   * The result is rounded towards zero.
                   */
                  function average(int256 a, int256 b) internal pure returns (int256) {
                      // Formula from the book "Hacker's Delight"
                      int256 x = (a & b) + ((a ^ b) >> 1);
                      return x + (int256(uint256(x) >> 255) & (a ^ b));
                  }
                  /**
                   * @dev Returns the absolute unsigned value of a signed value.
                   */
                  function abs(int256 n) internal pure returns (uint256) {
                      unchecked {
                          // must be unchecked in order to support `n = type(int256).min`
                          return uint256(n >= 0 ? n : -n);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @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:
               * ```solidity
               * 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`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[EIP].
               *
               * Implementers can declare support of contract interfaces, which can then be
               * queried by others ({ERC165Checker}).
               *
               * For an implementation, see {ERC165}.
               */
              interface IERC165 {
                  /**
                   * @dev Returns true if this contract implements the interface defined by
                   * `interfaceId`. See the corresponding
                   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                   * to learn more about how these ids are created.
                   *
                   * This function call must use less than 30 000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              

              File 12 of 16: CurveStableSwapNG
              # pragma version 0.3.10
              # pragma optimize codesize
              # pragma evm-version shanghai
              """
              @title CurveStableSwapNG
              @author Curve.Fi
              @license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
              @notice Stableswap implementation for up to 8 coins with no rehypothecation,
                      i.e. the AMM does not deposit tokens into other contracts. The Pool contract also
                      records exponential moving averages for coins relative to coin 0.
              @dev Asset Types:
                      0. Standard ERC20 token with no additional features.
                                        Note: Users are advised to do careful due-diligence on
                                              ERC20 tokens that they interact with, as this
                                              contract cannot differentiate between harmless and
                                              malicious ERC20 tokens.
                      1. Oracle - token with rate oracle (e.g. wstETH)
                                  Note: Oracles may be controlled externally by an EOA. Users
                                        are advised to proceed with caution.
                      2. Rebasing - token with rebase (e.g. stETH).
                                    Note: Users and Integrators are advised to understand how
                                          the AMM contract works with rebasing balances.
                      3. ERC4626 - token with convertToAssets method (e.g. sDAI).
                                   Note: Some ERC4626 implementations may be susceptible to
                                         Donation/Inflation attacks. Users are advised to
                                         proceed with caution.
                      NOTE: Pool Cannot support tokens with multiple asset types: e.g. ERC4626
                            with fees are not supported.
                   Supports:
                      1. ERC20 support for return True/revert, return True/False, return None
                      2. ERC20 tokens can have arbitrary decimals (<=18).
                      3. ERC20 tokens that rebase (either positive or fee on transfer)
                      4. ERC20 tokens that have a rate oracle (e.g. wstETH, cbETH, sDAI, etc.)
                         Note: Oracle precision _must_ be 10**18.
                      5. ERC4626 tokens with arbitrary precision (<=18) of Vault token and underlying
                         asset.
                   Additional features include:
                      1. Adds price oracles based on AMM State Price (and _not_ last traded price).
                      2. Adds TVL oracle based on D.
                      3. `exchange_received`: swaps that expect an ERC20 transfer to have occurred
                         prior to executing the swap.
                         Note: a. If pool contains rebasing tokens and one of the `asset_types` is 2 (Rebasing)
                                  then calling `exchange_received` will REVERT.
                               b. If pool contains rebasing token and `asset_types` does not contain 2 (Rebasing)
                                  then this is an incorrect implementation and rebases can be
                                  stolen.
                      4. Adds `get_dx`: Similar to `get_dy` which returns an expected output
                         of coin[j] for given `dx` amount of coin[i], `get_dx` returns expected
                         input of coin[i] for an output amount of coin[j].
                      5. Fees are dynamic: AMM will charge a higher fee if pool depegs. This can cause very
                                           slight discrepancies between calculated fees and realised fees.
              """
              
              from vyper.interfaces import ERC20
              from vyper.interfaces import ERC20Detailed
              from vyper.interfaces import ERC4626
              
              implements: ERC20
              
              # ------------------------------- Interfaces ---------------------------------
              
              interface Factory:
                  def fee_receiver() -> address: view
                  def admin() -> address: view
                  def views_implementation() -> address: view
              
              interface ERC1271:
                  def isValidSignature(_hash: bytes32, _signature: Bytes[65]) -> bytes32: view
              
              interface StableSwapViews:
                  def get_dx(i: int128, j: int128, dy: uint256, pool: address) -> uint256: view
                  def get_dy(i: int128, j: int128, dx: uint256, pool: address) -> uint256: view
                  def dynamic_fee(i: int128, j: int128, pool: address) -> uint256: view
                  def calc_token_amount(
                      _amounts: DynArray[uint256, MAX_COINS],
                      _is_deposit: bool,
                      _pool: address
                  ) -> uint256: view
              
              # --------------------------------- Events -----------------------------------
              
              event Transfer:
                  sender: indexed(address)
                  receiver: indexed(address)
                  value: uint256
              
              event Approval:
                  owner: indexed(address)
                  spender: indexed(address)
                  value: uint256
              
              event TokenExchange:
                  buyer: indexed(address)
                  sold_id: int128
                  tokens_sold: uint256
                  bought_id: int128
                  tokens_bought: uint256
              
              event TokenExchangeUnderlying:
                  buyer: indexed(address)
                  sold_id: int128
                  tokens_sold: uint256
                  bought_id: int128
                  tokens_bought: uint256
              
              event AddLiquidity:
                  provider: indexed(address)
                  token_amounts: DynArray[uint256, MAX_COINS]
                  fees: DynArray[uint256, MAX_COINS]
                  invariant: uint256
                  token_supply: uint256
              
              event RemoveLiquidity:
                  provider: indexed(address)
                  token_amounts: DynArray[uint256, MAX_COINS]
                  fees: DynArray[uint256, MAX_COINS]
                  token_supply: uint256
              
              event RemoveLiquidityOne:
                  provider: indexed(address)
                  token_id: int128
                  token_amount: uint256
                  coin_amount: uint256
                  token_supply: uint256
              
              event RemoveLiquidityImbalance:
                  provider: indexed(address)
                  token_amounts: DynArray[uint256, MAX_COINS]
                  fees: DynArray[uint256, MAX_COINS]
                  invariant: uint256
                  token_supply: uint256
              
              event RampA:
                  old_A: uint256
                  new_A: uint256
                  initial_time: uint256
                  future_time: uint256
              
              event StopRampA:
                  A: uint256
                  t: uint256
              
              event ApplyNewFee:
                  fee: uint256
                  offpeg_fee_multiplier: uint256
              
              event SetNewMATime:
                  ma_exp_time: uint256
                  D_ma_time: uint256
              
              
              MAX_COINS: constant(uint256) = 8  # max coins is 8 in the factory
              MAX_COINS_128: constant(int128) = 8
              
              # ---------------------------- Pool Variables --------------------------------
              
              N_COINS: public(immutable(uint256))
              N_COINS_128: immutable(int128)
              PRECISION: constant(uint256) = 10 ** 18
              
              factory: immutable(Factory)
              coins: public(immutable(DynArray[address, MAX_COINS]))
              asset_types: immutable(DynArray[uint8, MAX_COINS])
              pool_contains_rebasing_tokens: immutable(bool)
              stored_balances: DynArray[uint256, MAX_COINS]
              
              # Fee specific vars
              FEE_DENOMINATOR: constant(uint256) = 10 ** 10
              fee: public(uint256)  # fee * 1e10
              offpeg_fee_multiplier: public(uint256)  # * 1e10
              admin_fee: public(constant(uint256)) = 5000000000
              MAX_FEE: constant(uint256) = 5 * 10 ** 9
              
              # ---------------------- Pool Amplification Parameters -----------------------
              
              A_PRECISION: constant(uint256) = 100
              MAX_A: constant(uint256) = 10 ** 6
              MAX_A_CHANGE: constant(uint256) = 10
              
              initial_A: public(uint256)
              future_A: public(uint256)
              initial_A_time: public(uint256)
              future_A_time: public(uint256)
              
              # ---------------------------- Admin Variables -------------------------------
              
              MIN_RAMP_TIME: constant(uint256) = 86400
              admin_balances: public(DynArray[uint256, MAX_COINS])
              
              # ----------------------- Oracle Specific vars -------------------------------
              
              rate_multipliers: immutable(DynArray[uint256, MAX_COINS])
              # [bytes4 method_id][bytes8 <empty>][bytes20 oracle]
              rate_oracles: immutable(DynArray[uint256, MAX_COINS])
              
              # For ERC4626 tokens, we need:
              call_amount: immutable(DynArray[uint256, MAX_COINS])
              scale_factor: immutable(DynArray[uint256, MAX_COINS])
              
              last_prices_packed: DynArray[uint256, MAX_COINS]  #  packing: last_price, ma_price
              last_D_packed: uint256                            #  packing: last_D, ma_D
              ma_exp_time: public(uint256)
              D_ma_time: public(uint256)
              ma_last_time: public(uint256)                     # packing: ma_last_time_p, ma_last_time_D
              # ma_last_time has a distinction for p and D because p is _not_ updated if
              # users remove_liquidity, but D is.
              
              # shift(2**32 - 1, 224)
              ORACLE_BIT_MASK: constant(uint256) = (2**32 - 1) * 256**28
              
              # --------------------------- ERC20 Specific Vars ----------------------------
              
              name: public(immutable(String[64]))
              symbol: public(immutable(String[32]))
              decimals: public(constant(uint8)) = 18
              version: public(constant(String[8])) = "v7.0.0"
              
              balanceOf: public(HashMap[address, uint256])
              allowance: public(HashMap[address, HashMap[address, uint256]])
              total_supply: uint256
              nonces: public(HashMap[address, uint256])
              
              # keccak256("isValidSignature(bytes32,bytes)")[:4] << 224
              ERC1271_MAGIC_VAL: constant(bytes32) = 0x1626ba7e00000000000000000000000000000000000000000000000000000000
              EIP712_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)")
              EIP2612_TYPEHASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
              
              VERSION_HASH: constant(bytes32) = keccak256(version)
              NAME_HASH: immutable(bytes32)
              CACHED_CHAIN_ID: immutable(uint256)
              salt: public(immutable(bytes32))
              CACHED_DOMAIN_SEPARATOR: immutable(bytes32)
              
              
              # ------------------------------ AMM Setup -----------------------------------
              
              
              @external
              def __init__(
                  _name: String[32],
                  _symbol: String[10],
                  _A: uint256,
                  _fee: uint256,
                  _offpeg_fee_multiplier: uint256,
                  _ma_exp_time: uint256,
                  _coins: DynArray[address, MAX_COINS],
                  _rate_multipliers: DynArray[uint256, MAX_COINS],
                  _asset_types: DynArray[uint8, MAX_COINS],
                  _method_ids: DynArray[bytes4, MAX_COINS],
                  _oracles: DynArray[address, MAX_COINS],
              ):
                  """
                  @notice Initialize the pool contract
                  @param _name Name of the new plain pool.
                  @param _symbol Symbol for the new plain pool.
                  @param _A Amplification co-efficient - a lower value here means
                            less tolerance for imbalance within the pool's assets.
                            Suggested values include:
                             * Uncollateralized algorithmic stablecoins: 5-10
                             * Non-redeemable, collateralized assets: 100
                             * Redeemable assets: 200-400
                  @param _fee Trade fee, given as an integer with 1e10 precision. The
                              the maximum is 1% (100000000).
                              50% of the fee is distributed to veCRV holders.
                  @param _offpeg_fee_multiplier A multiplier that determines how much to increase
                                                Fees by when assets in the AMM depeg. Example value: 20000000000
                  @param _ma_exp_time Averaging window of oracle. Set as time_in_seconds / ln(2)
                                      Example: for 10 minute EMA, _ma_exp_time is 600 / ln(2) ~= 866
                  @param _coins List of addresses of the coins being used in the pool.
                  @param _rate_multipliers An array of: [10 ** (36 - _coins[n].decimals()), ... for n in range(N_COINS)]
                  @param _asset_types Array of uint8 representing tokens in pool
                  @param _method_ids Array of first four bytes of the Keccak-256 hash of the function signatures
                                     of the oracle addresses that gives rate oracles.
                                     Calculated as: keccak(text=event_signature.replace(" ", ""))[:4]
                  @param _oracles Array of rate oracle addresses.
                  """
              
                  coins = _coins
                  asset_types = _asset_types
                  pool_contains_rebasing_tokens = 2 in asset_types
                  __n_coins: uint256 = len(_coins)
                  N_COINS = __n_coins
                  N_COINS_128 = convert(__n_coins, int128)
              
                  rate_multipliers = _rate_multipliers
              
                  factory = Factory(msg.sender)
              
                  A: uint256 = unsafe_mul(_A, A_PRECISION)
                  self.initial_A = A
                  self.future_A = A
                  self.fee = _fee
                  self.offpeg_fee_multiplier = _offpeg_fee_multiplier
              
                  assert _ma_exp_time != 0
                  self.ma_exp_time = _ma_exp_time
                  self.D_ma_time = 62324  # <--------- 12 hours default on contract start.
                  self.ma_last_time = self.pack_2(block.timestamp, block.timestamp)
              
                  #  ------------------- initialize storage for DynArrays ------------------
              
                  _call_amount: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  _scale_factor: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  _rate_oracles: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if i < N_COINS_128 - 1:
                          self.last_prices_packed.append(self.pack_2(10**18, 10**18))
              
                      _rate_oracles.append(convert(_method_ids[i], uint256) * 2**224 | convert(_oracles[i], uint256))
                      self.stored_balances.append(0)
                      self.admin_balances.append(0)
              
                      if _asset_types[i] == 3:
              
                          _call_amount.append(10**convert(ERC20Detailed(_coins[i]).decimals(), uint256))
                          _underlying_asset: address = ERC4626(_coins[i]).asset()
                          _scale_factor.append(10**(18 - convert(ERC20Detailed(_underlying_asset).decimals(), uint256)))
              
                      else:
              
                          _call_amount.append(0)
                          _scale_factor.append(0)
              
                  call_amount = _call_amount
                  scale_factor = _scale_factor
                  rate_oracles = _rate_oracles
              
                  # ----------------------------- ERC20 stuff ------------------------------
              
                  name = _name
                  symbol = _symbol
              
                  # EIP712 related params -----------------
                  NAME_HASH = keccak256(name)
                  salt = block.prevhash
                  CACHED_CHAIN_ID = chain.id
                  CACHED_DOMAIN_SEPARATOR = keccak256(
                      _abi_encode(
                          EIP712_TYPEHASH,
                          NAME_HASH,
                          VERSION_HASH,
                          chain.id,
                          self,
                          salt,
                      )
                  )
              
                  # ------------------------ Fire a transfer event -------------------------
              
                  log Transfer(empty(address), msg.sender, 0)
              
              
              # ------------------ Token transfers in and out of the AMM -------------------
              
              
              @internal
              def _transfer_in(
                  coin_idx: int128,
                  dx: uint256,
                  sender: address,
                  expect_optimistic_transfer: bool,
              ) -> uint256:
                  """
                  @notice Contains all logic to handle ERC20 token transfers.
                  @param coin_idx Index of the coin to transfer in.
                  @param dx amount of `_coin` to transfer into the pool.
                  @param sender address to transfer `_coin` from.
                  @param receiver address to transfer `_coin` to.
                  @param expect_optimistic_transfer True if contract expects an optimistic coin transfer
                  """
                  _dx: uint256 = ERC20(coins[coin_idx]).balanceOf(self)
              
                  # ------------------------- Handle Transfers -----------------------------
              
                  if expect_optimistic_transfer:
              
                      _dx = _dx - self.stored_balances[coin_idx]
                      assert _dx >= dx
              
                  else:
              
                      assert dx > 0  # dev : do not transferFrom 0 tokens into the pool
                      assert ERC20(coins[coin_idx]).transferFrom(
                          sender, self, dx, default_return_value=True
                      )
              
                      _dx = ERC20(coins[coin_idx]).balanceOf(self) - _dx
              
                  # --------------------------- Store transferred in amount ---------------------------
              
                  self.stored_balances[coin_idx] += _dx
              
                  return _dx
              
              
              @internal
              def _transfer_out(_coin_idx: int128, _amount: uint256, receiver: address):
                  """
                  @notice Transfer a single token from the pool to receiver.
                  @dev This function is called by `remove_liquidity` and
                       `remove_liquidity_one_coin`, `_exchange`, `_withdraw_admin_fees` and
                       `remove_liquidity_imbalance` methods.
                  @param _coin_idx Index of the token to transfer out
                  @param _amount Amount of token to transfer out
                  @param receiver Address to send the tokens to
                  """
                  assert receiver != empty(address)  # dev: do not send tokens to zero_address
              
                  if not pool_contains_rebasing_tokens:
              
                      # we need not cache balanceOf pool before swap out
                      self.stored_balances[_coin_idx] -= _amount
                      assert ERC20(coins[_coin_idx]).transfer(
                          receiver, _amount, default_return_value=True
                      )
              
                  else:
              
                      # cache balances pre and post to account for fee on transfers etc.
                      coin_balance: uint256 = ERC20(coins[_coin_idx]).balanceOf(self)
                      assert ERC20(coins[_coin_idx]).transfer(
                          receiver, _amount, default_return_value=True
                      )
                      self.stored_balances[_coin_idx] = coin_balance - _amount
              
              
              # -------------------------- AMM Special Methods -----------------------------
              
              
              @view
              @internal
              def _stored_rates() -> DynArray[uint256, MAX_COINS]:
                  """
                  @notice Gets rate multipliers for each coin.
                  @dev If the coin has a rate oracle that has been properly initialised,
                       this method queries that rate by static-calling an external
                       contract.
                  """
                  rates: DynArray[uint256, MAX_COINS] = rate_multipliers
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if asset_types[i] == 1 and not rate_oracles[i] == 0:
              
                          # NOTE: fetched_rate is assumed to be 10**18 precision
                          oracle_response: Bytes[32] = raw_call(
                              convert(rate_oracles[i] % 2**160, address),
                              _abi_encode(rate_oracles[i] & ORACLE_BIT_MASK),
                              max_outsize=32,
                              is_static_call=True,
                          )
                          assert len(oracle_response) == 32
                          fetched_rate: uint256 = convert(oracle_response, uint256)
              
                          rates[i] = unsafe_div(rates[i] * fetched_rate, PRECISION)
              
                      elif asset_types[i] == 3:  # ERC4626
              
                          # fetched_rate: uint256 = ERC4626(coins[i]).convertToAssets(call_amount[i]) * scale_factor[i]
                          # here: call_amount has ERC4626 precision, but the returned value is scaled up to 18
                          # using scale_factor which is (18 - n) if underlying asset has n decimals.
                          rates[i] = unsafe_div(
                              rates[i] * ERC4626(coins[i]).convertToAssets(call_amount[i]) * scale_factor[i],
                              PRECISION
                          )  # 1e18 precision
              
                  return rates
              
              
              @view
              @internal
              def _balances() -> DynArray[uint256, MAX_COINS]:
                  """
                  @notice Calculates the pool's balances _excluding_ the admin's balances.
                  @dev If the pool contains rebasing tokens, this method ensures LPs keep all
                          rebases and admin only claims swap fees. This also means that, since
                          admin's balances are stored in an array and not inferred from read balances,
                          the fees in the rebasing token that the admin collects is immune to
                          slashing events.
                  """
                  result: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  balances_i: uint256 = 0
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if pool_contains_rebasing_tokens:
                          # Read balances by gulping to account for rebases
                          balances_i = ERC20(coins[i]).balanceOf(self) - self.admin_balances[i]
                      else:
                          # Use cached balances
                          balances_i = self.stored_balances[i] - self.admin_balances[i]
              
                      result.append(balances_i)
              
                  return result
              
              
              # -------------------------- AMM Main Functions ------------------------------
              
              
              @external
              @nonreentrant('lock')
              def exchange(
                  i: int128,
                  j: int128,
                  _dx: uint256,
                  _min_dy: uint256,
                  _receiver: address = msg.sender,
              ) -> uint256:
                  """
                  @notice Perform an exchange between two coins
                  @dev Index values can be found via the `coins` public getter method
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @param _dx Amount of `i` being exchanged
                  @param _min_dy Minimum amount of `j` to receive
                  @param _receiver Address that receives `j`
                  @return Actual amount of `j` received
                  """
                  return self._exchange(
                      msg.sender,
                      i,
                      j,
                      _dx,
                      _min_dy,
                      _receiver,
                      False
                  )
              
              
              @external
              @nonreentrant('lock')
              def exchange_received(
                  i: int128,
                  j: int128,
                  _dx: uint256,
                  _min_dy: uint256,
                  _receiver: address = msg.sender,
              ) -> uint256:
                  """
                  @notice Perform an exchange between two coins without transferring token in
                  @dev The contract swaps tokens based on a change in balance of coin[i]. The
                       dx = ERC20(coin[i]).balanceOf(self) - self.stored_balances[i]. Users of
                       this method are dex aggregators, arbitrageurs, or other users who do not
                       wish to grant approvals to the contract: they would instead send tokens
                       directly to the contract and call `exchange_received`.
                       Note: This is disabled if pool contains rebasing tokens.
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @param _dx Amount of `i` being exchanged
                  @param _min_dy Minimum amount of `j` to receive
                  @param _receiver Address that receives `j`
                  @return Actual amount of `j` received
                  """
                  assert not pool_contains_rebasing_tokens  # dev: exchange_received not supported if pool contains rebasing tokens
                  return self._exchange(
                      msg.sender,
                      i,
                      j,
                      _dx,
                      _min_dy,
                      _receiver,
                      True,  # <--------------------------------------- swap optimistically.
                  )
              
              
              @external
              @nonreentrant('lock')
              def add_liquidity(
                  _amounts: DynArray[uint256, MAX_COINS],
                  _min_mint_amount: uint256,
                  _receiver: address = msg.sender
              ) -> uint256:
                  """
                  @notice Deposit coins into the pool
                  @param _amounts List of amounts of coins to deposit
                  @param _min_mint_amount Minimum amount of LP tokens to mint from the deposit
                  @param _receiver Address that owns the minted LP tokens
                  @return Amount of LP tokens received by depositing
                  """
                  assert _receiver != empty(address)  # dev: do not send LP tokens to zero_address
              
                  amp: uint256 = self._A()
                  old_balances: DynArray[uint256, MAX_COINS] = self._balances()
                  rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
              
                  # Initial invariant
                  D0: uint256 = self.get_D_mem(rates, old_balances, amp)
              
                  total_supply: uint256 = self.total_supply
                  new_balances: DynArray[uint256, MAX_COINS] = old_balances
              
                  # -------------------------- Do Transfers In -----------------------------
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if _amounts[i] > 0:
              
                          new_balances[i] += self._transfer_in(
                              i,
                              _amounts[i],
                              msg.sender,
                              False,  # expect_optimistic_transfer
                          )
              
                      else:
              
                          assert total_supply != 0  # dev: initial deposit requires all coins
              
                  # ------------------------------------------------------------------------
              
                  # Invariant after change
                  D1: uint256 = self.get_D_mem(rates, new_balances, amp)
                  assert D1 > D0
              
                  # We need to recalculate the invariant accounting for fees
                  # to calculate fair user's share
                  fees: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  mint_amount: uint256 = 0
              
                  if total_supply > 0:
              
                      ideal_balance: uint256 = 0
                      difference: uint256 = 0
                      new_balance: uint256 = 0
              
                      ys: uint256 = unsafe_div(D0 + D1, N_COINS)
                      xs: uint256 = 0
                      _dynamic_fee_i: uint256 = 0
              
                      # Only account for fees if we are not the first to deposit
                      base_fee: uint256 = unsafe_div(
                          unsafe_mul(self.fee, N_COINS),
                          unsafe_mul(4, unsafe_sub(N_COINS, 1))
                      )
              
                      for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                          ideal_balance = D1 * old_balances[i] / D0
                          difference = 0
                          new_balance = new_balances[i]
              
                          if ideal_balance > new_balance:
                              difference = unsafe_sub(ideal_balance, new_balance)
                          else:
                              difference = unsafe_sub(new_balance, ideal_balance)
              
                          # fee[i] = _dynamic_fee(i, j) * difference / FEE_DENOMINATOR
                          xs = unsafe_div(rates[i] * (old_balances[i] + new_balance), PRECISION)
                          _dynamic_fee_i = self._dynamic_fee(xs, ys, base_fee)
                          fees.append(unsafe_div(_dynamic_fee_i * difference, FEE_DENOMINATOR))
                          self.admin_balances[i] += unsafe_div(fees[i] * admin_fee, FEE_DENOMINATOR)
                          new_balances[i] -= fees[i]
              
                      xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, new_balances)
                      D1 = self.get_D(xp, amp)  # <--------------- Reuse D1 for new D value.
                      mint_amount = unsafe_div(total_supply * (D1 - D0), D0)
                      self.upkeep_oracles(xp, amp, D1)
              
                  else:
              
                      mint_amount = D1  # Take the dust if there was any
              
                      # (re)instantiate D oracle if totalSupply is zero.
                      self.last_D_packed = self.pack_2(D1, D1)
              
                      # Update D ma time:
                      ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
                      if ma_last_time_unpacked[1] < block.timestamp:
                          ma_last_time_unpacked[1] = block.timestamp
                          self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])
              
                  assert mint_amount >= _min_mint_amount, "Slippage screwed you"
              
                  # Mint pool tokens
                  total_supply += mint_amount
                  self.balanceOf[_receiver] += mint_amount
                  self.total_supply = total_supply
                  log Transfer(empty(address), _receiver, mint_amount)
              
                  log AddLiquidity(msg.sender, _amounts, fees, D1, total_supply)
              
                  return mint_amount
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity_one_coin(
                  _burn_amount: uint256,
                  i: int128,
                  _min_received: uint256,
                  _receiver: address = msg.sender,
              ) -> uint256:
                  """
                  @notice Withdraw a single coin from the pool
                  @param _burn_amount Amount of LP tokens to burn in the withdrawal
                  @param i Index value of the coin to withdraw
                  @param _min_received Minimum amount of coin to receive
                  @param _receiver Address that receives the withdrawn coins
                  @return Amount of coin received
                  """
                  assert _burn_amount > 0  # dev: do not remove 0 LP tokens
                  dy: uint256 = 0
                  fee: uint256 = 0
                  xp: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  amp: uint256 = empty(uint256)
                  D: uint256 = empty(uint256)
              
                  dy, fee, xp, amp, D = self._calc_withdraw_one_coin(_burn_amount, i)
                  assert dy >= _min_received, "Not enough coins removed"
              
                  self.admin_balances[i] += unsafe_div(fee * admin_fee, FEE_DENOMINATOR)
              
                  self._burnFrom(msg.sender, _burn_amount)
              
                  self._transfer_out(i, dy, _receiver)
              
                  log RemoveLiquidityOne(msg.sender, i, _burn_amount, dy, self.total_supply)
              
                  self.upkeep_oracles(xp, amp, D)
              
                  return dy
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity_imbalance(
                  _amounts: DynArray[uint256, MAX_COINS],
                  _max_burn_amount: uint256,
                  _receiver: address = msg.sender
              ) -> uint256:
                  """
                  @notice Withdraw coins from the pool in an imbalanced amount
                  @param _amounts List of amounts of underlying coins to withdraw
                  @param _max_burn_amount Maximum amount of LP token to burn in the withdrawal
                  @param _receiver Address that receives the withdrawn coins
                  @return Actual amount of the LP token burned in the withdrawal
                  """
                  amp: uint256 = self._A()
                  rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
                  old_balances: DynArray[uint256, MAX_COINS] = self._balances()
                  D0: uint256 = self.get_D_mem(rates, old_balances, amp)
                  new_balances: DynArray[uint256, MAX_COINS] = old_balances
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if _amounts[i] != 0:
                          new_balances[i] -= _amounts[i]
                          self._transfer_out(i, _amounts[i], _receiver)
              
                  D1: uint256 = self.get_D_mem(rates, new_balances, amp)
                  base_fee: uint256 = unsafe_div(
                      unsafe_mul(self.fee, N_COINS),
                      unsafe_mul(4, unsafe_sub(N_COINS, 1))
                  )
                  ys: uint256 = unsafe_div((D0 + D1), N_COINS)
              
                  fees: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  dynamic_fee: uint256 = 0
                  xs: uint256 = 0
                  ideal_balance: uint256 = 0
                  difference: uint256 = 0
                  new_balance: uint256 = 0
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      ideal_balance = D1 * old_balances[i] / D0
                      difference = 0
                      new_balance = new_balances[i]
              
                      if ideal_balance > new_balance:
                          difference = unsafe_sub(ideal_balance, new_balance)
                      else:
                          difference = unsafe_sub(new_balance, ideal_balance)
              
                      xs = unsafe_div(rates[i] * (old_balances[i] + new_balance), PRECISION)
                      dynamic_fee = self._dynamic_fee(xs, ys, base_fee)
                      fees.append(unsafe_div(dynamic_fee * difference, FEE_DENOMINATOR))
              
                      self.admin_balances[i] += unsafe_div(fees[i] * admin_fee, FEE_DENOMINATOR)
                      new_balances[i] -= fees[i]
              
                  D1 = self.get_D_mem(rates, new_balances, amp)  # dev: reuse D1 for new D.
                  self.upkeep_oracles(self._xp_mem(rates, new_balances), amp, D1)
              
                  total_supply: uint256 = self.total_supply
                  burn_amount: uint256 = unsafe_div((D0 - D1) * total_supply, D0) + 1
                  assert burn_amount > 1  # dev: zero tokens burned
                  assert burn_amount <= _max_burn_amount, "Slippage screwed you"
              
                  self._burnFrom(msg.sender, burn_amount)
              
                  log RemoveLiquidityImbalance(
                      msg.sender,
                      _amounts,
                      fees,
                      D1,
                      total_supply - burn_amount
                  )
              
                  return burn_amount
              
              
              @external
              @nonreentrant('lock')
              def remove_liquidity(
                  _burn_amount: uint256,
                  _min_amounts: DynArray[uint256, MAX_COINS],
                  _receiver: address = msg.sender,
                  _claim_admin_fees: bool = True,
              ) -> DynArray[uint256, MAX_COINS]:
                  """
                  @notice Withdraw coins from the pool
                  @dev Withdrawal amounts are based on current deposit ratios
                  @param _burn_amount Quantity of LP tokens to burn in the withdrawal
                  @param _min_amounts Minimum amounts of underlying coins to receive
                  @param _receiver Address that receives the withdrawn coins
                  @return List of amounts of coins that were withdrawn
                  """
                  total_supply: uint256 = self.total_supply
                  assert _burn_amount > 0  # dev: invalid burn amount
                  assert len(_min_amounts) == N_COINS  # dev: invalid array length for _min_amounts
              
                  amounts: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  balances: DynArray[uint256, MAX_COINS] = self._balances()
              
                  value: uint256 = 0
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      value = unsafe_div(balances[i] * _burn_amount, total_supply)
                      assert value >= _min_amounts[i], "Withdrawal resulted in fewer coins than expected"
                      amounts.append(value)
                      self._transfer_out(i, value, _receiver)
              
                  self._burnFrom(msg.sender, _burn_amount)  # <---- Updates self.total_supply
              
                  # --------------------------- Upkeep D_oracle ----------------------------
              
                  ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
                  last_D_packed_current: uint256 = self.last_D_packed
                  old_D: uint256 = last_D_packed_current & (2**128 - 1)
              
                  self.last_D_packed = self.pack_2(
                      old_D - unsafe_div(old_D * _burn_amount, total_supply),  # new_D = proportionally reduce D.
                      self._calc_moving_average(
                          last_D_packed_current,
                          self.D_ma_time,
                          ma_last_time_unpacked[1]
                      )
                  )
              
                  if ma_last_time_unpacked[1] < block.timestamp:
                      ma_last_time_unpacked[1] = block.timestamp
                      self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])
              
                  # ------------------------------- Log event ------------------------------
              
                  log RemoveLiquidity(
                      msg.sender,
                      amounts,
                      empty(DynArray[uint256, MAX_COINS]),
                      unsafe_sub(total_supply, _burn_amount)
                  )
              
                  # ------- Withdraw admin fees if _claim_admin_fees is set to True --------
                  if _claim_admin_fees:
                      self._withdraw_admin_fees()
              
                  return amounts
              
              
              @external
              @nonreentrant('lock')
              def withdraw_admin_fees():
                  """
                  @notice Claim admin fees. Callable by anyone.
                  """
                  self._withdraw_admin_fees()
              
              
              # ------------------------ AMM Internal Functions ----------------------------
              
              
              @view
              @internal
              def _dynamic_fee(xpi: uint256, xpj: uint256, _fee: uint256) -> uint256:
              
                  _offpeg_fee_multiplier: uint256 = self.offpeg_fee_multiplier
                  if _offpeg_fee_multiplier <= FEE_DENOMINATOR:
                      return _fee
              
                  xps2: uint256 = (xpi + xpj) ** 2
                  return unsafe_div(
                      unsafe_mul(_offpeg_fee_multiplier, _fee),
                      unsafe_add(
                          unsafe_sub(_offpeg_fee_multiplier, FEE_DENOMINATOR) * 4 * xpi * xpj / xps2,
                          FEE_DENOMINATOR
                      )
                  )
              
              
              @internal
              def __exchange(
                  x: uint256,
                  _xp: DynArray[uint256, MAX_COINS],
                  rates: DynArray[uint256, MAX_COINS],
                  i: int128,
                  j: int128,
              ) -> uint256:
              
                  amp: uint256 = self._A()
                  D: uint256 = self.get_D(_xp, amp)
                  y: uint256 = self.get_y(i, j, x, _xp, amp, D)
              
                  dy: uint256 = _xp[j] - y - 1  # -1 just in case there were some rounding errors
                  dy_fee: uint256 = unsafe_div(
                      dy * self._dynamic_fee(
                          unsafe_div(_xp[i] + x, 2), unsafe_div(_xp[j] + y, 2), self.fee
                      ),
                      FEE_DENOMINATOR
                  )
              
                  # Convert all to real units
                  dy = (dy - dy_fee) * PRECISION / rates[j]
              
                  self.admin_balances[j] += unsafe_div(
                      unsafe_div(dy_fee * admin_fee, FEE_DENOMINATOR) * PRECISION,
                      rates[j]
                  )
              
                  # Calculate and store state prices:
                  xp: DynArray[uint256, MAX_COINS] = _xp
                  xp[i] = x
                  xp[j] = y
                  # D is not changed because we did not apply a fee
                  self.upkeep_oracles(xp, amp, D)
              
                  return dy
              
              
              @internal
              def _exchange(
                  sender: address,
                  i: int128,
                  j: int128,
                  _dx: uint256,
                  _min_dy: uint256,
                  receiver: address,
                  expect_optimistic_transfer: bool
              ) -> uint256:
              
                  assert i != j  # dev: coin index out of range
                  assert _dx > 0  # dev: do not exchange 0 coins
              
                  rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
                  old_balances: DynArray[uint256, MAX_COINS] = self._balances()
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, old_balances)
              
                  # --------------------------- Do Transfer in -----------------------------
              
                  # `dx` is whatever the pool received after ERC20 transfer:
                  dx: uint256 = self._transfer_in(
                      i,
                      _dx,
                      sender,
                      expect_optimistic_transfer
                  )
              
                  # ------------------------------- Exchange -------------------------------
              
                  x: uint256 = xp[i] + unsafe_div(dx * rates[i], PRECISION)
                  dy: uint256 = self.__exchange(x, xp, rates, i, j)
                  assert dy >= _min_dy, "Exchange resulted in fewer coins than expected"
              
                  # --------------------------- Do Transfer out ----------------------------
              
                  self._transfer_out(j, dy, receiver)
              
                  # ------------------------------------------------------------------------
              
                  log TokenExchange(msg.sender, i, dx, j, dy)
              
                  return dy
              
              
              @internal
              def _withdraw_admin_fees():
                  fee_receiver: address = factory.fee_receiver()
                  if fee_receiver == empty(address):
                      return  # Do nothing.
              
                  admin_balances: DynArray[uint256, MAX_COINS] = self.admin_balances
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
              
                      if admin_balances[i] > 0:
              
                          self._transfer_out(i, admin_balances[i], fee_receiver)
                          admin_balances[i] = 0
              
                  self.admin_balances = admin_balances
              
              
              # --------------------------- AMM Math Functions -----------------------------
              
              
              @view
              @internal
              def get_y(
                  i: int128,
                  j: int128,
                  x: uint256,
                  xp: DynArray[uint256, MAX_COINS],
                  _amp: uint256,
                  _D: uint256
              ) -> uint256:
                  """
                  Calculate x[j] if one makes x[i] = x
              
                  Done by solving quadratic equation iteratively.
                  x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
                  x_1**2 + b*x_1 = c
              
                  x_1 = (x_1**2 + c) / (2*x_1 + b)
                  """
                  # x in the input is converted to the same price/precision
              
                  assert i != j       # dev: same coin
                  assert j >= 0       # dev: j below zero
                  assert j < N_COINS_128  # dev: j above N_COINS
              
                  # should be unreachable, but good for safety
                  assert i >= 0
                  assert i < N_COINS_128
              
                  amp: uint256 = _amp
                  D: uint256 = _D
              
                  S_: uint256 = 0
                  _x: uint256 = 0
                  y_prev: uint256 = 0
                  c: uint256 = D
                  Ann: uint256 = amp * N_COINS
              
                  for _i in range(MAX_COINS_128):
              
                      if _i == N_COINS_128:
                          break
              
                      if _i == i:
                          _x = x
                      elif _i != j:
                          _x = xp[_i]
                      else:
                          continue
              
                      S_ += _x
                      c = c * D / (_x * N_COINS)
              
                  c = c * D * A_PRECISION / (Ann * N_COINS)
                  b: uint256 = S_ + D * A_PRECISION / Ann  # - D
                  y: uint256 = D
              
                  for _i in range(255):
                      y_prev = y
                      y = (y*y + c) / (2 * y + b - D)
                      # Equality with the precision of 1
                      if y > y_prev:
                          if y - y_prev <= 1:
                              return y
                      else:
                          if y_prev - y <= 1:
                              return y
                  raise
              
              
              @pure
              @internal
              def get_D(_xp: DynArray[uint256, MAX_COINS], _amp: uint256) -> uint256:
                  """
                  D invariant calculation in non-overflowing integer operations
                  iteratively
              
                  A * sum(x_i) * n**n + D = A * D * n**n + D**(n+1) / (n**n * prod(x_i))
              
                  Converging solution:
                  D[j+1] = (A * n**n * sum(x_i) - D[j]**(n+1) / (n**n prod(x_i))) / (A * n**n - 1)
                  """
                  S: uint256 = 0
                  for x in _xp:
                      S += x
                  if S == 0:
                      return 0
              
                  D: uint256 = S
                  Ann: uint256 = _amp * N_COINS
              
                  for i in range(255):
              
                      D_P: uint256 = D
                      for x in _xp:
                          D_P = D_P * D / x
                      D_P /= pow_mod256(N_COINS, N_COINS)
                      Dprev: uint256 = D
              
                      # (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P)
                      D = (
                          (unsafe_div(Ann * S, A_PRECISION) + D_P * N_COINS) * D
                          /
                          (
                              unsafe_div((Ann - A_PRECISION) * D, A_PRECISION) +
                              unsafe_add(N_COINS, 1) * D_P
                          )
                      )
              
                      # Equality with the precision of 1
                      if D > Dprev:
                          if D - Dprev <= 1:
                              return D
                      else:
                          if Dprev - D <= 1:
                              return D
                  # convergence typically occurs in 4 rounds or less, this should be unreachable!
                  # if it does happen the pool is borked and LPs can withdraw via `remove_liquidity`
                  raise
              
              
              @pure
              @internal
              def get_y_D(
                  A: uint256,
                  i: int128,
                  xp: DynArray[uint256, MAX_COINS],
                  D: uint256
              ) -> uint256:
                  """
                  Calculate x[i] if one reduces D from being calculated for xp to D
              
                  Done by solving quadratic equation iteratively.
                  x_1**2 + x_1 * (sum' - (A*n**n - 1) * D / (A * n**n)) = D ** (n + 1) / (n ** (2 * n) * prod' * A)
                  x_1**2 + b*x_1 = c
              
                  x_1 = (x_1**2 + c) / (2*x_1 + b)
                  """
                  # x in the input is converted to the same price/precision
              
                  assert i >= 0  # dev: i below zero
                  assert i < N_COINS_128  # dev: i above N_COINS
              
                  S_: uint256 = 0
                  _x: uint256 = 0
                  y_prev: uint256 = 0
                  c: uint256 = D
                  Ann: uint256 = A * N_COINS
              
                  for _i in range(MAX_COINS_128):
              
                      if _i == N_COINS_128:
                          break
              
                      if _i != i:
                          _x = xp[_i]
                      else:
                          continue
                      S_ += _x
                      c = c * D / (_x * N_COINS)
              
                  c = c * D * A_PRECISION / (Ann * N_COINS)
                  b: uint256 = S_ + D * A_PRECISION / Ann
                  y: uint256 = D
              
                  for _i in range(255):
                      y_prev = y
                      y = (y*y + c) / (2 * y + b - D)
                      # Equality with the precision of 1
                      if y > y_prev:
                          if y - y_prev <= 1:
                              return y
                      else:
                          if y_prev - y <= 1:
                              return y
                  raise
              
              
              @view
              @internal
              def _A() -> uint256:
                  """
                  Handle ramping A up or down
                  """
                  t1: uint256 = self.future_A_time
                  A1: uint256 = self.future_A
              
                  if block.timestamp < t1:
                      A0: uint256 = self.initial_A
                      t0: uint256 = self.initial_A_time
                      # Expressions in uint256 cannot have negative numbers, thus "if"
                      if A1 > A0:
                          return A0 + unsafe_sub(A1, A0) * (block.timestamp - t0) / (t1 - t0)
                      else:
                          return A0 - unsafe_sub(A0, A1) * (block.timestamp - t0) / (t1 - t0)
              
                  else:  # when t1 == 0 or block.timestamp >= t1
                      return A1
              
              
              @pure
              @internal
              def _xp_mem(
                  _rates: DynArray[uint256, MAX_COINS],
                  _balances: DynArray[uint256, MAX_COINS]
              ) -> DynArray[uint256, MAX_COINS]:
              
                  result: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
                      result.append(unsafe_div(_rates[i] * _balances[i], PRECISION))
                  return result
              
              
              @view
              @internal
              def get_D_mem(
                  _rates: DynArray[uint256, MAX_COINS],
                  _balances: DynArray[uint256, MAX_COINS],
                  _amp: uint256
              ) -> uint256:
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(_rates, _balances)
                  return self.get_D(xp, _amp)
              
              
              @view
              @internal
              def _calc_withdraw_one_coin(
                  _burn_amount: uint256,
                  i: int128
              ) -> (
                  uint256,
                  uint256,
                  DynArray[uint256, MAX_COINS],
                  uint256,
                  uint256
              ):
                  # First, need to calculate
                  # * Get current D
                  # * Solve Eqn against y_i for D - _token_amount
                  amp: uint256 = self._A()
                  rates: DynArray[uint256, MAX_COINS] = self._stored_rates()
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(rates, self._balances())
                  D0: uint256 = self.get_D(xp, amp)
              
                  total_supply: uint256 = self.total_supply
                  D1: uint256 = D0 - _burn_amount * D0 / total_supply
                  new_y: uint256 = self.get_y_D(amp, i, xp, D1)
              
                  base_fee: uint256 = unsafe_div(
                      unsafe_mul(self.fee, N_COINS),
                      unsafe_mul(4, unsafe_sub(N_COINS, 1))
                  )
                  xp_reduced: DynArray[uint256, MAX_COINS] = xp
                  ys: uint256 = unsafe_div((D0 + D1), unsafe_mul(2, N_COINS))
              
                  dx_expected: uint256 = 0
                  xp_j: uint256 = 0
                  xavg: uint256 = 0
                  dynamic_fee: uint256 = 0
              
                  for j in range(MAX_COINS_128):
              
                      if j == N_COINS_128:
                          break
              
                      dx_expected = 0
                      xp_j = xp[j]
              
                      if j == i:
                          dx_expected = xp_j * D1 / D0 - new_y
                          xavg = unsafe_div((xp_j + new_y), 2)
                      else:
                          dx_expected = xp_j - xp_j * D1 / D0
                          xavg = xp_j
              
                      dynamic_fee = self._dynamic_fee(xavg, ys, base_fee)
                      xp_reduced[j] = xp_j - unsafe_div(dynamic_fee * dx_expected, FEE_DENOMINATOR)
              
                  dy: uint256 = xp_reduced[i] - self.get_y_D(amp, i, xp_reduced, D1)
                  dy_0: uint256 = (xp[i] - new_y) * PRECISION / rates[i]  # w/o fees
                  dy = unsafe_div((dy - 1) * PRECISION, rates[i])  # Withdraw less to account for rounding errors
              
                  # update xp with new_y for p calculations.
                  xp[i] = new_y
              
                  return dy, dy_0 - dy, xp, amp, D1
              
              
              # -------------------------- AMM Price Methods -------------------------------
              
              @pure
              @internal
              def pack_2(p1: uint256, p2: uint256) -> uint256:
                  assert p1 < 2**128
                  assert p2 < 2**128
                  return p1 | (p2 << 128)
              
              
              @pure
              @internal
              def unpack_2(packed: uint256) -> uint256[2]:
                  return [packed & (2**128 - 1), packed >> 128]
              
              
              @internal
              @pure
              def _get_p(
                  xp: DynArray[uint256, MAX_COINS],
                  amp: uint256,
                  D: uint256,
              ) -> DynArray[uint256, MAX_COINS]:
              
                  # dx_0 / dx_1 only, however can have any number of coins in pool
                  ANN: uint256 = unsafe_mul(amp, N_COINS)
                  Dr: uint256 = unsafe_div(D, pow_mod256(N_COINS, N_COINS))
              
                  for i in range(N_COINS_128, bound=MAX_COINS_128):
                      Dr = Dr * D / xp[i]
              
                  p: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
                  xp0_A: uint256 = unsafe_div(ANN * xp[0], A_PRECISION)
              
                  for i in range(1, MAX_COINS):
              
                      if i == N_COINS:
                          break
              
                      p.append(10**18 * (xp0_A + unsafe_div(Dr * xp[0], xp[i])) / (xp0_A + Dr))
              
                  return p
              
              
              @internal
              def upkeep_oracles(xp: DynArray[uint256, MAX_COINS], amp: uint256, D: uint256):
                  """
                  @notice Upkeeps price and D oracles.
                  """
                  ma_last_time_unpacked: uint256[2] = self.unpack_2(self.ma_last_time)
                  last_prices_packed_current: DynArray[uint256, MAX_COINS] = self.last_prices_packed
                  last_prices_packed_new: DynArray[uint256, MAX_COINS] = last_prices_packed_current
              
                  spot_price: DynArray[uint256, MAX_COINS] = self._get_p(xp, amp, D)
              
                  # -------------------------- Upkeep price oracle -------------------------
              
                  for i in range(MAX_COINS):
              
                      if i == N_COINS - 1:
                          break
              
                      if spot_price[i] != 0:
              
                          # Update packed prices -----------------
                          last_prices_packed_new[i] = self.pack_2(
                              min(spot_price[i], 2 * 10**18),  # <----- Cap spot value by 2.
                              self._calc_moving_average(
                                  last_prices_packed_current[i],
                                  self.ma_exp_time,
                                  ma_last_time_unpacked[0],  # index 0 is ma_last_time for prices
                              )
                          )
              
                  self.last_prices_packed = last_prices_packed_new
              
                  # ---------------------------- Upkeep D oracle ---------------------------
              
                  last_D_packed_current: uint256 = self.last_D_packed
                  self.last_D_packed = self.pack_2(
                      D,
                      self._calc_moving_average(
                          last_D_packed_current,
                          self.D_ma_time,
                          ma_last_time_unpacked[1],  # index 1 is ma_last_time for D
                      )
                  )
              
                  # Housekeeping: Update ma_last_time for p and D oracles ------------------
                  for i in range(2):
                      if ma_last_time_unpacked[i] < block.timestamp:
                          ma_last_time_unpacked[i] = block.timestamp
              
                  self.ma_last_time = self.pack_2(ma_last_time_unpacked[0], ma_last_time_unpacked[1])
              
              
              @internal
              @view
              def _calc_moving_average(
                  packed_value: uint256,
                  averaging_window: uint256,
                  ma_last_time: uint256
              ) -> uint256:
              
                  last_spot_value: uint256 = packed_value & (2**128 - 1)
                  last_ema_value: uint256 = (packed_value >> 128)
              
                  if ma_last_time < block.timestamp:  # calculate new_ema_value and return that.
                      alpha: uint256 = self.exp(
                          -convert(
                              unsafe_div(unsafe_mul(unsafe_sub(block.timestamp, ma_last_time), 10**18), averaging_window), int256
                          )
                      )
                      return unsafe_div(last_spot_value * (10**18 - alpha) + last_ema_value * alpha, 10**18)
              
                  return last_ema_value
              
              
              @view
              @external
              def last_price(i: uint256) -> uint256:
                  return self.last_prices_packed[i] & (2**128 - 1)
              
              
              @view
              @external
              def ema_price(i: uint256) -> uint256:
                  return (self.last_prices_packed[i] >> 128)
              
              
              @external
              @view
              def get_p(i: uint256) -> uint256:
                  """
                  @notice Returns the AMM State price of token
                  @dev if i = 0, it will return the state price of coin[1].
                  @param i index of state price (0 for coin[1], 1 for coin[2], ...)
                  @return uint256 The state price quoted by the AMM for coin[i+1]
                  """
                  amp: uint256 = self._A()
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(
                      self._stored_rates(), self._balances()
                  )
                  D: uint256 = self.get_D(xp, amp)
                  return self._get_p(xp, amp, D)[i]
              
              
              @external
              @view
              @nonreentrant('lock')
              def price_oracle(i: uint256) -> uint256:
                  return self._calc_moving_average(
                      self.last_prices_packed[i],
                      self.ma_exp_time,
                      self.ma_last_time & (2**128 - 1)
                  )
              
              
              @external
              @view
              @nonreentrant('lock')
              def D_oracle() -> uint256:
                  return self._calc_moving_average(
                      self.last_D_packed,
                      self.D_ma_time,
                      self.ma_last_time >> 128
                  )
              
              
              # ----------------------------- Math Utils -----------------------------------
              
              
              @internal
              @pure
              def exp(x: int256) -> uint256:
                  """
                  @dev Calculates the natural exponential function of a signed integer with
                       a precision of 1e18.
                  @notice Note that this function consumes about 810 gas units. The implementation
                          is inspired by Remco Bloemen's implementation under the MIT license here:
                          https://xn--2-umb.com/22/exp-ln.
                  @dev This implementation is derived from Snekmate, which is authored
                       by pcaversaccio (Snekmate), distributed under the AGPL-3.0 license.
                       https://github.com/pcaversaccio/snekmate
                  @param x The 32-byte variable.
                  @return int256 The 32-byte calculation result.
                  """
                  value: int256 = x
              
                  # If the result is `< 0.5`, we return zero. This happens when we have the following:
                  # "x <= floor(log(0.5e18) * 1e18) ~ -42e18".
                  if (x <= -41446531673892822313):
                      return empty(uint256)
              
                  # When the result is "> (2 ** 255 - 1) / 1e18" we cannot represent it as a signed integer.
                  # This happens when "x >= floor(log((2 ** 255 - 1) / 1e18) * 1e18) ~ 135".
                  assert x < 135305999368893231589, "wad_exp overflow"
              
                  # `x` is now in the range "(-42, 136) * 1e18". Convert to "(-42, 136) * 2 ** 96" for higher
                  # intermediate precision and a binary base. This base conversion is a multiplication with
                  # "1e18 / 2 ** 96 = 5 ** 18 / 2 ** 78".
                  value = unsafe_div(x << 78, 5 ** 18)
              
                  # Reduce the range of `x` to "(-½ ln 2, ½ ln 2) * 2 ** 96" by factoring out powers of two
                  # so that "exp(x) = exp(x') * 2 ** k", where `k` is a signer integer. Solving this gives
                  # "k = round(x / log(2))" and "x' = x - k * log(2)". Thus, `k` is in the range "[-61, 195]".
                  k: int256 = unsafe_add(unsafe_div(value << 96, 54916777467707473351141471128), 2 ** 95) >> 96
                  value = unsafe_sub(value, unsafe_mul(k, 54916777467707473351141471128))
              
                  # Evaluate using a "(6, 7)"-term rational approximation. Since `p` is monic,
                  # we will multiply by a scaling factor later.
                  y: int256 = unsafe_add(unsafe_mul(unsafe_add(value, 1346386616545796478920950773328), value) >> 96, 57155421227552351082224309758442)
                  p: int256 = unsafe_add(unsafe_mul(unsafe_add(unsafe_mul(unsafe_sub(unsafe_add(y, value), 94201549194550492254356042504812), y) >> 96,\
                                         28719021644029726153956944680412240), value), 4385272521454847904659076985693276 << 96)
              
                  # We leave `p` in the "2 ** 192" base so that we do not have to scale it up
                  # again for the division.
                  q: int256 = unsafe_add(unsafe_mul(unsafe_sub(value, 2855989394907223263936484059900), value) >> 96, 50020603652535783019961831881945)
                  q = unsafe_sub(unsafe_mul(q, value) >> 96, 533845033583426703283633433725380)
                  q = unsafe_add(unsafe_mul(q, value) >> 96, 3604857256930695427073651918091429)
                  q = unsafe_sub(unsafe_mul(q, value) >> 96, 14423608567350463180887372962807573)
                  q = unsafe_add(unsafe_mul(q, value) >> 96, 26449188498355588339934803723976023)
              
                  # The polynomial `q` has no zeros in the range because all its roots are complex.
                  # No scaling is required, as `p` is already "2 ** 96" too large. Also,
                  # `r` is in the range "(0.09, 0.25) * 2**96" after the division.
                  r: int256 = unsafe_div(p, q)
              
                  # To finalise the calculation, we have to multiply `r` by:
                  #   - the scale factor "s = ~6.031367120",
                  #   - the factor "2 ** k" from the range reduction, and
                  #   - the factor "1e18 / 2 ** 96" for the base conversion.
                  # We do this all at once, with an intermediate result in "2**213" base,
                  # so that the final right shift always gives a positive value.
              
                  # Note that to circumvent Vyper's safecast feature for the potentially
                  # negative parameter value `r`, we first convert `r` to `bytes32` and
                  # subsequently to `uint256`. Remember that the EVM default behaviour is
                  # to use two's complement representation to handle signed integers.
                  return unsafe_mul(convert(convert(r, bytes32), uint256), 3822833074963236453042738258902158003155416615667) >> convert(unsafe_sub(195, k), uint256)
              
              
              # ---------------------------- ERC20 Utils -----------------------------------
              
              @view
              @internal
              def _domain_separator() -> bytes32:
                  if chain.id != CACHED_CHAIN_ID:
                      return keccak256(
                          _abi_encode(
                              EIP712_TYPEHASH,
                              NAME_HASH,
                              VERSION_HASH,
                              chain.id,
                              self,
                              salt,
                          )
                      )
                  return CACHED_DOMAIN_SEPARATOR
              
              
              @internal
              def _transfer(_from: address, _to: address, _value: uint256):
                  # # NOTE: vyper does not allow underflows
                  # #       so the following subtraction would revert on insufficient balance
                  self.balanceOf[_from] -= _value
                  self.balanceOf[_to] += _value
              
                  log Transfer(_from, _to, _value)
              
              
              @internal
              def _burnFrom(_from: address, _burn_amount: uint256):
              
                  self.total_supply -= _burn_amount
                  self.balanceOf[_from] -= _burn_amount
                  log Transfer(_from, empty(address), _burn_amount)
              
              
              @external
              def transfer(_to : address, _value : uint256) -> bool:
                  """
                  @dev Transfer token for a specified address
                  @param _to The address to transfer to.
                  @param _value The amount to be transferred.
                  """
                  self._transfer(msg.sender, _to, _value)
                  return True
              
              
              @external
              def transferFrom(_from : address, _to : address, _value : uint256) -> bool:
                  """
                   @dev Transfer tokens from one address to another.
                   @param _from address The address which you want to send tokens from
                   @param _to address The address which you want to transfer to
                   @param _value uint256 the amount of tokens to be transferred
                  """
                  self._transfer(_from, _to, _value)
              
                  _allowance: uint256 = self.allowance[_from][msg.sender]
                  if _allowance != max_value(uint256):
                      _new_allowance: uint256 = _allowance - _value
                      self.allowance[_from][msg.sender] = _new_allowance
                      log Approval(_from, msg.sender, _new_allowance)
              
                  return True
              
              
              @external
              def approve(_spender : address, _value : uint256) -> bool:
                  """
                  @notice Approve the passed address to transfer the specified amount of
                          tokens on behalf of msg.sender
                  @dev Beware that changing an allowance via this method brings the risk that
                       someone may use both the old and new allowance by unfortunate transaction
                       ordering: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                  @param _spender The address which will transfer the funds
                  @param _value The amount of tokens that may be transferred
                  @return bool success
                  """
                  self.allowance[msg.sender][_spender] = _value
              
                  log Approval(msg.sender, _spender, _value)
                  return True
              
              
              @external
              def permit(
                  _owner: address,
                  _spender: address,
                  _value: uint256,
                  _deadline: uint256,
                  _v: uint8,
                  _r: bytes32,
                  _s: bytes32
              ) -> bool:
                  """
                  @notice Approves spender by owner's signature to expend owner's tokens.
                      See https://eips.ethereum.org/EIPS/eip-2612.
                  @dev Inspired by https://github.com/yearn/yearn-vaults/blob/main/contracts/Vault.vy#L753-L793
                  @dev Supports smart contract wallets which implement ERC1271
                      https://eips.ethereum.org/EIPS/eip-1271
                  @param _owner The address which is a source of funds and has signed the Permit.
                  @param _spender The address which is allowed to spend the funds.
                  @param _value The amount of tokens to be spent.
                  @param _deadline The timestamp after which the Permit is no longer valid.
                  @param _v The bytes[64] of the valid secp256k1 signature of permit by owner
                  @param _r The bytes[0:32] of the valid secp256k1 signature of permit by owner
                  @param _s The bytes[32:64] of the valid secp256k1 signature of permit by owner
                  @return True, if transaction completes successfully
                  """
                  assert _owner != empty(address)
                  assert block.timestamp <= _deadline
              
                  nonce: uint256 = self.nonces[_owner]
                  digest: bytes32 = keccak256(
                      concat(
                          b"\x19\x01",
                          self._domain_separator(),
                          keccak256(_abi_encode(EIP2612_TYPEHASH, _owner, _spender, _value, nonce, _deadline))
                      )
                  )
              
                  if _owner.is_contract:
                      sig: Bytes[65] = concat(_abi_encode(_r, _s), slice(convert(_v, bytes32), 31, 1))
                      # reentrancy not a concern since this is a staticcall
                      assert ERC1271(_owner).isValidSignature(digest, sig) == ERC1271_MAGIC_VAL
                  else:
                      assert ecrecover(digest, convert(_v, uint256), convert(_r, uint256), convert(_s, uint256)) == _owner
              
                  self.allowance[_owner][_spender] = _value
                  self.nonces[_owner] = unsafe_add(nonce, 1)
              
                  log Approval(_owner, _spender, _value)
                  return True
              
              
              @view
              @external
              def DOMAIN_SEPARATOR() -> bytes32:
                  """
                  @notice EIP712 domain separator.
                  @return bytes32 Domain Separator set for the current chain.
                  """
                  return self._domain_separator()
              
              
              # ------------------------- AMM View Functions -------------------------------
              
              
              @view
              @external
              def get_dx(i: int128, j: int128, dy: uint256) -> uint256:
                  """
                  @notice Calculate the current input dx given output dy
                  @dev Index values can be found via the `coins` public getter method
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @param dy Amount of `j` being received after exchange
                  @return Amount of `i` predicted
                  """
                  return StableSwapViews(factory.views_implementation()).get_dx(i, j, dy, self)
              
              
              @view
              @external
              def get_dy(i: int128, j: int128, dx: uint256) -> uint256:
                  """
                  @notice Calculate the current output dy given input dx
                  @dev Index values can be found via the `coins` public getter method
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @param dx Amount of `i` being exchanged
                  @return Amount of `j` predicted
                  """
                  return StableSwapViews(factory.views_implementation()).get_dy(i, j, dx, self)
              
              
              @view
              @external
              def calc_withdraw_one_coin(_burn_amount: uint256, i: int128) -> uint256:
                  """
                  @notice Calculate the amount received when withdrawing a single coin
                  @param _burn_amount Amount of LP tokens to burn in the withdrawal
                  @param i Index value of the coin to withdraw
                  @return Amount of coin received
                  """
                  return self._calc_withdraw_one_coin(_burn_amount, i)[0]
              
              
              @view
              @external
              @nonreentrant('lock')
              def totalSupply() -> uint256:
                  """
                  @notice The total supply of pool LP tokens
                  @return self.total_supply, 18 decimals.
                  """
                  return self.total_supply
              
              
              @view
              @external
              @nonreentrant('lock')
              def get_virtual_price() -> uint256:
                  """
                  @notice The current virtual price of the pool LP token
                  @dev Useful for calculating profits.
                       The method may be vulnerable to donation-style attacks if implementation
                       contains rebasing tokens. For integrators, caution is advised.
                  @return LP token virtual price normalized to 1e18
                  """
                  amp: uint256 = self._A()
                  xp: DynArray[uint256, MAX_COINS] = self._xp_mem(
                      self._stored_rates(), self._balances()
                  )
                  D: uint256 = self.get_D(xp, amp)
                  # D is in the units similar to DAI (e.g. converted to precision 1e18)
                  # When balanced, D = n * x_u - total virtual value of the portfolio
                  return D * PRECISION / self.total_supply
              
              
              @view
              @external
              def calc_token_amount(
                  _amounts: DynArray[uint256, MAX_COINS],
                  _is_deposit: bool
              ) -> uint256:
                  """
                  @notice Calculate addition or reduction in token supply from a deposit or withdrawal
                  @param _amounts Amount of each coin being deposited
                  @param _is_deposit set True for deposits, False for withdrawals
                  @return Expected amount of LP tokens received
                  """
                  return StableSwapViews(factory.views_implementation()).calc_token_amount(_amounts, _is_deposit, self)
              
              
              @view
              @external
              def A() -> uint256:
                  return unsafe_div(self._A(), A_PRECISION)
              
              
              @view
              @external
              def A_precise() -> uint256:
                  return self._A()
              
              
              @view
              @external
              def balances(i: uint256) -> uint256:
                  """
                  @notice Get the current balance of a coin within the
                          pool, less the accrued admin fees
                  @param i Index value for the coin to query balance of
                  @return Token balance
                  """
                  return self._balances()[i]
              
              
              @view
              @external
              def get_balances() -> DynArray[uint256, MAX_COINS]:
                  return self._balances()
              
              
              @view
              @external
              def stored_rates() -> DynArray[uint256, MAX_COINS]:
                  return self._stored_rates()
              
              
              @view
              @external
              def dynamic_fee(i: int128, j: int128) -> uint256:
                  """
                  @notice Return the fee for swapping between `i` and `j`
                  @param i Index value for the coin to send
                  @param j Index value of the coin to receive
                  @return Swap fee expressed as an integer with 1e10 precision
                  """
                  return StableSwapViews(factory.views_implementation()).dynamic_fee(i, j, self)
              
              
              # --------------------------- AMM Admin Functions ----------------------------
              
              
              @external
              def ramp_A(_future_A: uint256, _future_time: uint256):
                  assert msg.sender == factory.admin()  # dev: only owner
                  assert block.timestamp >= self.initial_A_time + MIN_RAMP_TIME
                  assert _future_time >= block.timestamp + MIN_RAMP_TIME  # dev: insufficient time
              
                  _initial_A: uint256 = self._A()
                  _future_A_p: uint256 = _future_A * A_PRECISION
              
                  assert _future_A > 0 and _future_A < MAX_A
                  if _future_A_p < _initial_A:
                      assert _future_A_p * MAX_A_CHANGE >= _initial_A
                  else:
                      assert _future_A_p <= _initial_A * MAX_A_CHANGE
              
                  self.initial_A = _initial_A
                  self.future_A = _future_A_p
                  self.initial_A_time = block.timestamp
                  self.future_A_time = _future_time
              
                  log RampA(_initial_A, _future_A_p, block.timestamp, _future_time)
              
              
              @external
              def stop_ramp_A():
                  assert msg.sender == factory.admin()  # dev: only owner
              
                  current_A: uint256 = self._A()
                  self.initial_A = current_A
                  self.future_A = current_A
                  self.initial_A_time = block.timestamp
                  self.future_A_time = block.timestamp
                  # now (block.timestamp < t1) is always False, so we return saved A
              
                  log StopRampA(current_A, block.timestamp)
              
              
              @external
              def set_new_fee(_new_fee: uint256, _new_offpeg_fee_multiplier: uint256):
              
                  assert msg.sender == factory.admin()
              
                  # set new fee:
                  assert _new_fee <= MAX_FEE
                  self.fee = _new_fee
              
                  # set new offpeg_fee_multiplier:
                  assert _new_offpeg_fee_multiplier * _new_fee <= MAX_FEE * FEE_DENOMINATOR  # dev: offpeg multiplier exceeds maximum
                  self.offpeg_fee_multiplier = _new_offpeg_fee_multiplier
              
                  log ApplyNewFee(_new_fee, _new_offpeg_fee_multiplier)
              
              
              @external
              def set_ma_exp_time(_ma_exp_time: uint256, _D_ma_time: uint256):
                  """
                  @notice Set the moving average window of the price oracles.
                  @param _ma_exp_time Moving average window for the price oracle. It is time_in_seconds / ln(2).
                  @param _D_ma_time Moving average window for the D oracle. It is time_in_seconds / ln(2).
                  """
                  assert msg.sender == factory.admin()  # dev: only owner
                  assert unsafe_mul(_ma_exp_time, _D_ma_time) > 0  # dev: 0 in input values
              
                  self.ma_exp_time = _ma_exp_time
                  self.D_ma_time = _D_ma_time
              
                  log SetNewMATime(_ma_exp_time, _D_ma_time)

              File 13 of 16: MetaAggregationRouterV2
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.9;
              import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
              import '@openzeppelin/contracts/utils/Context.sol';
              import '@openzeppelin/contracts/access/Ownable.sol';
              import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
              import './dependency/Permitable.sol';
              import './interfaces/IAggregationExecutor.sol';
              import './interfaces/IAggregationExecutor1Inch.sol';
              import './libraries/TransferHelper.sol';
              import './libraries/RevertReasonParser.sol';
              contract MetaAggregationRouterV2 is Permitable, Ownable {
                using SafeERC20 for IERC20;
                address public immutable WETH;
                address private constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
                uint256 private constant _PARTIAL_FILL = 0x01;
                uint256 private constant _REQUIRES_EXTRA_ETH = 0x02;
                uint256 private constant _SHOULD_CLAIM = 0x04;
                uint256 private constant _BURN_FROM_MSG_SENDER = 0x08;
                uint256 private constant _BURN_FROM_TX_ORIGIN = 0x10;
                uint256 private constant _SIMPLE_SWAP = 0x20;
                uint256 private constant _FEE_ON_DST = 0x40;
                uint256 private constant _FEE_IN_BPS = 0x80;
                uint256 private constant _APPROVE_FUND = 0x100;
                uint256 private constant BPS = 10000;
                mapping(address => bool) public isWhitelist;
                struct SwapDescriptionV2 {
                  IERC20 srcToken;
                  IERC20 dstToken;
                  address[] srcReceivers; // transfer src token to these addresses, default
                  uint256[] srcAmounts;
                  address[] feeReceivers;
                  uint256[] feeAmounts;
                  address dstReceiver;
                  uint256 amount;
                  uint256 minReturnAmount;
                  uint256 flags;
                  bytes permit;
                }
                /// @dev  use for swapGeneric and swap to avoid stack too deep
                struct SwapExecutionParams {
                  address callTarget; // call this address
                  address approveTarget; // approve this address if _APPROVE_FUND set
                  bytes targetData;
                  SwapDescriptionV2 desc;
                  bytes clientData;
                }
                struct SimpleSwapData {
                  address[] firstPools;
                  uint256[] firstSwapAmounts;
                  bytes[] swapDatas;
                  uint256 deadline;
                  bytes destTokenFeeData;
                }
                event Swapped(
                  address sender,
                  IERC20 srcToken,
                  IERC20 dstToken,
                  address dstReceiver,
                  uint256 spentAmount,
                  uint256 returnAmount
                );
                event ClientData(bytes clientData);
                event Exchange(address pair, uint256 amountOut, address output);
                event Fee(address token, uint256 totalAmount, uint256 totalFee, address[] recipients, uint256[] amounts, bool isBps);
                constructor(address _WETH) {
                  WETH = _WETH;
                }
                receive() external payable {}
                function rescueFunds(address token, uint256 amount) external onlyOwner {
                  if (_isETH(IERC20(token))) {
                    TransferHelper.safeTransferETH(msg.sender, amount);
                  } else {
                    TransferHelper.safeTransfer(token, msg.sender, amount);
                  }
                }
                function updateWhitelist(address[] memory addr, bool[] memory value) external onlyOwner {
                  require(addr.length == value.length);
                  for (uint256 i; i < addr.length; ++i) {
                    isWhitelist[addr[i]] = value[i];
                  }
                }
                function swapGeneric(SwapExecutionParams calldata execution)
                  external
                  payable
                  returns (uint256 returnAmount, uint256 gasUsed)
                {
                  uint256 gasBefore = gasleft();
                  require(isWhitelist[execution.callTarget], 'Address not whitelisted');
                  if (execution.approveTarget != execution.callTarget && execution.approveTarget != address(0)) {
                    require(isWhitelist[execution.approveTarget], 'Address not whitelisted');
                  }
                  SwapDescriptionV2 memory desc = execution.desc;
                  require(desc.minReturnAmount > 0, 'Invalid min return amount');
                  // if extra eth is needed, in case srcToken is ETH
                  _collectExtraETHIfNeeded(desc);
                  _permit(desc.srcToken, desc.amount, desc.permit);
                  bool feeInBps = _flagsChecked(desc.flags, _FEE_IN_BPS);
                  uint256 spentAmount;
                  address dstReceiver = desc.dstReceiver == address(0) ? msg.sender : desc.dstReceiver;
                  if (!_flagsChecked(desc.flags, _FEE_ON_DST)) {
                    // fee on src token
                    // take fee on srcToken
                    // take fee and deduct total amount
                    desc.amount = _takeFee(desc.srcToken, msg.sender, desc.feeReceivers, desc.feeAmounts, desc.amount, feeInBps);
                    bool collected;
                    if (!_isETH(desc.srcToken) && _flagsChecked(desc.flags, _SHOULD_CLAIM)) {
                      (collected, desc.amount) = _collectTokenIfNeeded(desc, msg.sender, address(this));
                    }
                    _transferFromOrApproveTarget(msg.sender, execution.approveTarget, desc, collected);
                    // execute swap
                    (spentAmount, returnAmount) = _executeSwap(
                      execution.callTarget,
                      execution.targetData,
                      desc,
                      _isETH(desc.srcToken) ? desc.amount : 0,
                      dstReceiver
                    );
                  } else {
                    bool collected;
                    if (!_isETH(desc.srcToken) && _flagsChecked(desc.flags, _SHOULD_CLAIM)) {
                      (collected, desc.amount) = _collectTokenIfNeeded(desc, msg.sender, address(this));
                    }
                    uint256 initialDstReceiverBalance = _getBalance(desc.dstToken, dstReceiver);
                    _transferFromOrApproveTarget(msg.sender, execution.approveTarget, desc, collected);
                    // fee on dst token
                    // router get dst token first
                    (spentAmount, returnAmount) = _executeSwap(
                      execution.callTarget,
                      execution.targetData,
                      desc,
                      _isETH(desc.srcToken) ? msg.value : 0,
                      address(this)
                    );
                    {
                      // then take fee on dst token
                      uint256 leftAmount = _takeFee(
                        desc.dstToken,
                        address(this),
                        desc.feeReceivers,
                        desc.feeAmounts,
                        returnAmount,
                        feeInBps
                      );
                      _doTransferERC20(desc.dstToken, address(this), dstReceiver, leftAmount);
                    }
                    returnAmount = _getBalance(desc.dstToken, dstReceiver) - initialDstReceiverBalance;
                  }
                  // check return amount
                  _checkReturnAmount(spentAmount, returnAmount, desc);
                  //revoke allowance
                  if (!_isETH(desc.srcToken) && execution.approveTarget != address(0)) {
                    desc.srcToken.safeApprove(execution.approveTarget, 0);
                  }
                  emit Swapped(msg.sender, desc.srcToken, desc.dstToken, dstReceiver, spentAmount, returnAmount);
                  emit Exchange(execution.callTarget, returnAmount, _isETH(desc.dstToken) ? WETH : address(desc.dstToken));
                  emit ClientData(execution.clientData);
                  unchecked {
                    gasUsed = gasBefore - gasleft();
                  }
                }
                function swap(SwapExecutionParams calldata execution)
                  external
                  payable
                  returns (uint256 returnAmount, uint256 gasUsed)
                {
                  uint256 gasBefore = gasleft();
                  SwapDescriptionV2 memory desc = execution.desc;
                  require(desc.minReturnAmount > 0, 'Min return should not be 0');
                  require(execution.targetData.length > 0, 'executorData should be not zero');
                  // simple mode swap
                  if (_flagsChecked(desc.flags, _SIMPLE_SWAP)) {
                    return
                      swapSimpleMode(IAggregationExecutor(execution.callTarget), desc, execution.targetData, execution.clientData);
                  }
                  _collectExtraETHIfNeeded(desc);
                  _permit(desc.srcToken, desc.amount, desc.permit);
                  bool feeInBps = _flagsChecked(desc.flags, _FEE_IN_BPS);
                  uint256 spentAmount;
                  address dstReceiver = desc.dstReceiver == address(0) ? msg.sender : desc.dstReceiver;
                  if (!_flagsChecked(desc.flags, _FEE_ON_DST)) {
                    // fee on src token
                    {
                      // take fee on srcToken
                      // deduct total swap amount
                      desc.amount = _takeFee(
                        desc.srcToken,
                        msg.sender,
                        desc.feeReceivers,
                        desc.feeAmounts,
                        _isETH(desc.srcToken) ? msg.value : desc.amount,
                        feeInBps
                      );
                      // transfer fund from msg.sender to our executor
                      _transferFromOrApproveTarget(msg.sender, address(0), desc, false);
                      // execute swap
                      (spentAmount, returnAmount) = _executeSwap(
                        execution.callTarget,
                        abi.encodeWithSelector(IAggregationExecutor.callBytes.selector, execution.targetData),
                        desc,
                        _isETH(desc.srcToken) ? desc.amount : 0,
                        dstReceiver
                      );
                    }
                  } else {
                    // fee on dst token
                    // router get dst token first
                    uint256 initialDstReceiverBalance = _getBalance(desc.dstToken, dstReceiver);
                    // transfer fund from msg.sender to our executor
                    _transferFromOrApproveTarget(msg.sender, address(0), desc, false);
                    // swap to receive dstToken on this router
                    (spentAmount, returnAmount) = _executeSwap(
                      execution.callTarget,
                      abi.encodeWithSelector(IAggregationExecutor.callBytes.selector, execution.targetData),
                      desc,
                      _isETH(desc.srcToken) ? msg.value : 0,
                      address(this)
                    );
                    {
                      // then take fee on dst token
                      uint256 leftAmount = _takeFee(
                        desc.dstToken,
                        address(this),
                        desc.feeReceivers,
                        desc.feeAmounts,
                        returnAmount,
                        feeInBps
                      );
                      _doTransferERC20(desc.dstToken, address(this), dstReceiver, leftAmount);
                    }
                    returnAmount = _getBalance(desc.dstToken, dstReceiver) - initialDstReceiverBalance;
                  }
                  _checkReturnAmount(spentAmount, returnAmount, desc);
                  emit Swapped(msg.sender, desc.srcToken, desc.dstToken, dstReceiver, spentAmount, returnAmount);
                  emit Exchange(execution.callTarget, returnAmount, _isETH(desc.dstToken) ? WETH : address(desc.dstToken));
                  emit ClientData(execution.clientData);
                  unchecked {
                    gasUsed = gasBefore - gasleft();
                  }
                }
                function swapSimpleMode(
                  IAggregationExecutor caller,
                  SwapDescriptionV2 memory desc,
                  bytes calldata executorData,
                  bytes calldata clientData
                ) public returns (uint256 returnAmount, uint256 gasUsed) {
                  uint256 gasBefore = gasleft();
                  require(!_isETH(desc.srcToken), 'src is eth, should use normal swap');
                  _permit(desc.srcToken, desc.amount, desc.permit);
                  address dstReceiver = (desc.dstReceiver == address(0)) ? msg.sender : desc.dstReceiver;
                  {
                    bool isBps = _flagsChecked(desc.flags, _FEE_IN_BPS);
                    if (!_flagsChecked(desc.flags, _FEE_ON_DST)) {
                      // take fee and deduct total swap amount
                      desc.amount = _takeFee(desc.srcToken, msg.sender, desc.feeReceivers, desc.feeAmounts, desc.amount, isBps);
                    } else {
                      dstReceiver = address(this);
                    }
                  }
                  uint256 initialDstBalance = _getBalance(desc.dstToken, dstReceiver);
                  uint256 initialSrcBalance = _getBalance(desc.srcToken, msg.sender);
                  _swapMultiSequencesWithSimpleMode(
                    caller,
                    address(desc.srcToken),
                    desc.amount,
                    address(desc.dstToken),
                    dstReceiver,
                    executorData
                  );
                  // amount returned to this router
                  returnAmount = _getBalance(desc.dstToken, dstReceiver) - initialDstBalance;
                  {
                    // take fee
                    if (_flagsChecked(desc.flags, _FEE_ON_DST)) {
                      {
                        bool isBps = _flagsChecked(desc.flags, _FEE_IN_BPS);
                        returnAmount = _takeFee(
                          desc.dstToken,
                          address(this),
                          desc.feeReceivers,
                          desc.feeAmounts,
                          returnAmount,
                          isBps
                        );
                      }
                      IERC20 dstToken = desc.dstToken;
                      dstReceiver = desc.dstReceiver == address(0) ? msg.sender : desc.dstReceiver;
                      // dst receiver initial balance
                      initialDstBalance = _getBalance(dstToken, dstReceiver);
                      // transfer remainning token to dst receiver
                      _doTransferERC20(dstToken, address(this), dstReceiver, returnAmount);
                      // amount returned to dst receiver
                      returnAmount = _getBalance(dstToken, dstReceiver) - initialDstBalance;
                    }
                  }
                  uint256 spentAmount = initialSrcBalance - _getBalance(desc.srcToken, msg.sender);
                  _checkReturnAmount(spentAmount, returnAmount, desc);
                  emit Swapped(msg.sender, desc.srcToken, desc.dstToken, dstReceiver, spentAmount, returnAmount);
                  emit Exchange(address(caller), returnAmount, _isETH(desc.dstToken) ? WETH : address(desc.dstToken));
                  emit ClientData(clientData);
                  unchecked {
                    gasUsed = gasBefore - gasleft();
                  }
                }
                function _doTransferERC20(
                  IERC20 token,
                  address from,
                  address to,
                  uint256 amount
                ) internal {
                  require(from != to, 'sender != recipient');
                  if (amount > 0) {
                    if (_isETH(token)) {
                      if (from == address(this)) TransferHelper.safeTransferETH(to, amount);
                    } else {
                      if (from == address(this)) {
                        TransferHelper.safeTransfer(address(token), to, amount);
                      } else {
                        TransferHelper.safeTransferFrom(address(token), from, to, amount);
                      }
                    }
                  }
                }
                // Only use this mode if the first pool of each sequence can receive tokenIn directly into the pool
                function _swapMultiSequencesWithSimpleMode(
                  IAggregationExecutor caller,
                  address tokenIn,
                  uint256 totalSwapAmount,
                  address tokenOut,
                  address dstReceiver,
                  bytes calldata data
                ) internal {
                  SimpleSwapData memory swapData = abi.decode(data, (SimpleSwapData));
                  require(swapData.deadline >= block.timestamp, 'ROUTER: Expired');
                  require(
                    swapData.firstPools.length == swapData.firstSwapAmounts.length &&
                      swapData.firstPools.length == swapData.swapDatas.length,
                    'invalid swap data length'
                  );
                  uint256 numberSeq = swapData.firstPools.length;
                  for (uint256 i = 0; i < numberSeq; i++) {
                    // collect amount to the first pool
                    {
                      uint256 balanceBefore = _getBalance(IERC20(tokenIn), msg.sender);
                      _doTransferERC20(IERC20(tokenIn), msg.sender, swapData.firstPools[i], swapData.firstSwapAmounts[i]);
                      require(swapData.firstSwapAmounts[i] <= totalSwapAmount, 'invalid swap amount');
                      uint256 spentAmount = balanceBefore - _getBalance(IERC20(tokenIn), msg.sender);
                      totalSwapAmount -= spentAmount;
                    }
                    {
                      // solhint-disable-next-line avoid-low-level-calls
                      // may take some native tokens for commission fee
                      (bool success, bytes memory result) = address(caller).call(
                        abi.encodeWithSelector(caller.swapSingleSequence.selector, swapData.swapDatas[i])
                      );
                      if (!success) {
                        revert(RevertReasonParser.parse(result, 'swapSingleSequence failed: '));
                      }
                    }
                  }
                  {
                    // solhint-disable-next-line avoid-low-level-calls
                    // may take some native tokens for commission fee
                    (bool success, bytes memory result) = address(caller).call(
                      abi.encodeWithSelector(
                        caller.finalTransactionProcessing.selector,
                        tokenIn,
                        tokenOut,
                        dstReceiver,
                        swapData.destTokenFeeData
                      )
                    );
                    if (!success) {
                      revert(RevertReasonParser.parse(result, 'finalTransactionProcessing failed: '));
                    }
                  }
                }
                function _getBalance(IERC20 token, address account) internal view returns (uint256) {
                  if (_isETH(token)) {
                    return account.balance;
                  } else {
                    return token.balanceOf(account);
                  }
                }
                function _isETH(IERC20 token) internal pure returns (bool) {
                  return (address(token) == ETH_ADDRESS);
                }
                /// @dev this function calls to external contract to execute swap and also validate the returned amounts
                function _executeSwap(
                  address callTarget,
                  bytes memory targetData,
                  SwapDescriptionV2 memory desc,
                  uint256 value,
                  address dstReceiver
                ) internal returns (uint256 spentAmount, uint256 returnAmount) {
                  uint256 initialDstBalance = _getBalance(desc.dstToken, dstReceiver);
                  uint256 routerInitialSrcBalance = _getBalance(desc.srcToken, address(this));
                  uint256 routerInitialDstBalance = _getBalance(desc.dstToken, address(this));
                  {
                    // call to external contract
                    (bool success, ) = callTarget.call{value: value}(targetData);
                    require(success, 'Call failed');
                  }
                  // if the `callTarget` returns amount to `msg.sender`, meaning this contract
                  if (dstReceiver != address(this)) {
                    uint256 stuckAmount = _getBalance(desc.dstToken, address(this)) - routerInitialDstBalance;
                    _doTransferERC20(desc.dstToken, address(this), dstReceiver, stuckAmount);
                  }
                  // safe check here
                  returnAmount = _getBalance(desc.dstToken, dstReceiver) - initialDstBalance;
                  spentAmount = desc.amount;
                  //should refund tokens router collected when partial fill
                  if (
                    _flagsChecked(desc.flags, _PARTIAL_FILL) && (_isETH(desc.srcToken) || _flagsChecked(desc.flags, _SHOULD_CLAIM))
                  ) {
                    uint256 currBalance = _getBalance(desc.srcToken, address(this));
                    if (currBalance != routerInitialSrcBalance) {
                      spentAmount = routerInitialSrcBalance - currBalance;
                      _doTransferERC20(desc.srcToken, address(this), msg.sender, desc.amount - spentAmount);
                    }
                  }
                }
                function _collectExtraETHIfNeeded(SwapDescriptionV2 memory desc) internal {
                  bool srcETH = _isETH(desc.srcToken);
                  if (_flagsChecked(desc.flags, _REQUIRES_EXTRA_ETH)) {
                    require(msg.value > (srcETH ? desc.amount : 0), 'Invalid msg.value');
                  } else {
                    require(msg.value == (srcETH ? desc.amount : 0), 'Invalid msg.value');
                  }
                }
                function _collectTokenIfNeeded(
                  SwapDescriptionV2 memory desc,
                  address from,
                  address to
                ) internal returns (bool collected, uint256 amount) {
                  require(!_isETH(desc.srcToken), 'Claim token is ETH');
                  uint256 initialRouterSrcBalance = _getBalance(desc.srcToken, address(this));
                  _doTransferERC20(desc.srcToken, from, to, desc.amount);
                  collected = true;
                  amount = _getBalance(desc.srcToken, address(this)) - initialRouterSrcBalance;
                }
                /// @dev transfer fund to `callTarget` or approve `approveTarget`
                function _transferFromOrApproveTarget(
                  address from,
                  address approveTarget,
                  SwapDescriptionV2 memory desc,
                  bool collected
                ) internal {
                  // if token is collected
                  require(desc.srcReceivers.length == desc.srcAmounts.length, 'invalid srcReceivers length');
                  if (collected) {
                    if (_flagsChecked(desc.flags, _APPROVE_FUND) && approveTarget != address(0)) {
                      // approve to approveTarget since some systems use an allowance proxy contract
                      desc.srcToken.safeIncreaseAllowance(approveTarget, desc.amount);
                      return;
                    }
                  }
                  uint256 total;
                  for (uint256 i; i < desc.srcReceivers.length; ++i) {
                    total += desc.srcAmounts[i];
                    _doTransferERC20(desc.srcToken, collected ? address(this) : from, desc.srcReceivers[i], desc.srcAmounts[i]);
                  }
                  require(total <= desc.amount, 'Exceeded desc.amount');
                }
                /// @dev token transferred from `from` to `feeData.recipients`
                function _takeFee(
                  IERC20 token,
                  address from,
                  address[] memory recipients,
                  uint256[] memory amounts,
                  uint256 totalAmount,
                  bool inBps
                ) internal returns (uint256 leftAmount) {
                  leftAmount = totalAmount;
                  uint256 recipientsLen = recipients.length;
                  if (recipientsLen > 0) {
                    bool isETH = _isETH(token);
                    uint256 balanceBefore = _getBalance(token, isETH ? address(this) : from);
                    require(amounts.length == recipientsLen, 'Invalid length');
                    for (uint256 i; i < recipientsLen; ++i) {
                      uint256 amount = inBps ? (totalAmount * amounts[i]) / BPS : amounts[i];
                      _doTransferERC20(token, isETH ? address(this) : from, recipients[i], amount);
                    }
                    uint256 totalFee = balanceBefore - _getBalance(token, isETH ? address(this) : from);
                    leftAmount = totalAmount - totalFee;
                    emit Fee(address(token), totalAmount, totalFee, recipients, amounts, inBps);
                  }
                }
                function _checkReturnAmount(
                  uint256 spentAmount,
                  uint256 returnAmount,
                  SwapDescriptionV2 memory desc
                ) internal pure {
                  if (_flagsChecked(desc.flags, _PARTIAL_FILL)) {
                    require(returnAmount * desc.amount >= desc.minReturnAmount * spentAmount, 'Return amount is not enough');
                  } else {
                    require(returnAmount >= desc.minReturnAmount, 'Return amount is not enough');
                  }
                }
                function _flagsChecked(uint256 number, uint256 flag) internal pure returns (bool) {
                  return number & flag != 0;
                }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
              import '@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol';
              import '../libraries/RevertReasonParser.sol';
              /*
              “Copyright (c) 2019-2021 1inch 
              Permission is hereby granted, free of charge, to any person obtaining a copy of this software
              and associated documentation files (the "Software"), to deal in the Software without restriction,
              including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
              and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
              subject to the following conditions: 
              The above copyright notice and this permission notice shall be included
              in all copies or substantial portions of the Software. 
              THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
              THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
              IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
              WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
              OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE”.
              */
              contract Permitable {
                event Error(string reason);
                function _permit(
                  IERC20 token,
                  uint256 amount,
                  bytes memory permit
                ) internal {
                  if (permit.length == 32 * 7) {
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory result) = address(token).call(
                      abi.encodePacked(IERC20Permit.permit.selector, permit)
                    );
                    if (!success) {
                      string memory reason = RevertReasonParser.parse(result, 'Permit call failed: ');
                      if (token.allowance(msg.sender, address(this)) < amount) {
                        revert(reason);
                      } else {
                        emit Error(reason);
                      }
                    }
                  }
                }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.12;
              interface IAggregationExecutor {
                function callBytes(bytes calldata data) external payable; // 0xd9c45357
                // callbytes per swap sequence
                function swapSingleSequence(bytes calldata data) external;
                function finalTransactionProcessing(
                  address tokenIn,
                  address tokenOut,
                  address to,
                  bytes calldata destTokenFeeData
                ) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.9;
              import '@openzeppelin/contracts/interfaces/IERC20.sol';
              interface IAggregationExecutor1Inch {
                function callBytes(address msgSender, bytes calldata data) external payable; // 0x2636f7f8
              }
              interface IAggregationRouter1InchV4 {
                function swap(
                  IAggregationExecutor1Inch caller,
                  SwapDescription1Inch calldata desc,
                  bytes calldata data
                ) external payable returns (uint256 returnAmount, uint256 gasLeft);
              }
              struct SwapDescription1Inch {
                IERC20 srcToken;
                IERC20 dstToken;
                address payable srcReceiver;
                address payable dstReceiver;
                uint256 amount;
                uint256 minReturnAmount;
                uint256 flags;
                bytes permit;
              }
              struct SwapDescriptionExecutor1Inch {
                IERC20 srcToken;
                IERC20 dstToken;
                address payable srcReceiver1Inch;
                address payable dstReceiver;
                address[] srcReceivers;
                uint256[] srcAmounts;
                uint256 amount;
                uint256 minReturnAmount;
                uint256 flags;
                bytes permit;
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity >=0.7.6;
              /*
              “Copyright (c) 2019-2021 1inch 
              Permission is hereby granted, free of charge, to any person obtaining a copy of this software
              and associated documentation files (the "Software"), to deal in the Software without restriction,
              including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
              and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
              subject to the following conditions: 
              The above copyright notice and this permission notice shall be included
              in all copies or substantial portions of the Software. 
              THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
              THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
              IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
              WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
              OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE”.
              */
              library RevertReasonParser {
                function parse(bytes memory data, string memory prefix) internal pure returns (string memory) {
                  // https://solidity.readthedocs.io/en/latest/control-structures.html#revert
                  // We assume that revert reason is abi-encoded as Error(string)
                  // 68 = 4-byte selector 0x08c379a0 + 32 bytes offset + 32 bytes length
                  if (data.length >= 68 && data[0] == '\\x08' && data[1] == '\\xc3' && data[2] == '\\x79' && data[3] == '\\xa0') {
                    string memory reason;
                    // solhint-disable no-inline-assembly
                    assembly {
                      // 68 = 32 bytes data length + 4-byte selector + 32 bytes offset
                      reason := add(data, 68)
                    }
                    /*
                              revert reason is padded up to 32 bytes with ABI encoder: Error(string)
                              also sometimes there is extra 32 bytes of zeros padded in the end:
                              https://github.com/ethereum/solidity/issues/10170
                              because of that we can't check for equality and instead check
                              that string length + extra 68 bytes is less than overall data length
                          */
                    require(data.length >= 68 + bytes(reason).length, 'Invalid revert reason');
                    return string(abi.encodePacked(prefix, 'Error(', reason, ')'));
                  }
                  // 36 = 4-byte selector 0x4e487b71 + 32 bytes integer
                  else if (data.length == 36 && data[0] == '\\x4e' && data[1] == '\\x48' && data[2] == '\\x7b' && data[3] == '\\x71') {
                    uint256 code;
                    // solhint-disable no-inline-assembly
                    assembly {
                      // 36 = 32 bytes data length + 4-byte selector
                      code := mload(add(data, 36))
                    }
                    return string(abi.encodePacked(prefix, 'Panic(', _toHex(code), ')'));
                  }
                  return string(abi.encodePacked(prefix, 'Unknown(', _toHex(data), ')'));
                }
                function _toHex(uint256 value) private pure returns (string memory) {
                  return _toHex(abi.encodePacked(value));
                }
                function _toHex(bytes memory data) private pure returns (string memory) {
                  bytes16 alphabet = 0x30313233343536373839616263646566;
                  bytes memory str = new bytes(2 + data.length * 2);
                  str[0] = '0';
                  str[1] = 'x';
                  for (uint256 i = 0; i < data.length; i++) {
                    str[2 * i + 2] = alphabet[uint8(data[i] >> 4)];
                    str[2 * i + 3] = alphabet[uint8(data[i] & 0x0f)];
                  }
                  return string(str);
                }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity >=0.5.16;
              // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
              library TransferHelper {
                function safeApprove(
                  address token,
                  address to,
                  uint256 value
                ) internal {
                  // bytes4(keccak256(bytes('approve(address,uint256)')));
                  (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
                  require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
                }
                function safeTransfer(
                  address token,
                  address to,
                  uint256 value
                ) internal {
                  // bytes4(keccak256(bytes('transfer(address,uint256)')));
                  if (value == 0) return;
                  (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
                  require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
                }
                function safeTransferFrom(
                  address token,
                  address from,
                  address to,
                  uint256 value
                ) internal {
                  // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                  if (value == 0) return;
                  (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
                  require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
                }
                function safeTransferETH(address to, uint256 value) internal {
                  if (value == 0) return;
                  (bool success, ) = to.call{value: value}(new bytes(0));
                  require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
                }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              abstract contract Ownable is Context {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  constructor() {
                      _transferOwnership(_msgSender());
                  }
                  /**
                   * @dev 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 {
                      _transferOwnership(address(0));
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Internal function without access restriction.
                   */
                  function _transferOwnership(address newOwner) internal virtual {
                      address oldOwner = _owner;
                      _owner = newOwner;
                      emit OwnershipTransferred(oldOwner, newOwner);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)
              pragma solidity ^0.8.0;
              import "../token/ERC20/IERC20.sol";
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 amount) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                   * @dev Moves `amount` tokens from `from` to `to` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) external returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               */
              interface IERC20Permit {
                  /**
                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                   * given ``owner``'s signed approval.
                   *
                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                   * ordering also apply here.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `deadline` must be a timestamp in the future.
                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                   * over the EIP712-formatted function arguments.
                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                   *
                   * For more information on the signature format, see the
                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                   * section].
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
                  /**
                   * @dev Returns the current nonce for `owner`. This value must be
                   * included whenever a signature is generated for {permit}.
                   *
                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                   * prevents a signature from being used multiple times.
                   */
                  function nonces(address owner) external view returns (uint256);
                  /**
                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }
              // 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/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              

              File 14 of 16: PendleRouterV4
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
              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 internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      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 overridden 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 internal 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 overridden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IPActionStorageV4 {
                  struct SelectorsToFacet {
                      address facet;
                      bytes4[] selectors;
                  }
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  event SelectorToFacetSet(bytes4 indexed selector, address indexed facet);
                  function owner() external view returns (address);
                  function pendingOwner() external view returns (address);
                  function transferOwnership(address newOwner, bool direct, bool renounce) external;
                  function claimOwnership() external;
                  function setSelectorToFacets(SelectorsToFacet[] calldata arr) external;
                  function selectorToFacet(bytes4 selector) external view returns (address);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.17;
              import "@openzeppelin/contracts/proxy/Proxy.sol";
              import "../interfaces/IPActionStorageV4.sol";
              import "./RouterStorage.sol";
              contract PendleRouterV4 is Proxy, RouterStorage {
                  constructor(address _owner, address actionStorage) {
                      RouterStorage.CoreStorage storage $ = _getCoreStorage();
                      $.owner = _owner;
                      $.selectorToFacet[IPActionStorageV4.setSelectorToFacets.selector] = actionStorage;
                  }
                  function _implementation() internal view override returns (address) {
                      RouterStorage.CoreStorage storage $ = _getCoreStorage();
                      address facet = $.selectorToFacet[msg.sig];
                      require(facet != address(0), "INVALID_SELECTOR");
                      return facet;
                  }
                  receive() external payable override {}
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.17;
              abstract contract RouterStorage {
                  struct CoreStorage {
                      address owner;
                      address pendingOwner;
                      mapping(bytes4 => address) selectorToFacet;
                  }
                  // keccak256(abi.encode(uint256(keccak256("pendle.routerv4.Core")) - 1)) & ~bytes32(uint256(0xff))
                  bytes32 private constant CORE_STORAGE_LOCATION = 0xf168c5b0cb4aca9a68f931815c18a144c61ad01d6dd7ca15bd6741672a0ab800;
                  function _getCoreStorage() internal pure returns (CoreStorage storage $) {
                      assembly {
                          $.slot := CORE_STORAGE_LOCATION
                      }
                  }
              }
              

              File 15 of 16: InitializableImmutableAdminUpgradeabilityProxy
              // SPDX-License-Identifier: AGPL-3.0
              pragma solidity 0.8.10;
              /**
               * @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) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @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');
                }
              }
              // SPDX-License-Identifier: AGPL-3.0
              pragma solidity 0.8.10;
              import './Proxy.sol';
              import '../contracts/Address.sol';
              /**
               * @title BaseUpgradeabilityProxy
               * @dev This contract implements a proxy that allows to change the
               * implementation address to which it will delegate.
               * Such a change is called an implementation upgrade.
               */
              contract BaseUpgradeabilityProxy is Proxy {
                /**
                 * @dev Emitted when the implementation is upgraded.
                 * @param implementation Address of the new implementation.
                 */
                event Upgraded(address indexed implementation);
                /**
                 * @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 Returns the current implementation.
                 * @return impl Address of the current implementation
                 */
                function _implementation() internal view override returns (address impl) {
                  bytes32 slot = IMPLEMENTATION_SLOT;
                  //solium-disable-next-line
                  assembly {
                    impl := sload(slot)
                  }
                }
                /**
                 * @dev Upgrades the proxy to a new implementation.
                 * @param newImplementation Address of the new implementation.
                 */
                function _upgradeTo(address newImplementation) internal {
                  _setImplementation(newImplementation);
                  emit Upgraded(newImplementation);
                }
                /**
                 * @dev Sets the implementation address of the proxy.
                 * @param newImplementation Address of the new implementation.
                 */
                function _setImplementation(address newImplementation) internal {
                  require(
                    Address.isContract(newImplementation),
                    'Cannot set a proxy implementation to a non-contract address'
                  );
                  bytes32 slot = IMPLEMENTATION_SLOT;
                  //solium-disable-next-line
                  assembly {
                    sstore(slot, newImplementation)
                  }
                }
              }
              // SPDX-License-Identifier: AGPL-3.0
              pragma solidity 0.8.10;
              import './BaseUpgradeabilityProxy.sol';
              /**
               * @title InitializableUpgradeabilityProxy
               * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
               * implementation and init data.
               */
              contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                /**
                 * @dev Contract initializer.
                 * @param _logic Address of the initial implementation.
                 * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                 * It should include the signature and the parameters of the function to be called, as described in
                 * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                 * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                 */
                function initialize(address _logic, bytes memory _data) public payable {
                  require(_implementation() == address(0));
                  assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                  _setImplementation(_logic);
                  if (_data.length > 0) {
                    (bool success, ) = _logic.delegatecall(_data);
                    require(success);
                  }
                }
              }
              // SPDX-License-Identifier: AGPL-3.0
              pragma solidity 0.8.10;
              /**
               * @title Proxy
               * @dev Implements delegation of calls to other contracts, with proper
               * forwarding of return values and bubbling of failures.
               * It defines a fallback function that delegates all calls to the address
               * returned by the abstract _implementation() internal function.
               */
              abstract contract Proxy {
                /**
                 * @dev Fallback function.
                 * Will run if no other function in the contract matches the call data.
                 * Implemented entirely in `_fallback`.
                 */
                fallback() external payable {
                  _fallback();
                }
                /**
                 * @return The Address of the implementation.
                 */
                function _implementation() internal view virtual returns (address);
                /**
                 * @dev Delegates execution to an implementation contract.
                 * This is a low level function that doesn't return to its internal call site.
                 * It will return to the external caller whatever the implementation returns.
                 * @param implementation Address to delegate.
                 */
                function _delegate(address implementation) internal {
                  //solium-disable-next-line
                  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 Function that is run as the first thing in the fallback function.
                 * Can be redefined in derived contracts to add functionality.
                 * Redefinitions must call super._willFallback().
                 */
                function _willFallback() internal virtual {}
                /**
                 * @dev fallback implementation.
                 * Extracted to enable manual triggering.
                 */
                function _fallback() internal {
                  _willFallback();
                  _delegate(_implementation());
                }
              }
              // SPDX-License-Identifier: AGPL-3.0
              pragma solidity 0.8.10;
              import {BaseUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @notice This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks.
               * @dev The admin role is stored in an immutable, which helps saving transactions costs
               * All external functions in this contract must be guarded by the
               * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
               * feature proposal that would enable this to be done automatically.
               */
              contract BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address internal immutable _admin;
                /**
                 * @dev Constructor.
                 * @param admin The address of the admin
                 */
                constructor(address admin) {
                  _admin = admin;
                }
                modifier ifAdmin() {
                  if (msg.sender == _admin) {
                    _;
                  } else {
                    _fallback();
                  }
                }
                /**
                 * @notice Return the admin address
                 * @return The address of the proxy admin.
                 */
                function admin() external ifAdmin returns (address) {
                  return _admin;
                }
                /**
                 * @notice Return the implementation address
                 * @return The address of the implementation.
                 */
                function implementation() external ifAdmin returns (address) {
                  return _implementation();
                }
                /**
                 * @notice Upgrade the backing implementation of the proxy.
                 * @dev Only the admin can call this function.
                 * @param newImplementation The address of the new implementation.
                 */
                function upgradeTo(address newImplementation) external ifAdmin {
                  _upgradeTo(newImplementation);
                }
                /**
                 * @notice Upgrade the backing implementation of the proxy and call a function
                 * on the new implementation.
                 * @dev This is useful to initialize the proxied contract.
                 * @param newImplementation The address of the new implementation.
                 * @param data Data to send as msg.data in the low level call.
                 * It should include the signature and the parameters of the function to be called, as described in
                 * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                 */
                function upgradeToAndCall(address newImplementation, bytes calldata data)
                  external
                  payable
                  ifAdmin
                {
                  _upgradeTo(newImplementation);
                  (bool success, ) = newImplementation.delegatecall(data);
                  require(success);
                }
                /**
                 * @notice Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != _admin, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: AGPL-3.0
              pragma solidity 0.8.10;
              import {InitializableUpgradeabilityProxy} from '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
              import {Proxy} from '../../../dependencies/openzeppelin/upgradeability/Proxy.sol';
              import {BaseImmutableAdminUpgradeabilityProxy} from './BaseImmutableAdminUpgradeabilityProxy.sol';
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @author Aave
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                /**
                 * @dev Constructor.
                 * @param admin The address of the admin
                 */
                constructor(address admin) BaseImmutableAdminUpgradeabilityProxy(admin) {
                  // Intentionally left blank
                }
                /// @inheritdoc BaseImmutableAdminUpgradeabilityProxy
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }
              

              File 16 of 16: Maestro
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.20;
              import "@openzeppelin/contracts/utils/math/SignedMath.sol";
              import "@openzeppelin/contracts/utils/math/SafeCast.sol";
              import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
              import { StorageSlot } from "@openzeppelin/contracts/utils/StorageSlot.sol";
              import { IPermit2 } from "../dependencies/Uniswap.sol";
              import "../dependencies/PayableMulticall.sol";
              import "../interfaces/IMaestro.sol";
              import "../interfaces/IContango.sol";
              import "../interfaces/IOrderManager.sol";
              import "../interfaces/IVault.sol";
              import "../libraries/Errors.sol";
              import "../libraries/Validations.sol";
              import "../libraries/ERC20Lib.sol";
              import "../utils/SimpleSpotExecutor.sol";
              import "../utils/Router.sol";
              contract Maestro is IMaestro, UUPSUpgradeable, PayableMulticall {
                  using SignedMath for int256;
                  using SafeCast for *;
                  using SafeERC20 for IERC20Permit;
                  using ERC20Lib for *;
                  using { validateCreatePositionPermissions, validateModifyPositionPermissions } for PositionNFT;
                  uint256 public constant ALL = 0; // used to indicate that the full amount should be used, 0 is cheaper on calldata than type(uint256).max
                  bytes32 public constant INTEGRATIONS_SLOT = keccak256("Maestro.integrations");
                  Timelock public immutable timelock;
                  IContango public immutable contango;
                  IOrderManager public immutable orderManager;
                  IVault public immutable vault;
                  PositionNFT public immutable positionNFT;
                  IWETH9 public immutable nativeToken;
                  IPermit2 public immutable permit2;
                  SimpleSpotExecutor public immutable spotExecutor;
                  address public immutable treasury;
                  Router public immutable router;
                  constructor(
                      Timelock _timelock,
                      IContango _contango,
                      IOrderManager _orderManager,
                      IVault _vault,
                      IPermit2 _permit2,
                      SimpleSpotExecutor _spotExecutor,
                      address _treasury,
                      Router _router
                  ) {
                      timelock = _timelock;
                      contango = _contango;
                      orderManager = _orderManager;
                      vault = _vault;
                      permit2 = _permit2;
                      spotExecutor = _spotExecutor;
                      treasury = _treasury;
                      positionNFT = _contango.positionNFT();
                      nativeToken = _vault.nativeToken();
                      router = _router;
                  }
                  function deposit(IERC20 token, uint256 amount) public payable override returns (uint256) {
                      return vault.deposit(token, msg.sender, amount);
                  }
                  function depositNative() public payable returns (uint256) {
                      return vault.depositNative{ value: msg.value }(msg.sender);
                  }
                  function applyPermit(IERC20 token, EIP2098Permit calldata permit, address spender) public {
                      token.applyPermit(permit, msg.sender, spender);
                  }
                  function depositWithPermit(IERC20 token, EIP2098Permit calldata permit, uint256 amount) public payable override returns (uint256) {
                      applyPermit(token, permit, address(vault));
                      return deposit(token, amount == ALL ? permit.amount : amount);
                  }
                  function usePermit2(IERC20 token, EIP2098Permit calldata permit, uint256 amount, address to) public {
                      permit2.pullFundsWithPermit2(token, permit, amount, msg.sender, to);
                  }
                  function depositWithPermit2(IERC20 token, EIP2098Permit calldata permit, uint256 amount) public payable override returns (uint256) {
                      amount = amount == ALL ? permit.amount : amount;
                      usePermit2(token, permit, amount, address(vault));
                      return deposit(token, amount);
                  }
                  function _swapAndDeposit(address payer, IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData)
                      internal
                      returns (uint256)
                  {
                      if (payer != address(0)) tokenToSell.transferOut(payer, address(spotExecutor), swapData.amountIn);
                      uint256 output = spotExecutor.executeSwap({
                          tokenToSell: tokenToSell,
                          tokenToBuy: tokenToDeposit,
                          spender: swapData.spender,
                          amountIn: swapData.amountIn,
                          minAmountOut: swapData.minAmountOut,
                          router: swapData.router,
                          swapBytes: swapData.swapBytes,
                          to: address(vault)
                      });
                      return deposit(tokenToDeposit, output);
                  }
                  function swapAndDeposit(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData)
                      public
                      payable
                      override
                      returns (uint256)
                  {
                      return _swapAndDeposit(msg.sender, tokenToSell, tokenToDeposit, swapData);
                  }
                  function swapAndDepositNative(IERC20 tokenToDeposit, SwapData calldata swapData) public payable override returns (uint256) {
                      nativeToken.deposit{ value: msg.value }();
                      return _swapAndDeposit(address(this), nativeToken, tokenToDeposit, swapData);
                  }
                  function swapAndDepositWithPermit(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData, EIP2098Permit calldata permit)
                      public
                      payable
                      override
                      returns (uint256)
                  {
                      applyPermit(tokenToSell, permit, address(this));
                      return _swapAndDeposit(msg.sender, tokenToSell, tokenToDeposit, swapData);
                  }
                  function swapAndDepositWithPermit2(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData, EIP2098Permit calldata permit)
                      public
                      payable
                      override
                      returns (uint256)
                  {
                      usePermit2(tokenToSell, permit, swapData.amountIn, address(spotExecutor));
                      return _swapAndDeposit(address(0), tokenToSell, tokenToDeposit, swapData);
                  }
                  function withdraw(IERC20 token, uint256 amount, address to) public payable override returns (uint256) {
                      if (amount == ALL) amount = vault.balanceOf(token, msg.sender);
                      if (amount == 0) return 0;
                      return vault.withdraw(token, msg.sender, amount, to);
                  }
                  function withdrawNative(uint256 amount, address to) public payable override returns (uint256) {
                      if (amount == ALL) amount = vault.balanceOf(nativeToken, msg.sender);
                      if (amount == 0) return 0;
                      return vault.withdrawNative(msg.sender, amount, to);
                  }
                  function swapAndWithdraw(IERC20 tokenToSell, IERC20 tokenToReceive, SwapData calldata swapData, address to)
                      public
                      payable
                      returns (uint256)
                  {
                      withdraw(tokenToSell, swapData.amountIn, address(spotExecutor));
                      return spotExecutor.executeSwap({
                          tokenToSell: tokenToSell,
                          tokenToBuy: tokenToReceive,
                          spender: swapData.spender,
                          amountIn: swapData.amountIn,
                          minAmountOut: swapData.minAmountOut,
                          router: swapData.router,
                          swapBytes: swapData.swapBytes,
                          to: to
                      });
                  }
                  function swapAndWithdrawNative(IERC20 tokenToSell, SwapData calldata swapData, address payable to)
                      public
                      payable
                      returns (uint256 output)
                  {
                      withdraw(tokenToSell, swapData.amountIn, address(spotExecutor));
                      output = spotExecutor.executeSwap({
                          tokenToSell: tokenToSell,
                          tokenToBuy: nativeToken,
                          spender: swapData.spender,
                          amountIn: swapData.amountIn,
                          minAmountOut: swapData.minAmountOut,
                          router: swapData.router,
                          swapBytes: swapData.swapBytes,
                          to: address(this)
                      });
                      nativeToken.transferOutNative(to, output);
                  }
                  function tradeWithFees(TradeParams memory tradeParams, ExecutionParams calldata execParams, FeeParams memory feeParams)
                      public
                      payable
                      returns (PositionId positionId_, Trade memory trade_)
                  {
                      if (positionNFT.exists(tradeParams.positionId)) positionNFT.validateModifyPositionPermissions(tradeParams.positionId);
                      if (feeParams.amount > 0 && tradeParams.cashflow > 0) withdraw(feeParams.token, feeParams.amount, treasury);
                      if (tradeParams.cashflow == type(int256).max) {
                          tradeParams.cashflow = vault.balanceOf(_cashflowToken(tradeParams), msg.sender).toInt256();
                      }
                      (positionId_, trade_) = contango.tradeOnBehalfOf(tradeParams, execParams, msg.sender);
                      if (feeParams.amount > 0) {
                          if (tradeParams.cashflow <= 0) withdraw(feeParams.token, feeParams.amount, treasury);
                          emit FeeCollected(positionId_, msg.sender, treasury, feeParams.token, feeParams.amount, feeParams.basisPoints);
                      }
                  }
                  function trade(TradeParams calldata tradeParams, ExecutionParams calldata execParams)
                      external
                      payable
                      returns (PositionId, Trade memory)
                  {
                      FeeParams memory feeParams;
                      return tradeWithFees(tradeParams, execParams, feeParams);
                  }
                  function tradeAndLinkedOrder(
                      TradeParams calldata tradeParams,
                      ExecutionParams calldata execParams,
                      LinkedOrderParams memory linkedOrderParams
                  ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId) {
                      FeeParams memory feeParams;
                      return tradeAndLinkedOrderWithFees(tradeParams, execParams, linkedOrderParams, feeParams);
                  }
                  function tradeAndLinkedOrderWithFees(
                      TradeParams calldata tradeParams,
                      ExecutionParams calldata execParams,
                      LinkedOrderParams memory linkedOrderParams,
                      FeeParams memory feeParams
                  ) public payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId) {
                      (positionId, trade_) = tradeWithFees(tradeParams, execParams, feeParams);
                      linkedOrderId = placeLinkedOrder(positionId, linkedOrderParams);
                  }
                  function tradeAndLinkedOrders(
                      TradeParams calldata tradeParams,
                      ExecutionParams calldata execParams,
                      LinkedOrderParams memory linkedOrderParams1,
                      LinkedOrderParams memory linkedOrderParams2
                  ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId1, OrderId linkedOrderId2) {
                      FeeParams memory feeParams;
                      return tradeAndLinkedOrdersWithFees(tradeParams, execParams, linkedOrderParams1, linkedOrderParams2, feeParams);
                  }
                  function tradeAndLinkedOrdersWithFees(
                      TradeParams calldata tradeParams,
                      ExecutionParams calldata execParams,
                      LinkedOrderParams memory linkedOrderParams1,
                      LinkedOrderParams memory linkedOrderParams2,
                      FeeParams memory feeParams
                  ) public payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId1, OrderId linkedOrderId2) {
                      (positionId, trade_) = tradeWithFees(tradeParams, execParams, feeParams);
                      linkedOrderId1 = placeLinkedOrder(positionId, linkedOrderParams1);
                      linkedOrderId2 = placeLinkedOrder(positionId, linkedOrderParams2);
                  }
                  function placeLinkedOrder(PositionId positionId, LinkedOrderParams memory params) public payable override returns (OrderId orderId) {
                      positionNFT.validateModifyPositionPermissions(positionId);
                      orderId = _placeLinkedOrder(positionId, params);
                  }
                  function _placeLinkedOrder(PositionId positionId, LinkedOrderParams memory params) private returns (OrderId orderId) {
                      return orderManager.placeOnBehalfOf(
                          OrderParams({
                              positionId: positionId,
                              quantity: type(int128).min,
                              limitPrice: params.limitPrice,
                              tolerance: params.tolerance,
                              cashflow: 0,
                              cashflowCcy: params.cashflowCcy,
                              deadline: params.deadline,
                              orderType: params.orderType
                          }),
                          msg.sender
                      );
                  }
                  function cancel(OrderId orderId) public payable override {
                      if (!positionNFT.isApprovedForAll(orderManager.orders(orderId).owner, msg.sender)) revert Unauthorised(msg.sender);
                      orderManager.cancel(orderId);
                  }
                  function _deposit(IERC20 token, uint256 amount) internal returns (uint256) {
                      if (msg.value > 0) {
                          if (token == nativeToken) return depositNative();
                          else revert NotNativeToken(token);
                      } else {
                          return deposit(token, amount);
                      }
                  }
                  function _validatePermitAmount(int256 cashflow, EIP2098Permit memory permit) private pure {
                      if (cashflow > 0 && int256(permit.amount) < cashflow) revert InsufficientPermitAmount(uint256(cashflow), permit.amount);
                  }
                  function _authorizeUpgrade(address) internal virtual override onlyTimelock { }
                  function _cashflowToken(TradeParams memory tradeParams) internal view returns (IERC20 cashflowToken) {
                      Instrument memory instrument = contango.instrument(tradeParams.positionId.getSymbol());
                      cashflowToken = tradeParams.cashflowCcy == Currency.Base ? instrument.base : instrument.quote;
                  }
                  receive() external payable {
                      if (msg.sender != address(nativeToken)) revert SenderIsNotNativeToken(msg.sender, address(nativeToken));
                  }
                  // =================== Routing ===================
                  /// @dev Allow users to route calls to a contract without inheriting this contract's permissions, to be used with batch
                  function route(address integration, uint256 value, bytes calldata data) external payable returns (bytes memory result) {
                      require(isIntegration(integration), UnknownIntegration(integration));
                      return router.route{ value: value }(integration, value, data);
                  }
                  function isIntegration(address integration) public view returns (bool) {
                      return _integrationSlot(integration).value;
                  }
                  function setIntegration(address integration, bool whitelisted) public onlyTimelock {
                      _integrationSlot(integration).value = whitelisted;
                      emit IntegrationSet(integration, whitelisted);
                  }
                  function _integrationSlot(address integration) private pure returns (StorageSlot.BooleanSlot storage slot) {
                      return StorageSlot.getBooleanSlot(keccak256(abi.encodePacked(integration, INTEGRATIONS_SLOT)));
                  }
                  function transferPosition(PositionId positionId, address to, bytes memory data) external payable override {
                      positionNFT.safeTransferFrom(msg.sender, to, positionId.asUint(), data);
                  }
                  modifier onlyTimelock() {
                      if (msg.sender != Timelock.unwrap(timelock)) revert Unauthorised(msg.sender);
                      _;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard signed math utilities missing in the Solidity language.
               */
              library SignedMath {
                  /**
                   * @dev Returns the largest of two signed numbers.
                   */
                  function max(int256 a, int256 b) internal pure returns (int256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two signed numbers.
                   */
                  function min(int256 a, int256 b) internal pure returns (int256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two signed numbers without overflow.
                   * The result is rounded towards zero.
                   */
                  function average(int256 a, int256 b) internal pure returns (int256) {
                      // Formula from the book "Hacker's Delight"
                      int256 x = (a & b) + ((a ^ b) >> 1);
                      return x + (int256(uint256(x) >> 255) & (a ^ b));
                  }
                  /**
                   * @dev Returns the absolute unsigned value of a signed value.
                   */
                  function abs(int256 n) internal pure returns (uint256) {
                      unchecked {
                          // must be unchecked in order to support `n = type(int256).min`
                          return uint256(n >= 0 ? n : -n);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
              // This file was procedurally generated from scripts/generate/templates/SafeCast.js.
              pragma solidity ^0.8.0;
              /**
               * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
               * checks.
               *
               * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
               * easily result in undesired exploitation or bugs, since developers usually
               * assume that overflows raise errors. `SafeCast` restores this intuition by
               * reverting the transaction when such an operation overflows.
               *
               * Using this library instead of the unchecked operations eliminates an entire
               * class of bugs, so it's recommended to use it always.
               *
               * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
               * all math on `uint256` and `int256` and then downcasting.
               */
              library SafeCast {
                  /**
                   * @dev Returns the downcasted uint248 from uint256, reverting on
                   * overflow (when the input is greater than largest uint248).
                   *
                   * Counterpart to Solidity's `uint248` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 248 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint248(uint256 value) internal pure returns (uint248) {
                      require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
                      return uint248(value);
                  }
                  /**
                   * @dev Returns the downcasted uint240 from uint256, reverting on
                   * overflow (when the input is greater than largest uint240).
                   *
                   * Counterpart to Solidity's `uint240` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 240 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint240(uint256 value) internal pure returns (uint240) {
                      require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
                      return uint240(value);
                  }
                  /**
                   * @dev Returns the downcasted uint232 from uint256, reverting on
                   * overflow (when the input is greater than largest uint232).
                   *
                   * Counterpart to Solidity's `uint232` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 232 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint232(uint256 value) internal pure returns (uint232) {
                      require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
                      return uint232(value);
                  }
                  /**
                   * @dev Returns the downcasted uint224 from uint256, reverting on
                   * overflow (when the input is greater than largest uint224).
                   *
                   * Counterpart to Solidity's `uint224` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 224 bits
                   *
                   * _Available since v4.2._
                   */
                  function toUint224(uint256 value) internal pure returns (uint224) {
                      require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
                      return uint224(value);
                  }
                  /**
                   * @dev Returns the downcasted uint216 from uint256, reverting on
                   * overflow (when the input is greater than largest uint216).
                   *
                   * Counterpart to Solidity's `uint216` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 216 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint216(uint256 value) internal pure returns (uint216) {
                      require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
                      return uint216(value);
                  }
                  /**
                   * @dev Returns the downcasted uint208 from uint256, reverting on
                   * overflow (when the input is greater than largest uint208).
                   *
                   * Counterpart to Solidity's `uint208` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 208 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint208(uint256 value) internal pure returns (uint208) {
                      require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
                      return uint208(value);
                  }
                  /**
                   * @dev Returns the downcasted uint200 from uint256, reverting on
                   * overflow (when the input is greater than largest uint200).
                   *
                   * Counterpart to Solidity's `uint200` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 200 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint200(uint256 value) internal pure returns (uint200) {
                      require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
                      return uint200(value);
                  }
                  /**
                   * @dev Returns the downcasted uint192 from uint256, reverting on
                   * overflow (when the input is greater than largest uint192).
                   *
                   * Counterpart to Solidity's `uint192` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 192 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint192(uint256 value) internal pure returns (uint192) {
                      require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
                      return uint192(value);
                  }
                  /**
                   * @dev Returns the downcasted uint184 from uint256, reverting on
                   * overflow (when the input is greater than largest uint184).
                   *
                   * Counterpart to Solidity's `uint184` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 184 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint184(uint256 value) internal pure returns (uint184) {
                      require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
                      return uint184(value);
                  }
                  /**
                   * @dev Returns the downcasted uint176 from uint256, reverting on
                   * overflow (when the input is greater than largest uint176).
                   *
                   * Counterpart to Solidity's `uint176` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 176 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint176(uint256 value) internal pure returns (uint176) {
                      require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
                      return uint176(value);
                  }
                  /**
                   * @dev Returns the downcasted uint168 from uint256, reverting on
                   * overflow (when the input is greater than largest uint168).
                   *
                   * Counterpart to Solidity's `uint168` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 168 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint168(uint256 value) internal pure returns (uint168) {
                      require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
                      return uint168(value);
                  }
                  /**
                   * @dev Returns the downcasted uint160 from uint256, reverting on
                   * overflow (when the input is greater than largest uint160).
                   *
                   * Counterpart to Solidity's `uint160` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 160 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint160(uint256 value) internal pure returns (uint160) {
                      require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
                      return uint160(value);
                  }
                  /**
                   * @dev Returns the downcasted uint152 from uint256, reverting on
                   * overflow (when the input is greater than largest uint152).
                   *
                   * Counterpart to Solidity's `uint152` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 152 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint152(uint256 value) internal pure returns (uint152) {
                      require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
                      return uint152(value);
                  }
                  /**
                   * @dev Returns the downcasted uint144 from uint256, reverting on
                   * overflow (when the input is greater than largest uint144).
                   *
                   * Counterpart to Solidity's `uint144` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 144 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint144(uint256 value) internal pure returns (uint144) {
                      require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
                      return uint144(value);
                  }
                  /**
                   * @dev Returns the downcasted uint136 from uint256, reverting on
                   * overflow (when the input is greater than largest uint136).
                   *
                   * Counterpart to Solidity's `uint136` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 136 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint136(uint256 value) internal pure returns (uint136) {
                      require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
                      return uint136(value);
                  }
                  /**
                   * @dev Returns the downcasted uint128 from uint256, reverting on
                   * overflow (when the input is greater than largest uint128).
                   *
                   * Counterpart to Solidity's `uint128` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 128 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint128(uint256 value) internal pure returns (uint128) {
                      require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
                      return uint128(value);
                  }
                  /**
                   * @dev Returns the downcasted uint120 from uint256, reverting on
                   * overflow (when the input is greater than largest uint120).
                   *
                   * Counterpart to Solidity's `uint120` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 120 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint120(uint256 value) internal pure returns (uint120) {
                      require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
                      return uint120(value);
                  }
                  /**
                   * @dev Returns the downcasted uint112 from uint256, reverting on
                   * overflow (when the input is greater than largest uint112).
                   *
                   * Counterpart to Solidity's `uint112` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 112 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint112(uint256 value) internal pure returns (uint112) {
                      require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
                      return uint112(value);
                  }
                  /**
                   * @dev Returns the downcasted uint104 from uint256, reverting on
                   * overflow (when the input is greater than largest uint104).
                   *
                   * Counterpart to Solidity's `uint104` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 104 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint104(uint256 value) internal pure returns (uint104) {
                      require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
                      return uint104(value);
                  }
                  /**
                   * @dev Returns the downcasted uint96 from uint256, reverting on
                   * overflow (when the input is greater than largest uint96).
                   *
                   * Counterpart to Solidity's `uint96` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 96 bits
                   *
                   * _Available since v4.2._
                   */
                  function toUint96(uint256 value) internal pure returns (uint96) {
                      require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
                      return uint96(value);
                  }
                  /**
                   * @dev Returns the downcasted uint88 from uint256, reverting on
                   * overflow (when the input is greater than largest uint88).
                   *
                   * Counterpart to Solidity's `uint88` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 88 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint88(uint256 value) internal pure returns (uint88) {
                      require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
                      return uint88(value);
                  }
                  /**
                   * @dev Returns the downcasted uint80 from uint256, reverting on
                   * overflow (when the input is greater than largest uint80).
                   *
                   * Counterpart to Solidity's `uint80` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 80 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint80(uint256 value) internal pure returns (uint80) {
                      require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
                      return uint80(value);
                  }
                  /**
                   * @dev Returns the downcasted uint72 from uint256, reverting on
                   * overflow (when the input is greater than largest uint72).
                   *
                   * Counterpart to Solidity's `uint72` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 72 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint72(uint256 value) internal pure returns (uint72) {
                      require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
                      return uint72(value);
                  }
                  /**
                   * @dev Returns the downcasted uint64 from uint256, reverting on
                   * overflow (when the input is greater than largest uint64).
                   *
                   * Counterpart to Solidity's `uint64` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 64 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint64(uint256 value) internal pure returns (uint64) {
                      require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
                      return uint64(value);
                  }
                  /**
                   * @dev Returns the downcasted uint56 from uint256, reverting on
                   * overflow (when the input is greater than largest uint56).
                   *
                   * Counterpart to Solidity's `uint56` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 56 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint56(uint256 value) internal pure returns (uint56) {
                      require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
                      return uint56(value);
                  }
                  /**
                   * @dev Returns the downcasted uint48 from uint256, reverting on
                   * overflow (when the input is greater than largest uint48).
                   *
                   * Counterpart to Solidity's `uint48` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 48 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint48(uint256 value) internal pure returns (uint48) {
                      require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
                      return uint48(value);
                  }
                  /**
                   * @dev Returns the downcasted uint40 from uint256, reverting on
                   * overflow (when the input is greater than largest uint40).
                   *
                   * Counterpart to Solidity's `uint40` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 40 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint40(uint256 value) internal pure returns (uint40) {
                      require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
                      return uint40(value);
                  }
                  /**
                   * @dev Returns the downcasted uint32 from uint256, reverting on
                   * overflow (when the input is greater than largest uint32).
                   *
                   * Counterpart to Solidity's `uint32` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 32 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint32(uint256 value) internal pure returns (uint32) {
                      require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
                      return uint32(value);
                  }
                  /**
                   * @dev Returns the downcasted uint24 from uint256, reverting on
                   * overflow (when the input is greater than largest uint24).
                   *
                   * Counterpart to Solidity's `uint24` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 24 bits
                   *
                   * _Available since v4.7._
                   */
                  function toUint24(uint256 value) internal pure returns (uint24) {
                      require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
                      return uint24(value);
                  }
                  /**
                   * @dev Returns the downcasted uint16 from uint256, reverting on
                   * overflow (when the input is greater than largest uint16).
                   *
                   * Counterpart to Solidity's `uint16` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 16 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint16(uint256 value) internal pure returns (uint16) {
                      require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
                      return uint16(value);
                  }
                  /**
                   * @dev Returns the downcasted uint8 from uint256, reverting on
                   * overflow (when the input is greater than largest uint8).
                   *
                   * Counterpart to Solidity's `uint8` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 8 bits
                   *
                   * _Available since v2.5._
                   */
                  function toUint8(uint256 value) internal pure returns (uint8) {
                      require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
                      return uint8(value);
                  }
                  /**
                   * @dev Converts a signed int256 into an unsigned uint256.
                   *
                   * Requirements:
                   *
                   * - input must be greater than or equal to 0.
                   *
                   * _Available since v3.0._
                   */
                  function toUint256(int256 value) internal pure returns (uint256) {
                      require(value >= 0, "SafeCast: value must be positive");
                      return uint256(value);
                  }
                  /**
                   * @dev Returns the downcasted int248 from int256, reverting on
                   * overflow (when the input is less than smallest int248 or
                   * greater than largest int248).
                   *
                   * Counterpart to Solidity's `int248` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 248 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt248(int256 value) internal pure returns (int248 downcasted) {
                      downcasted = int248(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
                  }
                  /**
                   * @dev Returns the downcasted int240 from int256, reverting on
                   * overflow (when the input is less than smallest int240 or
                   * greater than largest int240).
                   *
                   * Counterpart to Solidity's `int240` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 240 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt240(int256 value) internal pure returns (int240 downcasted) {
                      downcasted = int240(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
                  }
                  /**
                   * @dev Returns the downcasted int232 from int256, reverting on
                   * overflow (when the input is less than smallest int232 or
                   * greater than largest int232).
                   *
                   * Counterpart to Solidity's `int232` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 232 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt232(int256 value) internal pure returns (int232 downcasted) {
                      downcasted = int232(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
                  }
                  /**
                   * @dev Returns the downcasted int224 from int256, reverting on
                   * overflow (when the input is less than smallest int224 or
                   * greater than largest int224).
                   *
                   * Counterpart to Solidity's `int224` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 224 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt224(int256 value) internal pure returns (int224 downcasted) {
                      downcasted = int224(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
                  }
                  /**
                   * @dev Returns the downcasted int216 from int256, reverting on
                   * overflow (when the input is less than smallest int216 or
                   * greater than largest int216).
                   *
                   * Counterpart to Solidity's `int216` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 216 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt216(int256 value) internal pure returns (int216 downcasted) {
                      downcasted = int216(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
                  }
                  /**
                   * @dev Returns the downcasted int208 from int256, reverting on
                   * overflow (when the input is less than smallest int208 or
                   * greater than largest int208).
                   *
                   * Counterpart to Solidity's `int208` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 208 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt208(int256 value) internal pure returns (int208 downcasted) {
                      downcasted = int208(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
                  }
                  /**
                   * @dev Returns the downcasted int200 from int256, reverting on
                   * overflow (when the input is less than smallest int200 or
                   * greater than largest int200).
                   *
                   * Counterpart to Solidity's `int200` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 200 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt200(int256 value) internal pure returns (int200 downcasted) {
                      downcasted = int200(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
                  }
                  /**
                   * @dev Returns the downcasted int192 from int256, reverting on
                   * overflow (when the input is less than smallest int192 or
                   * greater than largest int192).
                   *
                   * Counterpart to Solidity's `int192` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 192 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt192(int256 value) internal pure returns (int192 downcasted) {
                      downcasted = int192(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
                  }
                  /**
                   * @dev Returns the downcasted int184 from int256, reverting on
                   * overflow (when the input is less than smallest int184 or
                   * greater than largest int184).
                   *
                   * Counterpart to Solidity's `int184` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 184 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt184(int256 value) internal pure returns (int184 downcasted) {
                      downcasted = int184(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
                  }
                  /**
                   * @dev Returns the downcasted int176 from int256, reverting on
                   * overflow (when the input is less than smallest int176 or
                   * greater than largest int176).
                   *
                   * Counterpart to Solidity's `int176` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 176 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt176(int256 value) internal pure returns (int176 downcasted) {
                      downcasted = int176(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
                  }
                  /**
                   * @dev Returns the downcasted int168 from int256, reverting on
                   * overflow (when the input is less than smallest int168 or
                   * greater than largest int168).
                   *
                   * Counterpart to Solidity's `int168` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 168 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt168(int256 value) internal pure returns (int168 downcasted) {
                      downcasted = int168(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
                  }
                  /**
                   * @dev Returns the downcasted int160 from int256, reverting on
                   * overflow (when the input is less than smallest int160 or
                   * greater than largest int160).
                   *
                   * Counterpart to Solidity's `int160` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 160 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt160(int256 value) internal pure returns (int160 downcasted) {
                      downcasted = int160(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
                  }
                  /**
                   * @dev Returns the downcasted int152 from int256, reverting on
                   * overflow (when the input is less than smallest int152 or
                   * greater than largest int152).
                   *
                   * Counterpart to Solidity's `int152` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 152 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt152(int256 value) internal pure returns (int152 downcasted) {
                      downcasted = int152(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
                  }
                  /**
                   * @dev Returns the downcasted int144 from int256, reverting on
                   * overflow (when the input is less than smallest int144 or
                   * greater than largest int144).
                   *
                   * Counterpart to Solidity's `int144` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 144 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt144(int256 value) internal pure returns (int144 downcasted) {
                      downcasted = int144(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
                  }
                  /**
                   * @dev Returns the downcasted int136 from int256, reverting on
                   * overflow (when the input is less than smallest int136 or
                   * greater than largest int136).
                   *
                   * Counterpart to Solidity's `int136` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 136 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt136(int256 value) internal pure returns (int136 downcasted) {
                      downcasted = int136(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
                  }
                  /**
                   * @dev Returns the downcasted int128 from int256, reverting on
                   * overflow (when the input is less than smallest int128 or
                   * greater than largest int128).
                   *
                   * Counterpart to Solidity's `int128` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 128 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt128(int256 value) internal pure returns (int128 downcasted) {
                      downcasted = int128(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
                  }
                  /**
                   * @dev Returns the downcasted int120 from int256, reverting on
                   * overflow (when the input is less than smallest int120 or
                   * greater than largest int120).
                   *
                   * Counterpart to Solidity's `int120` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 120 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt120(int256 value) internal pure returns (int120 downcasted) {
                      downcasted = int120(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
                  }
                  /**
                   * @dev Returns the downcasted int112 from int256, reverting on
                   * overflow (when the input is less than smallest int112 or
                   * greater than largest int112).
                   *
                   * Counterpart to Solidity's `int112` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 112 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt112(int256 value) internal pure returns (int112 downcasted) {
                      downcasted = int112(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
                  }
                  /**
                   * @dev Returns the downcasted int104 from int256, reverting on
                   * overflow (when the input is less than smallest int104 or
                   * greater than largest int104).
                   *
                   * Counterpart to Solidity's `int104` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 104 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt104(int256 value) internal pure returns (int104 downcasted) {
                      downcasted = int104(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
                  }
                  /**
                   * @dev Returns the downcasted int96 from int256, reverting on
                   * overflow (when the input is less than smallest int96 or
                   * greater than largest int96).
                   *
                   * Counterpart to Solidity's `int96` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 96 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt96(int256 value) internal pure returns (int96 downcasted) {
                      downcasted = int96(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
                  }
                  /**
                   * @dev Returns the downcasted int88 from int256, reverting on
                   * overflow (when the input is less than smallest int88 or
                   * greater than largest int88).
                   *
                   * Counterpart to Solidity's `int88` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 88 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt88(int256 value) internal pure returns (int88 downcasted) {
                      downcasted = int88(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
                  }
                  /**
                   * @dev Returns the downcasted int80 from int256, reverting on
                   * overflow (when the input is less than smallest int80 or
                   * greater than largest int80).
                   *
                   * Counterpart to Solidity's `int80` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 80 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt80(int256 value) internal pure returns (int80 downcasted) {
                      downcasted = int80(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
                  }
                  /**
                   * @dev Returns the downcasted int72 from int256, reverting on
                   * overflow (when the input is less than smallest int72 or
                   * greater than largest int72).
                   *
                   * Counterpart to Solidity's `int72` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 72 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt72(int256 value) internal pure returns (int72 downcasted) {
                      downcasted = int72(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
                  }
                  /**
                   * @dev Returns the downcasted int64 from int256, reverting on
                   * overflow (when the input is less than smallest int64 or
                   * greater than largest int64).
                   *
                   * Counterpart to Solidity's `int64` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 64 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt64(int256 value) internal pure returns (int64 downcasted) {
                      downcasted = int64(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
                  }
                  /**
                   * @dev Returns the downcasted int56 from int256, reverting on
                   * overflow (when the input is less than smallest int56 or
                   * greater than largest int56).
                   *
                   * Counterpart to Solidity's `int56` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 56 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt56(int256 value) internal pure returns (int56 downcasted) {
                      downcasted = int56(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
                  }
                  /**
                   * @dev Returns the downcasted int48 from int256, reverting on
                   * overflow (when the input is less than smallest int48 or
                   * greater than largest int48).
                   *
                   * Counterpart to Solidity's `int48` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 48 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt48(int256 value) internal pure returns (int48 downcasted) {
                      downcasted = int48(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
                  }
                  /**
                   * @dev Returns the downcasted int40 from int256, reverting on
                   * overflow (when the input is less than smallest int40 or
                   * greater than largest int40).
                   *
                   * Counterpart to Solidity's `int40` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 40 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt40(int256 value) internal pure returns (int40 downcasted) {
                      downcasted = int40(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
                  }
                  /**
                   * @dev Returns the downcasted int32 from int256, reverting on
                   * overflow (when the input is less than smallest int32 or
                   * greater than largest int32).
                   *
                   * Counterpart to Solidity's `int32` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 32 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt32(int256 value) internal pure returns (int32 downcasted) {
                      downcasted = int32(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
                  }
                  /**
                   * @dev Returns the downcasted int24 from int256, reverting on
                   * overflow (when the input is less than smallest int24 or
                   * greater than largest int24).
                   *
                   * Counterpart to Solidity's `int24` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 24 bits
                   *
                   * _Available since v4.7._
                   */
                  function toInt24(int256 value) internal pure returns (int24 downcasted) {
                      downcasted = int24(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
                  }
                  /**
                   * @dev Returns the downcasted int16 from int256, reverting on
                   * overflow (when the input is less than smallest int16 or
                   * greater than largest int16).
                   *
                   * Counterpart to Solidity's `int16` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 16 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt16(int256 value) internal pure returns (int16 downcasted) {
                      downcasted = int16(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
                  }
                  /**
                   * @dev Returns the downcasted int8 from int256, reverting on
                   * overflow (when the input is less than smallest int8 or
                   * greater than largest int8).
                   *
                   * Counterpart to Solidity's `int8` operator.
                   *
                   * Requirements:
                   *
                   * - input must fit into 8 bits
                   *
                   * _Available since v3.1._
                   */
                  function toInt8(int256 value) internal pure returns (int8 downcasted) {
                      downcasted = int8(value);
                      require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
                  }
                  /**
                   * @dev Converts an unsigned uint256 into a signed int256.
                   *
                   * Requirements:
                   *
                   * - input must be less than or equal to maxInt256.
                   *
                   * _Available since v3.0._
                   */
                  function toInt256(uint256 value) internal pure returns (int256) {
                      // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
                      require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
                      return int256(value);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)
              pragma solidity ^0.8.0;
              import "../../interfaces/draft-IERC1822Upgradeable.sol";
              import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
              import "./Initializable.sol";
              /**
               * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
               * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
               *
               * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
               * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
               * `UUPSUpgradeable` with a custom implementation of upgrades.
               *
               * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
               *
               * _Available since v4.1._
               */
              abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
                  function __UUPSUpgradeable_init() internal onlyInitializing {
                  }
                  function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
                  }
                  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
                  address private immutable __self = address(this);
                  /**
                   * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
                   * a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
                   * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
                   * function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
                   * fail.
                   */
                  modifier onlyProxy() {
                      require(address(this) != __self, "Function must be called through delegatecall");
                      require(_getImplementation() == __self, "Function must be called through active proxy");
                      _;
                  }
                  /**
                   * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
                   * callable on the implementing contract but not through proxies.
                   */
                  modifier notDelegated() {
                      require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
                      _;
                  }
                  /**
                   * @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
                   * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
                   */
                  function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
                      return _IMPLEMENTATION_SLOT;
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy to `newImplementation`.
                   *
                   * Calls {_authorizeUpgrade}.
                   *
                   * Emits an {Upgraded} event.
                   *
                   * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
                   */
                  function upgradeTo(address newImplementation) public virtual onlyProxy {
                      _authorizeUpgrade(newImplementation);
                      _upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
                   * encoded in `data`.
                   *
                   * Calls {_authorizeUpgrade}.
                   *
                   * Emits an {Upgraded} event.
                   *
                   * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
                   */
                  function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
                      _authorizeUpgrade(newImplementation);
                      _upgradeToAndCallUUPS(newImplementation, data, true);
                  }
                  /**
                   * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
                   * {upgradeTo} and {upgradeToAndCall}.
                   *
                   * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
                   *
                   * ```solidity
                   * function _authorizeUpgrade(address) internal override onlyOwner {}
                   * ```
                   */
                  function _authorizeUpgrade(address newImplementation) internal virtual;
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @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:
               * ```solidity
               * 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`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity ^0.8.20;
              interface IPermit2 {
                  struct TokenPermissions {
                      address token;
                      uint256 amount;
                  }
                  struct PermitTransferFrom {
                      TokenPermissions permitted;
                      uint256 nonce;
                      uint256 deadline;
                  }
                  struct SignatureTransferDetails {
                      address to;
                      uint256 requestedAmount;
                  }
                  function permitTransferFrom(
                      PermitTransferFrom memory permit,
                      SignatureTransferDetails calldata transferDetails,
                      address owner,
                      bytes calldata signature
                  ) external;
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // Inspired by OpenZeppelin Contracts (last updated v4.9.0) (utils/Multicall.sol)
              pragma solidity ^0.8.4;
              import "@openzeppelin/contracts/utils/Address.sol";
              /**
               * @dev Provides a function to batch together multiple payable calls in a single external call.
               *
               * _Available since v4.1._
               */
              abstract contract PayableMulticall {
                  /**
                   * @dev Receives and executes a batch of function calls on this contract.
                   * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
                   */
                  function multicall(bytes[] calldata data) external payable virtual returns (bytes[] memory results) {
                      results = new bytes[](data.length);
                      for (uint256 i = 0; i < data.length; i++) {
                          results[i] = Address.functionDelegateCall(address(this), data[i]);
                      }
                      return results;
                  }
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "../libraries/DataTypes.sol";
              import "../interfaces/IOrderManager.sol";
              import "../utils/SimpleSpotExecutor.sol";
              import { IPermit2 } from "../dependencies/Uniswap.sol";
              struct LinkedOrderParams {
                  uint128 limitPrice; // in quote currency
                  uint128 tolerance; // 0.003e4 = 0.3%
                  Currency cashflowCcy;
                  uint32 deadline;
                  OrderType orderType;
              }
              struct SwapData {
                  address router;
                  address spender;
                  uint256 amountIn;
                  uint256 minAmountOut;
                  bytes swapBytes;
              }
              interface IMaestroEvents {
                  event FeeCollected(
                      PositionId indexed positionId, address indexed trader, address treasury, IERC20 token, uint256 amount, uint8 basisPoints
                  );
                  event IntegrationSet(address indexed integration, bool whitelisted);
              }
              interface IMaestro is IContangoErrors, IOrderManagerErrors, IVaultErrors, IMaestroEvents {
                  error InvalidCashflow();
                  error InsufficientPermitAmount(uint256 required, uint256 actual);
                  error MismatchingPositionId(OrderId orderId1, OrderId orderId2);
                  error NotNativeToken(IERC20 token);
                  error UnknownIntegration(address integration);
                  function contango() external view returns (IContango);
                  function orderManager() external view returns (IOrderManager);
                  function vault() external view returns (IVault);
                  function positionNFT() external view returns (PositionNFT);
                  function nativeToken() external view returns (IWETH9);
                  function spotExecutor() external view returns (SimpleSpotExecutor);
                  function permit2() external view returns (IPermit2);
                  // =================== Routing management ===================
                  function route(address integration, uint256 value, bytes calldata data) external payable returns (bytes memory result);
                  function isIntegration(address integration) external view returns (bool);
                  function setIntegration(address integration, bool whitelisted) external;
                  function transferPosition(PositionId positionId, address to, bytes memory data) external payable;
                  // =================== Funding primitives ===================
                  function deposit(IERC20 token, uint256 amount) external payable returns (uint256);
                  function depositNative() external payable returns (uint256);
                  function depositWithPermit(IERC20 token, EIP2098Permit calldata permit, uint256 amount) external payable returns (uint256);
                  function depositWithPermit2(IERC20 token, EIP2098Permit calldata permit, uint256 amount) external payable returns (uint256);
                  function withdraw(IERC20 token, uint256 amount, address to) external payable returns (uint256);
                  function withdrawNative(uint256 amount, address to) external payable returns (uint256);
                  function swapAndDeposit(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData) external payable returns (uint256);
                  function swapAndDepositNative(IERC20 tokenToDeposit, SwapData calldata swapData) external payable returns (uint256);
                  function swapAndDepositWithPermit(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData, EIP2098Permit calldata permit)
                      external
                      payable
                      returns (uint256);
                  function swapAndDepositWithPermit2(IERC20 tokenToSell, IERC20 tokenToDeposit, SwapData calldata swapData, EIP2098Permit calldata permit)
                      external
                      payable
                      returns (uint256);
                  // =================== Trading actions ===================
                  function trade(TradeParams calldata tradeParams, ExecutionParams calldata execParams)
                      external
                      payable
                      returns (PositionId, Trade memory);
                  function tradeWithFees(TradeParams calldata tradeParams, ExecutionParams calldata execParams, FeeParams calldata feeParams)
                      external
                      payable
                      returns (PositionId, Trade memory);
                  function tradeAndLinkedOrder(
                      TradeParams calldata tradeParams,
                      ExecutionParams calldata execParams,
                      LinkedOrderParams memory linkedOrderParams
                  ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId);
                  function tradeAndLinkedOrderWithFees(
                      TradeParams calldata tradeParams,
                      ExecutionParams calldata execParams,
                      LinkedOrderParams memory linkedOrderParams,
                      FeeParams calldata feeParams
                  ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId);
                  function tradeAndLinkedOrders(
                      TradeParams calldata tradeParams,
                      ExecutionParams calldata execParams,
                      LinkedOrderParams memory linkedOrderParams1,
                      LinkedOrderParams memory linkedOrderParams2
                  ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId1, OrderId linkedOrderId2);
                  function tradeAndLinkedOrdersWithFees(
                      TradeParams calldata tradeParams,
                      ExecutionParams calldata execParams,
                      LinkedOrderParams memory linkedOrderParams1,
                      LinkedOrderParams memory linkedOrderParams2,
                      FeeParams calldata feeParams
                  ) external payable returns (PositionId positionId, Trade memory trade_, OrderId linkedOrderId1, OrderId linkedOrderId2);
                  function placeLinkedOrder(PositionId positionId, LinkedOrderParams memory params) external payable returns (OrderId orderId);
                  function cancel(OrderId orderId) external payable;
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import "erc7399/IERC7399.sol";
              import "../core/PositionNFT.sol";
              import "../interfaces/IContango.sol";
              import "../interfaces/IVault.sol";
              import "../libraries/DataTypes.sol";
              import "../moneymarkets/interfaces/IUnderlyingPositionFactory.sol";
              struct SwapInfo {
                  Currency inputCcy;
                  int256 input;
                  int256 output;
                  uint256 price; // in quote currency
              }
              struct Trade {
                  int256 quantity;
                  SwapInfo swap;
                  Currency cashflowCcy;
                  int256 cashflow; // negative when removing from position, positive otherwise
                  uint256 fee; // Deprecated
                  Currency feeCcy; // Deprecated
                  uint256 forwardPrice;
              }
              struct TradeParams {
                  PositionId positionId; // existing position or a new one when coded with number 0 (see ../libraries/DataTypes.sol and test/Encoder.sol)
                  int256 quantity;
                  uint256 limitPrice; // in quote currency
                  Currency cashflowCcy;
                  int256 cashflow;
              }
              struct ExecutionParams {
                  address spender;
                  address router;
                  uint256 swapAmount;
                  bytes swapBytes;
                  IERC7399 flashLoanProvider;
              }
              struct Instrument {
                  IERC20 base;
                  uint256 baseUnit; // e.g. WETH: 1e18
                  IERC20 quote;
                  uint256 quoteUnit; // e.g. USDC: 1e6
                  bool closingOnly;
              }
              interface IContangoEvents {
                  // `fee` and `feeCcy` are deprecated
                  event PositionUpserted(
                      PositionId indexed positionId,
                      address indexed owner,
                      address indexed tradedBy,
                      Currency cashflowCcy,
                      int256 cashflow,
                      int256 quantityDelta,
                      uint256 price,
                      uint256 fee,
                      Currency feeCcy
                  );
                  event ClosingOnlySet(Symbol indexed symbol, bool closingOnly);
                  event InstrumentCreated(Symbol indexed symbol, IERC20 base, IERC20 quote);
                  event MoneyMarketRegistered(MoneyMarketId indexed id, IMoneyMarket moneyMarket);
                  event PositionDonated(PositionId indexed positionId, address indexed from, address indexed to);
                  event RewardsClaimed(PositionId indexed positionId, address indexed to);
              }
              interface IContangoErrors {
                  error CashflowCcyRequired(); // 0x2bed762a
                  error ClosingOnly(); // 0x1dacbd6f
                  error InsufficientBaseOnOpen(uint256 expected, int256 actual); // 0x49cb41d9
                  error InsufficientBaseCashflow(int256 expected, int256 actual); // 0x0ef42287
                  error InstrumentAlreadyExists(Symbol symbol); // 0x6170624c
                  error InvalidInstrument(Symbol symbol); // 0x2d5bccd2
                  error NotFlashBorrowProvider(address msgSender); // 0x50459441
                  error OnlyFullClosureAllowedAfterExpiry(); // 0x62a73c9a
                  error PriceAboveLimit(uint256 limit, uint256 actual); // 0x6120c45f
                  error PriceBelowLimit(uint256 limit, uint256 actual); // 0x756cfc28
                  error UnexpectedCallback(); // 0xdab1e993
                  error InvalidCashflowCcy(); // 0x2c6ff311
                  error UnexpectedTrade(); // 0xf1a9b64c
              }
              interface IContango is IContangoEvents, IContangoErrors {
                  function trade(TradeParams calldata tradeParams, ExecutionParams calldata execParams)
                      external
                      payable
                      returns (PositionId positionId, Trade memory trade);
                  function tradeOnBehalfOf(TradeParams calldata tradeParams, ExecutionParams calldata execParams, address onBehalfOf)
                      external
                      payable
                      returns (PositionId positionId, Trade memory trade);
                  function claimRewards(PositionId positionId, address to) external;
                  function donatePosition(PositionId positionId, address to) external;
                  // ======== View ========
                  function positionFactory() external view returns (IUnderlyingPositionFactory);
                  function instrument(Symbol symbol) external view returns (Instrument memory);
                  function positionNFT() external view returns (PositionNFT);
                  function vault() external view returns (IVault);
                  function lastOwner(PositionId id) external view returns (address);
                  // ======== Admin ========
                  function createInstrument(Symbol symbol, IERC20 base, IERC20 quote) external;
                  function setClosingOnly(Symbol symbol, bool closingOnly) external;
                  function pause() external;
                  function unpause() external;
                  // ======== Callbacks ========
                  function completeClose(address initiator, address repayTo, address asset, uint256 amount, uint256 fee, bytes calldata params)
                      external
                      returns (bytes memory result);
                  function completeOpenFromFlashLoan(
                      address initiator,
                      address repayTo,
                      address asset,
                      uint256 amount,
                      uint256 fee,
                      bytes calldata params
                  ) external returns (bytes memory result);
                  function completeOpenFromFlashBorrow(IERC20 asset, uint256 amountOwed, bytes calldata params) external returns (bytes memory result);
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import "../libraries/DataTypes.sol";
              import "./IContango.sol";
              import "./IContangoOracle.sol";
              enum OrderType {
                  Limit,
                  TakeProfit,
                  StopLoss
              }
              struct OrderParams {
                  PositionId positionId;
                  int128 quantity;
                  uint128 limitPrice; // in quote currency
                  uint256 tolerance; // 0.003e4 = 0.3%
                  int128 cashflow;
                  Currency cashflowCcy;
                  uint32 deadline;
                  OrderType orderType;
              }
              struct Order {
                  address owner;
                  PositionId positionId;
                  int256 quantity;
                  uint256 limitPrice;
                  uint256 tolerance;
                  int256 cashflow;
                  Currency cashflowCcy;
                  uint256 deadline;
                  OrderType orderType;
              }
              interface IOrderManagerEvents {
                  event OrderPlaced(
                      OrderId indexed orderId,
                      PositionId indexed positionId,
                      address indexed owner,
                      int256 quantity,
                      uint256 limitPrice,
                      uint256 tolerance,
                      int256 cashflow,
                      Currency cashflowCcy,
                      uint256 deadline,
                      OrderType orderType,
                      address placedBy
                  );
                  event OrderCancelled(OrderId indexed orderId);
                  event OrderExecuted(OrderId indexed orderId, PositionId indexed positionId, uint256 keeperReward);
                  event FeeCollected(
                      OrderId indexed orderId,
                      PositionId indexed positionId,
                      address indexed trader,
                      address treasury,
                      IERC20 token,
                      uint256 amount,
                      uint8 basisPoints
                  );
                  event GasMultiplierSet(uint256 gasMultiplier);
                  event GasTipSet(uint256 gasTip);
                  event OracleSet(IContangoOracle oracle);
              }
              interface IOrderManagerErrors {
                  error AboveMaxGasMultiplier(uint64 gasMultiplier); // 0x64d79f87
                  error BelowMinGasMultiplier(uint64 gasMultiplier); // 0x7f732d9b
                  error InvalidDeadline(uint256 deadline, uint256 blockTimestamp); // 0x8848019e
                  error InvalidOrderType(OrderType orderType); // 0xf2bc1bb6
                  error InvalidPrice(uint256 forwardPrice, uint256 limitPrice); // 0xaf608abb
                  error InvalidQuantity(); // 0x524f409b
                  error InvalidTolerance(uint256 tolerance); // 0x7ca28bcf
                  error OrderDoesNotExist(OrderId orderId); // 0xbd8da02b
                  error OrderAlreadyExists(OrderId orderId); // 0x086371d3
                  error OrderExpired(OrderId orderId, uint256 deadline, uint256 blockTimestamp); // 0xc8105aba
                  error OrderInvalidated(OrderId orderId); // 0xd10aebae
                  error PositionDoesNotExist(PositionId positionId); // 0x80cc2277
              }
              interface IOrderManager is IOrderManagerEvents, IOrderManagerErrors, IContangoErrors {
                  function placeOnBehalfOf(OrderParams calldata params, address onBehalfOf) external returns (OrderId orderId);
                  function place(OrderParams calldata params) external returns (OrderId orderId);
                  function cancel(OrderId orderId) external;
                  function execute(OrderId orderId, ExecutionParams calldata execParams)
                      external
                      payable
                      returns (PositionId positionId_, Trade memory trade_, uint256 keeperReward_);
                  function executeWithFees(OrderId orderId, ExecutionParams calldata execParams, FeeParams memory feeParams)
                      external
                      payable
                      returns (PositionId positionId_, Trade memory trade_, uint256 keeperReward_);
                  // ======== View ========
                  function orders(OrderId orderId) external view returns (Order memory order);
                  function hasOrder(OrderId orderId) external view returns (bool);
                  function nativeToken() external view returns (IWETH9);
                  function positionNFT() external view returns (PositionNFT);
                  // ======== Admin ========
                  function setGasMultiplier(uint64 gasMultiplier) external;
                  function setGasTip(uint64 gasTip) external;
                  function setOracle(IContangoOracle _oracle) external;
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "../dependencies/IWETH9.sol";
              interface IVaultErrors {
                  error ZeroAmount();
                  error ZeroAddress();
                  error UnsupportedToken(IERC20 token);
                  error NotEnoughBalance(IERC20 token, uint256 balance, uint256 requested);
              }
              interface IVaultEvents {
                  event TokenSupportSet(IERC20 indexed token, bool indexed isSupported);
                  event Deposited(IERC20 indexed token, address indexed account, uint256 amount);
                  event Withdrawn(IERC20 indexed token, address indexed account, uint256 amount, address indexed to);
              }
              interface IVault is IVaultErrors, IVaultEvents {
                  function nativeToken() external view returns (IWETH9);
                  function isTokenSupported(IERC20 token) external view returns (bool);
                  function setTokenSupport(IERC20 token, bool isSupported) external;
                  function balanceOf(IERC20 token, address owner) external view returns (uint256);
                  function totalBalanceOf(IERC20 token) external view returns (uint256);
                  function deposit(IERC20 token, address account, uint256 amount) external returns (uint256);
                  function depositTo(IERC20 token, address account, uint256 amount) external returns (uint256);
                  function depositNative(address account) external payable returns (uint256);
                  function withdraw(IERC20 token, address account, uint256 amount, address to) external returns (uint256);
                  function withdrawNative(address account, uint256 amount, address to) external returns (uint256);
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.20;
              error SenderIsNotNativeToken(address msgSender, address nativeToken);
              error Unauthorised(address msgSender);
              error UnsupportedOperation();
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.20;
              import "../core/PositionNFT.sol";
              import "./DataTypes.sol";
              function validateCreatePositionPermissions(PositionNFT positionNFT, address onBehalfOf) view {
                  if (!positionNFT.isApprovedForAll(onBehalfOf, msg.sender)) revert Unauthorised(msg.sender);
              }
              function validateModifyPositionPermissions(PositionNFT positionNFT, PositionId positionId) view returns (address positionOwner) {
                  positionOwner = positionNFT.positionOwner(positionId);
                  if (positionOwner != msg.sender && !positionNFT.isApprovedForAll(positionOwner, msg.sender)) revert Unauthorised(msg.sender);
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.20;
              import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
              import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
              import "@openzeppelin/contracts/utils/Address.sol";
              import "@openzeppelin/contracts/utils/math/SafeCast.sol";
              import "../dependencies/IWETH9.sol";
              import { IPermit2 } from "../dependencies/Uniswap.sol";
              import "./DataTypes.sol";
              library ERC20Lib {
                  using Address for address payable;
                  using SafeERC20 for *;
                  using SafeCast for *;
                  bytes32 internal constant UPPER_BIT_MASK = (0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
                  error ZeroPayer();
                  error ZeroDestination();
                  function transferOutNative(IWETH9 token, address payable to, uint256 amount) internal returns (uint256 amountTransferred) {
                      if (to == address(0)) revert ZeroDestination();
                      if (amount == 0) return amount;
                      token.withdraw(amount);
                      to.sendValue(amount);
                      return amount;
                  }
                  function transferOut(IERC20 token, address payer, address to, uint256 amount) internal returns (uint256 amountTransferred) {
                      if (payer == address(0)) revert ZeroPayer();
                      if (to == address(0)) revert ZeroDestination();
                      if (payer == to || amount == 0) return amount;
                      return _transferOut(token, payer, to, amount);
                  }
                  function _transferOut(IERC20 token, address payer, address to, uint256 amount) internal returns (uint256 amountTransferred) {
                      payer == address(this) ? token.safeTransfer(to, amount) : token.safeTransferFrom(payer, to, amount);
                      return amount;
                  }
                  function transferBalance(IERC20 token, address to) internal returns (uint256 balance) {
                      balance = myBalance(token);
                      if (balance > 0) transferOut(token, address(this), to, balance);
                  }
                  function transferBalanceNative(IWETH9 token, address payable to) internal returns (uint256 balance) {
                      balance = myBalance(token);
                      if (balance > 0) transferOutNative(token, to, balance);
                  }
                  function myBalance(IERC20 token) internal view returns (uint256) {
                      return token.balanceOf(address(this));
                  }
                  function myBalanceI(IERC20 token) internal view returns (int256) {
                      return myBalance(token).toInt256();
                  }
                  function approveIfNecessary(IERC20 asset, address spender) internal {
                      if (asset.allowance(address(this), spender) == 0) asset.forceApprove(spender, type(uint256).max);
                  }
                  function unit(IERC20 token) internal view returns (uint256) {
                      return 10 ** token.decimals();
                  }
                  function infiniteApproval(IERC20 token, address addr) internal {
                      token.forceApprove(addr, type(uint256).max);
                  }
                  function applyPermit(IERC20 token, EIP2098Permit memory permit, address owner, address spender) internal {
                      // Inspired by https://github.com/Uniswap/permit2/blob/main/src/libraries/SignatureVerification.sol
                      IERC20Permit(address(token)).safePermit({
                          owner: owner,
                          spender: spender,
                          value: permit.amount,
                          deadline: permit.deadline,
                          r: permit.r,
                          v: uint8(uint256(permit.vs >> 255)) + 27,
                          s: permit.vs & UPPER_BIT_MASK
                      });
                  }
                  function pullFundsWithPermit2(IPermit2 permit2, IERC20 token, EIP2098Permit memory permit, uint256 amount, address owner, address to)
                      internal
                      returns (uint256)
                  {
                      permit2.permitTransferFrom({
                          permit: IPermit2.PermitTransferFrom({
                              permitted: IPermit2.TokenPermissions({ token: address(token), amount: permit.amount }),
                              nonce: uint256(keccak256(abi.encode(owner, token, permit.amount, permit.deadline))),
                              deadline: permit.deadline
                          }),
                          transferDetails: IPermit2.SignatureTransferDetails({ to: to, requestedAmount: amount }),
                          owner: owner,
                          signature: abi.encodePacked(permit.r, permit.vs)
                      });
                      return amount;
                  }
              }
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.20;
              import "../libraries/ERC20Lib.sol";
              interface SimpleSpotExecutorEvents {
                  event SwapExecuted(IERC20 indexed tokenToSell, IERC20 indexed tokenToBuy, uint256 amountIn, uint256 amountOut);
              }
              interface SimpleSpotExecutorErrors {
                  error InsufficientAmountOut(uint256 minExpected, uint256 actual);
              }
              contract SimpleSpotExecutor is SimpleSpotExecutorEvents, SimpleSpotExecutorErrors {
                  function executeSwap(
                      IERC20 tokenToSell,
                      IERC20 tokenToBuy,
                      address spender,
                      uint256 amountIn,
                      uint256 minAmountOut,
                      address router,
                      bytes calldata swapBytes,
                      address to
                  ) external returns (uint256 output) {
                      SafeERC20.forceApprove(tokenToSell, spender, amountIn);
                      Address.functionCall(router, swapBytes);
                      output = ERC20Lib.transferBalance(tokenToBuy, to);
                      if (output < minAmountOut) revert InsufficientAmountOut(minAmountOut, output);
                      emit SwapExecuted(tokenToSell, tokenToBuy, amountIn, output);
                  }
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.20;
              import "@openzeppelin/contracts/utils/Address.sol";
              /// @dev Router forwards calls between two contracts, so that any permissions
              /// given to the original caller are stripped from the call.
              /// This is useful when implementing generic call routing functions on contracts
              /// that might have ERC20 approvals or AccessControl authorizations.
              // Inspired by https://github.com/yieldprotocol/vault-v2/blob/master/src/Router.sol
              contract Router {
                  using Address for address;
                  /// @dev Allow users to route calls, to be used with batch
                  function route(address target, uint256 value, bytes calldata data) external payable returns (bytes memory result) {
                      return target.functionCallWithValue(data, value);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822ProxiableUpgradeable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeaconUpgradeable.sol";
              import "../../interfaces/IERC1967Upgradeable.sol";
              import "../../interfaces/draft-IERC1822Upgradeable.sol";
              import "../../utils/AddressUpgradeable.sol";
              import "../../utils/StorageSlotUpgradeable.sol";
              import "../utils/Initializable.sol";
              /**
               * @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 ERC1967UpgradeUpgradeable is Initializable, IERC1967Upgradeable {
                  function __ERC1967Upgrade_init() internal onlyInitializing {
                  }
                  function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
                  }
                  // 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 Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlotUpgradeable.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 {
                      _upgradeTo(newImplementation);
                      if (data.length > 0 || forceCall) {
                          AddressUpgradeable.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @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 Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlotUpgradeable.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");
                      StorageSlotUpgradeable.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 Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @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) {
                          AddressUpgradeable.functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
                      }
                  }
                  /**
                   * @dev This empty reserved space is put in place to allow future versions to add new
                   * variables without shifting down storage in the inheritance chain.
                   * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
                   */
                  uint256[50] private __gap;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
              pragma solidity ^0.8.2;
              import "../../utils/AddressUpgradeable.sol";
              /**
               * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
               * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
               * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
               * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
               *
               * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
               * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
               * case an upgrade adds a module that needs to be initialized.
               *
               * For example:
               *
               * [.hljs-theme-light.nopadding]
               * ```solidity
               * contract MyToken is ERC20Upgradeable {
               *     function initialize() initializer public {
               *         __ERC20_init("MyToken", "MTK");
               *     }
               * }
               *
               * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
               *     function initializeV2() reinitializer(2) public {
               *         __ERC20Permit_init("MyToken");
               *     }
               * }
               * ```
               *
               * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
               * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
               *
               * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
               * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
               *
               * [CAUTION]
               * ====
               * Avoid leaving a contract uninitialized.
               *
               * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
               * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
               * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
               *
               * [.hljs-theme-light.nopadding]
               * ```
               * /// @custom:oz-upgrades-unsafe-allow constructor
               * constructor() {
               *     _disableInitializers();
               * }
               * ```
               * ====
               */
              abstract contract Initializable {
                  /**
                   * @dev Indicates that the contract has been initialized.
                   * @custom:oz-retyped-from bool
                   */
                  uint8 private _initialized;
                  /**
                   * @dev Indicates that the contract is in the process of being initialized.
                   */
                  bool private _initializing;
                  /**
                   * @dev Triggered when the contract has been initialized or reinitialized.
                   */
                  event Initialized(uint8 version);
                  /**
                   * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
                   * `onlyInitializing` functions can be used to initialize parent contracts.
                   *
                   * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
                   * constructor.
                   *
                   * Emits an {Initialized} event.
                   */
                  modifier initializer() {
                      bool isTopLevelCall = !_initializing;
                      require(
                          (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
                          "Initializable: contract is already initialized"
                      );
                      _initialized = 1;
                      if (isTopLevelCall) {
                          _initializing = true;
                      }
                      _;
                      if (isTopLevelCall) {
                          _initializing = false;
                          emit Initialized(1);
                      }
                  }
                  /**
                   * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
                   * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
                   * used to initialize parent contracts.
                   *
                   * A reinitializer may be used after the original initialization step. This is essential to configure modules that
                   * are added through upgrades and that require initialization.
                   *
                   * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
                   * cannot be nested. If one is invoked in the context of another, execution will revert.
                   *
                   * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
                   * a contract, executing them in the right order is up to the developer or operator.
                   *
                   * WARNING: setting the version to 255 will prevent any future reinitialization.
                   *
                   * Emits an {Initialized} event.
                   */
                  modifier reinitializer(uint8 version) {
                      require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
                      _initialized = version;
                      _initializing = true;
                      _;
                      _initializing = false;
                      emit Initialized(version);
                  }
                  /**
                   * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
                   * {initializer} and {reinitializer} modifiers, directly or indirectly.
                   */
                  modifier onlyInitializing() {
                      require(_initializing, "Initializable: contract is not initializing");
                      _;
                  }
                  /**
                   * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
                   * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
                   * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
                   * through proxies.
                   *
                   * Emits an {Initialized} event the first time it is successfully executed.
                   */
                  function _disableInitializers() internal virtual {
                      require(!_initializing, "Initializable: contract is initializing");
                      if (_initialized != type(uint8).max) {
                          _initialized = type(uint8).max;
                          emit Initialized(type(uint8).max);
                      }
                  }
                  /**
                   * @dev Returns the highest version that has been initialized. See {reinitializer}.
                   */
                  function _getInitializedVersion() internal view returns (uint8) {
                      return _initialized;
                  }
                  /**
                   * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
                   */
                  function _isInitializing() internal view returns (bool) {
                      return _initializing;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC20.sol";
              /**
               * @dev Interface for the optional metadata functions from the ERC20 standard.
               *
               * _Available since v4.1._
               */
              interface IERC20Metadata is IERC20 {
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the symbol of the token.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the decimals places of the token.
                   */
                  function decimals() external view returns (uint8);
              }
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.20;
              import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "./extensions/PositionIdExt.sol";
              uint256 constant WAD = 1e18;
              uint256 constant RAY = 1e27;
              uint256 constant PERCENTAGE_UNIT = 1e4;
              uint256 constant ONE_HUNDRED_PERCENT = 1e4;
              enum Currency {
                  None,
                  Base,
                  Quote
              }
              type Symbol is bytes16;
              type Payload is bytes5;
              function payloadEquals(Payload a, Payload b) pure returns (bool) {
                  return Payload.unwrap(a) == Payload.unwrap(b);
              }
              using { payloadEquals as == } for Payload global;
              type PositionId is bytes32;
              using {
                  decode,
                  getSymbol,
                  getNumber,
                  getMoneyMarket,
                  getExpiry,
                  isPerp,
                  isExpired,
                  withNumber,
                  getFlags,
                  getPayload,
                  getPayloadNoFlags,
                  asUint,
                  positionIdEquals as ==,
                  positionIdNotEquals as !=
              } for PositionId global;
              type OrderId is bytes32;
              type MoneyMarketId is uint8;
              function mmEquals(MoneyMarketId a, MoneyMarketId b) pure returns (bool) {
                  return MoneyMarketId.unwrap(a) == MoneyMarketId.unwrap(b);
              }
              using { mmEquals as == } for MoneyMarketId global;
              type Timelock is address;
              type Operator is address;
              struct EIP2098Permit {
                  uint256 amount;
                  uint256 deadline;
                  bytes32 r;
                  bytes32 vs;
              }
              struct FeeParams {
                  IERC20 token;
                  uint256 amount;
                  uint8 basisPoints;
              }
              // SPDX-License-Identifier: CC0
              pragma solidity >=0.6.4;
              /// @dev Specification for flash lenders compatible with ERC-7399
              interface IERC7399 {
                  /// @dev The amount of currency available to be lent.
                  /// @param asset The loan currency.
                  /// @return The amount of `asset` that can be borrowed.
                  function maxFlashLoan(address asset) external view returns (uint256);
                  /// @dev The fee to be charged for a given loan.
                  /// @param asset The loan currency.
                  /// @param amount The amount of assets lent.
                  /// @return The amount of `asset` to be charged for the loan, on top of the returned principal.
                  function flashFee(address asset, uint256 amount) external view returns (uint256);
                  /// @dev Initiate a flash loan.
                  /// @param loanReceiver The address receiving the flash loan
                  /// @param asset The asset to be loaned
                  /// @param amount The amount to loaned
                  /// @param data The ABI encoded user data
                  /// @param callback The address and signature of the callback function
                  /// @return result ABI encoded result of the callback
                  function flash(
                      address loanReceiver,
                      address asset,
                      uint256 amount,
                      bytes calldata data,
                      /// @dev callback. This is a combination of the callback receiver address, and the signature of callback
                      /// function. It is encoded packed as 20 bytes + 4 bytes.
                      /// @dev the return of the callback function is not encoded in the parameter, but must be `returns (bytes
                      /// memory)` for compliance with the standard.
                      /// @param initiator The address that called this function
                      /// @param paymentReceiver The address that needs to receive the amount plus fee at the end of the callback
                      /// @param asset The asset to be loaned
                      /// @param amount The amount to loaned
                      /// @param fee The fee to be paid
                      /// @param data The ABI encoded data to be passed to the callback
                      /// @return result ABI encoded result of the callback
                      function(address, address, address, uint256, uint256, bytes memory) external returns (bytes memory) callback
                  ) external returns (bytes memory);
              }
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.20;
              import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
              import "@openzeppelin/contracts/access/AccessControl.sol";
              import "../libraries/DataTypes.sol";
              import "../libraries/Errors.sol";
              import "../libraries/Roles.sol";
              /// @title PositionNFT
              /// @notice An ERC721 NFT that represents ownership of each position created through the protocol
              /// @dev Instances can only be minted by other contango contracts
              contract PositionNFT is ERC721, AccessControl {
                  event ContangoContractSet(address indexed contractAddr, bool indexed enabled);
                  uint256 public counter = 1;
                  mapping(address contractAddr => bool enabled) public contangoContracts;
                  constructor(Timelock timelock) ERC721("Contango Position", "CTGP") {
                      // Grant the admin role to the timelock by default
                      _grantRole(DEFAULT_ADMIN_ROLE, Timelock.unwrap(timelock));
                  }
                  /// @notice creates a new position in the protocol by minting a new NFT instance
                  /// @param positionId positionId of the new position without the number component set
                  /// @param to The would be owner of the newly minted position
                  /// @return positionId_ The newly created positionId
                  function mint(PositionId positionId, address to) external onlyRole(MINTER_ROLE) returns (PositionId positionId_) {
                      positionId_ = positionId.withNumber(counter++);
                      _safeMint(to, uint256(PositionId.unwrap(positionId_)));
                  }
                  /// @notice closes a position in the protocol by burning the NFT instance
                  /// @param positionId positionId of the closed position
                  function burn(PositionId positionId) external onlyRole(MINTER_ROLE) {
                      _burn(uint256(PositionId.unwrap(positionId)));
                  }
                  function positionOwner(PositionId positionId) public view returns (address) {
                      return ownerOf(uint256(PositionId.unwrap(positionId)));
                  }
                  function exists(PositionId positionId) external view returns (bool) {
                      return _exists(uint256(PositionId.unwrap(positionId)));
                  }
                  function setContangoContract(address contractAddr, bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
                      contangoContracts[contractAddr] = enabled;
                      emit ContangoContractSet(contractAddr, enabled);
                  }
                  function isApprovedForAll(address owner, address operator) public view override returns (bool) {
                      return owner == operator || contangoContracts[operator] || super.isApprovedForAll(owner, operator);
                  }
                  /// @inheritdoc ERC721
                  function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControl, ERC721) returns (bool) {
                      return super.supportsInterface(interfaceId) || AccessControl.supportsInterface(interfaceId);
                  }
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import "./IMoneyMarket.sol";
              interface IUnderlyingPositionFactoryEvents {
                  event UnderlyingPositionCreated(address indexed account, PositionId indexed positionId);
                  event MoneyMarketRegistered(MoneyMarketId indexed mm, IMoneyMarket indexed moneyMarket);
              }
              interface IUnderlyingPositionFactory is IUnderlyingPositionFactoryEvents {
                  function registerMoneyMarket(IMoneyMarket imm) external;
                  function createUnderlyingPosition(PositionId) external returns (IMoneyMarket);
                  /// @return plain IMoneyMarket implementation without any position context
                  function moneyMarket(MoneyMarketId) external view returns (IMoneyMarket);
                  /// @return position context loaded IMoneyMarket
                  function moneyMarket(PositionId) external view returns (IMoneyMarket);
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "../libraries/DataTypes.sol";
              interface IContangoOracle {
                  function priceInNativeToken(PositionId positionId, IERC20 asset) external view returns (uint256 price_);
                  function priceInNativeToken(MoneyMarketId mmId, IERC20 asset) external view returns (uint256 price_);
                  function priceInUSD(PositionId positionId, IERC20 asset) external view returns (uint256 price_);
                  function priceInUSD(MoneyMarketId mmId, IERC20 asset) external view returns (uint256 price_);
                  function baseQuoteRate(PositionId positionId) external view returns (uint256);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.20;
              import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              interface IWETH9 is IERC20 {
                  function deposit() external payable;
                  function withdraw(uint256 wad) external;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
              pragma solidity ^0.8.0;
              import "../IERC20.sol";
              import "../extensions/IERC20Permit.sol";
              import "../../../utils/Address.sol";
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using Address for address;
                  /**
                   * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful.
                   */
                  function safeTransfer(IERC20 token, address to, uint256 value) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
                  /**
                   * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                   * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                   */
                  function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                  }
                  /**
                   * @dev Deprecated. This function has issues similar to the ones found in
                   * {IERC20-approve}, and its usage is discouraged.
                   *
                   * Whenever possible, use {safeIncreaseAllowance} and
                   * {safeDecreaseAllowance} instead.
                   */
                  function safeApprove(IERC20 token, address spender, uint256 value) internal {
                      // safeApprove should only be called when setting an initial allowance,
                      // or when resetting it to zero. To increase and decrease it, use
                      // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                      require(
                          (value == 0) || (token.allowance(address(this), spender) == 0),
                          "SafeERC20: approve from non-zero to non-zero allowance"
                      );
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                  }
                  /**
                   * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful.
                   */
                  function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                      uint256 oldAllowance = token.allowance(address(this), spender);
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
                  }
                  /**
                   * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful.
                   */
                  function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                      unchecked {
                          uint256 oldAllowance = token.allowance(address(this), spender);
                          require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
                      }
                  }
                  /**
                   * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
                   * to be set to zero before setting it to a non-zero value, such as USDT.
                   */
                  function forceApprove(IERC20 token, address spender, uint256 value) internal {
                      bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
                      if (!_callOptionalReturnBool(token, approvalCall)) {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                          _callOptionalReturn(token, approvalCall);
                      }
                  }
                  /**
                   * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
                   * Revert on invalid signature.
                   */
                  function safePermit(
                      IERC20Permit token,
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) internal {
                      uint256 nonceBefore = token.nonces(owner);
                      token.permit(owner, spender, value, deadline, v, r, s);
                      uint256 nonceAfter = token.nonces(owner);
                      require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
                  }
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
                      // the target address contains contract code and also asserts for success in the low-level call.
                      bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                      require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   *
                   * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                   */
                  function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
                      // and not revert is the subcall reverts.
                      (bool success, bytes memory returndata) = address(token).call(data);
                      return
                          success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
               * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
               *
               * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
               * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
               * need to send a transaction, and thus is not required to hold Ether at all.
               */
              interface IERC20Permit {
                  /**
                   * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
                   * given ``owner``'s signed approval.
                   *
                   * IMPORTANT: The same issues {IERC20-approve} has related to transaction
                   * ordering also apply here.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   * - `deadline` must be a timestamp in the future.
                   * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
                   * over the EIP712-formatted function arguments.
                   * - the signature must use ``owner``'s current nonce (see {nonces}).
                   *
                   * For more information on the signature format, see the
                   * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
                   * section].
                   */
                  function permit(
                      address owner,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
                  /**
                   * @dev Returns the current nonce for `owner`. This value must be
                   * included whenever a signature is generated for {permit}.
                   *
                   * Every successful call to {permit} increases ``owner``'s nonce by one. This
                   * prevents a signature from being used multiple times.
                   */
                  function nonces(address owner) external view returns (uint256);
                  /**
                   * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This is the interface that {BeaconProxy} expects of its beacon.
               */
              interface IBeaconUpgradeable {
                  /**
                   * @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);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               *
               * _Available since v4.8.3._
               */
              interface IERC1967Upgradeable {
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Emitted when the beacon is changed.
                   */
                  event BeaconUpgraded(address indexed beacon);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library AddressUpgradeable {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   *
                   * Furthermore, `isContract` will also return true if the target contract within
                   * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
                   * which only has an effect at the end of a transaction.
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
                   * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
                   *
                   * _Available since v4.8._
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      if (success) {
                          if (returndata.length == 0) {
                              // only check isContract if the call was successful and the return data is empty
                              // otherwise we already know that it was a contract
                              require(isContract(target), "Address: call to non-contract");
                          }
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason or using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          _revert(returndata, errorMessage);
                      }
                  }
                  function _revert(bytes memory returndata, string memory errorMessage) private pure {
                      // Look for revert reason and bubble it up if present
                      if (returndata.length > 0) {
                          // The easiest way to bubble the revert reason is using memory via assembly
                          /// @solidity memory-safe-assembly
                          assembly {
                              let returndata_size := mload(returndata)
                              revert(add(32, returndata), returndata_size)
                          }
                      } else {
                          revert(errorMessage);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.0;
              /**
               * @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:
               * ```solidity
               * 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`, `uint256`._
               * _Available since v4.9 for `string`, `bytes`._
               */
              library StorageSlotUpgradeable {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  struct StringSlot {
                      string value;
                  }
                  struct BytesSlot {
                      bytes value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` with member `value` located at `slot`.
                   */
                  function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
                   */
                  function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` with member `value` located at `slot`.
                   */
                  function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
                   */
                  function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
                      /// @solidity memory-safe-assembly
                      assembly {
                          r.slot := store.slot
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 amount) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                   * @dev Moves `amount` tokens from `from` to `to` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 amount) external returns (bool);
              }
              //SPDX-License-Identifier: BUSL-1.1
              pragma solidity ^0.8.20;
              import "../DataTypes.sol";
              error InvalidUInt48(uint256 n);
              error InvalidUInt32(uint256 n);
              error InvalidExpiry();
              error InvalidPositionId();
              //                                 { 5B: Payload  }
              //  16B   -      1B      -   4B   -  1B   -  4B   -  6B
              // symbol - money market - expiry - flags - empty - number
              function decode(PositionId positionId) pure returns (Symbol symbol, MoneyMarketId mm, uint32 expiry, uint256 number) {
                  bytes32 raw = PositionId.unwrap(positionId);
                  symbol = Symbol.wrap(bytes16(raw));
                  mm = MoneyMarketId.wrap(uint8(uint256(raw >> 120)));
                  expiry = (uint32(uint256(raw >> 88)));
                  number = uint48(uint256(raw));
              }
              function getSymbol(PositionId positionId) pure returns (Symbol) {
                  return Symbol.wrap(bytes16(PositionId.unwrap(positionId)));
              }
              function getNumber(PositionId positionId) pure returns (uint256) {
                  return uint48(uint256(PositionId.unwrap(positionId)));
              }
              function getMoneyMarket(PositionId positionId) pure returns (MoneyMarketId) {
                  return MoneyMarketId.wrap(uint8(uint256(PositionId.unwrap(positionId) >> 120)));
              }
              function getExpiry(PositionId positionId) pure returns (uint32) {
                  return (uint32(uint256(PositionId.unwrap(positionId) >> 88)));
              }
              function isPerp(PositionId positionId) pure returns (bool) {
                  return getExpiry(positionId) == type(uint32).max;
              }
              function isExpired(PositionId positionId) view returns (bool) {
                  return block.timestamp >= getExpiry(positionId);
              }
              function withNumber(PositionId positionId, uint256 number) pure returns (PositionId) {
                  if (uint48(number) != number) revert InvalidUInt48(number);
                  if (getNumber(positionId) != 0) revert InvalidPositionId();
                  return PositionId.wrap(bytes32(uint256(PositionId.unwrap(positionId)) + number));
              }
              function getFlags(PositionId positionId) pure returns (bytes1) {
                  return bytes1(PositionId.unwrap(positionId) << 168);
              }
              function getPayload(PositionId positionId) pure returns (Payload) {
                  return Payload.wrap(bytes5(PositionId.unwrap(positionId) << 168));
              }
              function getPayloadNoFlags(PositionId positionId) pure returns (bytes4) {
                  return bytes4(PositionId.unwrap(positionId) << 176);
              }
              function asUint(PositionId positionId) pure returns (uint256) {
                  return uint256(PositionId.unwrap(positionId));
              }
              function fromUint(uint256 n) pure returns (PositionId) {
                  return PositionId.wrap(bytes32(n));
              }
              function positionIdEquals(PositionId a, PositionId b) pure returns (bool) {
                  return PositionId.unwrap(a) == PositionId.unwrap(b);
              }
              function positionIdNotEquals(PositionId a, PositionId b) pure returns (bool) {
                  return !positionIdEquals(a, b);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/ERC721.sol)
              pragma solidity ^0.8.0;
              import "./IERC721.sol";
              import "./IERC721Receiver.sol";
              import "./extensions/IERC721Metadata.sol";
              import "../../utils/Address.sol";
              import "../../utils/Context.sol";
              import "../../utils/Strings.sol";
              import "../../utils/introspection/ERC165.sol";
              /**
               * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
               * the Metadata extension, but not including the Enumerable extension, which is available separately as
               * {ERC721Enumerable}.
               */
              contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
                  using Address for address;
                  using Strings for uint256;
                  // Token name
                  string private _name;
                  // Token symbol
                  string private _symbol;
                  // Mapping from token ID to owner address
                  mapping(uint256 => address) private _owners;
                  // Mapping owner address to token count
                  mapping(address => uint256) private _balances;
                  // Mapping from token ID to approved address
                  mapping(uint256 => address) private _tokenApprovals;
                  // Mapping from owner to operator approvals
                  mapping(address => mapping(address => bool)) private _operatorApprovals;
                  /**
                   * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
                   */
                  constructor(string memory name_, string memory symbol_) {
                      _name = name_;
                      _symbol = symbol_;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
                      return
                          interfaceId == type(IERC721).interfaceId ||
                          interfaceId == type(IERC721Metadata).interfaceId ||
                          super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev See {IERC721-balanceOf}.
                   */
                  function balanceOf(address owner) public view virtual override returns (uint256) {
                      require(owner != address(0), "ERC721: address zero is not a valid owner");
                      return _balances[owner];
                  }
                  /**
                   * @dev See {IERC721-ownerOf}.
                   */
                  function ownerOf(uint256 tokenId) public view virtual override returns (address) {
                      address owner = _ownerOf(tokenId);
                      require(owner != address(0), "ERC721: invalid token ID");
                      return owner;
                  }
                  /**
                   * @dev See {IERC721Metadata-name}.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev See {IERC721Metadata-symbol}.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev See {IERC721Metadata-tokenURI}.
                   */
                  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
                      _requireMinted(tokenId);
                      string memory baseURI = _baseURI();
                      return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
                  }
                  /**
                   * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
                   * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
                   * by default, can be overridden in child contracts.
                   */
                  function _baseURI() internal view virtual returns (string memory) {
                      return "";
                  }
                  /**
                   * @dev See {IERC721-approve}.
                   */
                  function approve(address to, uint256 tokenId) public virtual override {
                      address owner = ERC721.ownerOf(tokenId);
                      require(to != owner, "ERC721: approval to current owner");
                      require(
                          _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
                          "ERC721: approve caller is not token owner or approved for all"
                      );
                      _approve(to, tokenId);
                  }
                  /**
                   * @dev See {IERC721-getApproved}.
                   */
                  function getApproved(uint256 tokenId) public view virtual override returns (address) {
                      _requireMinted(tokenId);
                      return _tokenApprovals[tokenId];
                  }
                  /**
                   * @dev See {IERC721-setApprovalForAll}.
                   */
                  function setApprovalForAll(address operator, bool approved) public virtual override {
                      _setApprovalForAll(_msgSender(), operator, approved);
                  }
                  /**
                   * @dev See {IERC721-isApprovedForAll}.
                   */
                  function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
                      return _operatorApprovals[owner][operator];
                  }
                  /**
                   * @dev See {IERC721-transferFrom}.
                   */
                  function transferFrom(address from, address to, uint256 tokenId) public virtual override {
                      //solhint-disable-next-line max-line-length
                      require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                      _transfer(from, to, tokenId);
                  }
                  /**
                   * @dev See {IERC721-safeTransferFrom}.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
                      safeTransferFrom(from, to, tokenId, "");
                  }
                  /**
                   * @dev See {IERC721-safeTransferFrom}.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
                      require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
                      _safeTransfer(from, to, tokenId, data);
                  }
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * `data` is additional data, it has no specified format and it is sent in call to `to`.
                   *
                   * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
                   * implement alternative mechanisms to perform token transfer, such as signature-based.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual {
                      _transfer(from, to, tokenId);
                      require(_checkOnERC721Received(from, to, tokenId, data), "ERC721: transfer to non ERC721Receiver implementer");
                  }
                  /**
                   * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist
                   */
                  function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
                      return _owners[tokenId];
                  }
                  /**
                   * @dev Returns whether `tokenId` exists.
                   *
                   * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
                   *
                   * Tokens start existing when they are minted (`_mint`),
                   * and stop existing when they are burned (`_burn`).
                   */
                  function _exists(uint256 tokenId) internal view virtual returns (bool) {
                      return _ownerOf(tokenId) != address(0);
                  }
                  /**
                   * @dev Returns whether `spender` is allowed to manage `tokenId`.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
                      address owner = ERC721.ownerOf(tokenId);
                      return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender);
                  }
                  /**
                   * @dev Safely mints `tokenId` and transfers it to `to`.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must not exist.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _safeMint(address to, uint256 tokenId) internal virtual {
                      _safeMint(to, tokenId, "");
                  }
                  /**
                   * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
                   * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
                   */
                  function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual {
                      _mint(to, tokenId);
                      require(
                          _checkOnERC721Received(address(0), to, tokenId, data),
                          "ERC721: transfer to non ERC721Receiver implementer"
                      );
                  }
                  /**
                   * @dev Mints `tokenId` and transfers it to `to`.
                   *
                   * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
                   *
                   * Requirements:
                   *
                   * - `tokenId` must not exist.
                   * - `to` cannot be the zero address.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _mint(address to, uint256 tokenId) internal virtual {
                      require(to != address(0), "ERC721: mint to the zero address");
                      require(!_exists(tokenId), "ERC721: token already minted");
                      _beforeTokenTransfer(address(0), to, tokenId, 1);
                      // Check that tokenId was not minted by `_beforeTokenTransfer` hook
                      require(!_exists(tokenId), "ERC721: token already minted");
                      unchecked {
                          // Will not overflow unless all 2**256 token ids are minted to the same owner.
                          // Given that tokens are minted one by one, it is impossible in practice that
                          // this ever happens. Might change if we allow batch minting.
                          // The ERC fails to describe this case.
                          _balances[to] += 1;
                      }
                      _owners[tokenId] = to;
                      emit Transfer(address(0), to, tokenId);
                      _afterTokenTransfer(address(0), to, tokenId, 1);
                  }
                  /**
                   * @dev Destroys `tokenId`.
                   * The approval is cleared when the token is burned.
                   * This is an internal function that does not check if the sender is authorized to operate on the token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _burn(uint256 tokenId) internal virtual {
                      address owner = ERC721.ownerOf(tokenId);
                      _beforeTokenTransfer(owner, address(0), tokenId, 1);
                      // Update ownership in case tokenId was transferred by `_beforeTokenTransfer` hook
                      owner = ERC721.ownerOf(tokenId);
                      // Clear approvals
                      delete _tokenApprovals[tokenId];
                      unchecked {
                          // Cannot overflow, as that would require more tokens to be burned/transferred
                          // out than the owner initially received through minting and transferring in.
                          _balances[owner] -= 1;
                      }
                      delete _owners[tokenId];
                      emit Transfer(owner, address(0), tokenId);
                      _afterTokenTransfer(owner, address(0), tokenId, 1);
                  }
                  /**
                   * @dev Transfers `tokenId` from `from` to `to`.
                   *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   *
                   * Emits a {Transfer} event.
                   */
                  function _transfer(address from, address to, uint256 tokenId) internal virtual {
                      require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                      require(to != address(0), "ERC721: transfer to the zero address");
                      _beforeTokenTransfer(from, to, tokenId, 1);
                      // Check that tokenId was not transferred by `_beforeTokenTransfer` hook
                      require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
                      // Clear approvals from the previous owner
                      delete _tokenApprovals[tokenId];
                      unchecked {
                          // `_balances[from]` cannot overflow for the same reason as described in `_burn`:
                          // `from`'s balance is the number of token held, which is at least one before the current
                          // transfer.
                          // `_balances[to]` could overflow in the conditions described in `_mint`. That would require
                          // all 2**256 token ids to be minted, which in practice is impossible.
                          _balances[from] -= 1;
                          _balances[to] += 1;
                      }
                      _owners[tokenId] = to;
                      emit Transfer(from, to, tokenId);
                      _afterTokenTransfer(from, to, tokenId, 1);
                  }
                  /**
                   * @dev Approve `to` to operate on `tokenId`
                   *
                   * Emits an {Approval} event.
                   */
                  function _approve(address to, uint256 tokenId) internal virtual {
                      _tokenApprovals[tokenId] = to;
                      emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
                  }
                  /**
                   * @dev Approve `operator` to operate on all of `owner` tokens
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function _setApprovalForAll(address owner, address operator, bool approved) internal virtual {
                      require(owner != operator, "ERC721: approve to caller");
                      _operatorApprovals[owner][operator] = approved;
                      emit ApprovalForAll(owner, operator, approved);
                  }
                  /**
                   * @dev Reverts if the `tokenId` has not been minted yet.
                   */
                  function _requireMinted(uint256 tokenId) internal view virtual {
                      require(_exists(tokenId), "ERC721: invalid token ID");
                  }
                  /**
                   * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
                   * The call is not executed if the target address is not a contract.
                   *
                   * @param from address representing the previous owner of the given token ID
                   * @param to target address that will receive the tokens
                   * @param tokenId uint256 ID of the token to be transferred
                   * @param data bytes optional data to send along with the call
                   * @return bool whether the call correctly returned the expected magic value
                   */
                  function _checkOnERC721Received(
                      address from,
                      address to,
                      uint256 tokenId,
                      bytes memory data
                  ) private returns (bool) {
                      if (to.isContract()) {
                          try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) {
                              return retval == IERC721Receiver.onERC721Received.selector;
                          } catch (bytes memory reason) {
                              if (reason.length == 0) {
                                  revert("ERC721: transfer to non ERC721Receiver implementer");
                              } else {
                                  /// @solidity memory-safe-assembly
                                  assembly {
                                      revert(add(32, reason), mload(reason))
                                  }
                              }
                          }
                      } else {
                          return true;
                      }
                  }
                  /**
                   * @dev Hook that is called before any token transfer. This includes minting and burning. If {ERC721Consecutive} is
                   * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, ``from``'s tokens will be transferred to `to`.
                   * - When `from` is zero, the tokens will be minted for `to`.
                   * - When `to` is zero, ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   * - `batchSize` is non-zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
                  /**
                   * @dev Hook that is called after any token transfer. This includes minting and burning. If {ERC721Consecutive} is
                   * used, the hook may be called as part of a consecutive (batch) mint, as indicated by `batchSize` greater than 1.
                   *
                   * Calling conditions:
                   *
                   * - When `from` and `to` are both non-zero, ``from``'s tokens were transferred to `to`.
                   * - When `from` is zero, the tokens were minted for `to`.
                   * - When `to` is zero, ``from``'s tokens were burned.
                   * - `from` and `to` are never both zero.
                   * - `batchSize` is non-zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal virtual {}
                  /**
                   * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override.
                   *
                   * WARNING: Anyone calling this MUST ensure that the balances remain consistent with the ownership. The invariant
                   * being that for any address `a` the value returned by `balanceOf(a)` must be equal to the number of tokens such
                   * that `ownerOf(tokenId)` is `a`.
                   */
                  // solhint-disable-next-line func-name-mixedcase
                  function __unsafe_increaseBalance(address account, uint256 amount) internal {
                      _balances[account] += amount;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
              pragma solidity ^0.8.0;
              import "./IAccessControl.sol";
              import "../utils/Context.sol";
              import "../utils/Strings.sol";
              import "../utils/introspection/ERC165.sol";
              /**
               * @dev Contract module that allows children to implement role-based access
               * control mechanisms. This is a lightweight version that doesn't allow enumerating role
               * members except through off-chain means by accessing the contract event logs. Some
               * applications may benefit from on-chain enumerability, for those cases see
               * {AccessControlEnumerable}.
               *
               * Roles are referred to by their `bytes32` identifier. These should be exposed
               * in the external API and be unique. The best way to achieve this is by
               * using `public constant` hash digests:
               *
               * ```solidity
               * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
               * ```
               *
               * Roles can be used to represent a set of permissions. To restrict access to a
               * function call, use {hasRole}:
               *
               * ```solidity
               * function foo() public {
               *     require(hasRole(MY_ROLE, msg.sender));
               *     ...
               * }
               * ```
               *
               * Roles can be granted and revoked dynamically via the {grantRole} and
               * {revokeRole} functions. Each role has an associated admin role, and only
               * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
               *
               * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
               * that only accounts with this role will be able to grant or revoke other
               * roles. More complex role relationships can be created by using
               * {_setRoleAdmin}.
               *
               * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
               * grant and revoke this role. Extra precautions should be taken to secure
               * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
               * to enforce additional security measures for this role.
               */
              abstract contract AccessControl is Context, IAccessControl, ERC165 {
                  struct RoleData {
                      mapping(address => bool) members;
                      bytes32 adminRole;
                  }
                  mapping(bytes32 => RoleData) private _roles;
                  bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
                  /**
                   * @dev Modifier that checks that an account has a specific role. Reverts
                   * with a standardized message including the required role.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   *
                   * _Available since v4.1._
                   */
                  modifier onlyRole(bytes32 role) {
                      _checkRole(role);
                      _;
                  }
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
                  }
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
                      return _roles[role].members[account];
                  }
                  /**
                   * @dev Revert with a standard message if `_msgSender()` is missing `role`.
                   * Overriding this function changes the behavior of the {onlyRole} modifier.
                   *
                   * Format of the revert message is described in {_checkRole}.
                   *
                   * _Available since v4.6._
                   */
                  function _checkRole(bytes32 role) internal view virtual {
                      _checkRole(role, _msgSender());
                  }
                  /**
                   * @dev Revert with a standard message if `account` is missing `role`.
                   *
                   * The format of the revert reason is given by the following regular expression:
                   *
                   *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
                   */
                  function _checkRole(bytes32 role, address account) internal view virtual {
                      if (!hasRole(role, account)) {
                          revert(
                              string(
                                  abi.encodePacked(
                                      "AccessControl: account ",
                                      Strings.toHexString(account),
                                      " is missing role ",
                                      Strings.toHexString(uint256(role), 32)
                                  )
                              )
                          );
                      }
                  }
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
                      return _roles[role].adminRole;
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been revoked `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function renounceRole(bytes32 role, address account) public virtual override {
                      require(account == _msgSender(), "AccessControl: can only renounce roles for self");
                      _revokeRole(role, account);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event. Note that unlike {grantRole}, this function doesn't perform any
                   * checks on the calling account.
                   *
                   * May emit a {RoleGranted} event.
                   *
                   * [WARNING]
                   * ====
                   * This function should only be called from the constructor when setting
                   * up the initial roles for the system.
                   *
                   * Using this function in any other way is effectively circumventing the admin
                   * system imposed by {AccessControl}.
                   * ====
                   *
                   * NOTE: This function is deprecated in favor of {_grantRole}.
                   */
                  function _setupRole(bytes32 role, address account) internal virtual {
                      _grantRole(role, account);
                  }
                  /**
                   * @dev Sets `adminRole` as ``role``'s admin role.
                   *
                   * Emits a {RoleAdminChanged} event.
                   */
                  function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
                      bytes32 previousAdminRole = getRoleAdmin(role);
                      _roles[role].adminRole = adminRole;
                      emit RoleAdminChanged(role, previousAdminRole, adminRole);
                  }
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleGranted} event.
                   */
                  function _grantRole(bytes32 role, address account) internal virtual {
                      if (!hasRole(role, account)) {
                          _roles[role].members[account] = true;
                          emit RoleGranted(role, account, _msgSender());
                      }
                  }
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * Internal function without access restriction.
                   *
                   * May emit a {RoleRevoked} event.
                   */
                  function _revokeRole(bytes32 role, address account) internal virtual {
                      if (hasRole(role, account)) {
                          _roles[role].members[account] = false;
                          emit RoleRevoked(role, account, _msgSender());
                      }
                  }
              }
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.20;
              bytes32 constant EMERGENCY_BREAK_ROLE = keccak256("EMERGENCY_BREAK");
              bytes32 constant RESTARTER_ROLE = keccak256("RESTARTER");
              bytes32 constant OPERATOR_ROLE = keccak256("OPERATOR");
              bytes32 constant CONTANGO_ROLE = keccak256("CONTANGO");
              bytes32 constant BOT_ROLE = keccak256("BOT");
              bytes32 constant MINTER_ROLE = keccak256("MINTER");
              bytes32 constant BURNER_ROLE = keccak256("BURNER");
              bytes32 constant MODIFIER_ROLE = keccak256("MODIFIER");
              //SPDX-License-Identifier: MIT
              pragma solidity ^0.8.4;
              import { IERC20Metadata as IERC20 } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
              import { PositionId, MoneyMarketId } from "../../libraries/DataTypes.sol";
              interface IMoneyMarketEvents {
                  event Borrowed(PositionId indexed positionId, IERC20 indexed asset, uint256 amount, uint256 balanceBefore);
                  event Lent(PositionId indexed positionId, IERC20 indexed asset, uint256 amount, uint256 balanceBefore);
                  event Repaid(PositionId indexed positionId, IERC20 indexed asset, uint256 amount, uint256 balanceBefore);
                  event Withdrawn(PositionId indexed positionId, IERC20 indexed asset, uint256 amount, uint256 balanceBefore);
                  event RewardsClaimed(PositionId indexed positionId, address to);
                  event RewardsClaimed(PositionId indexed positionId, IERC20 indexed rewardsToken, address to, uint256 rewardsAmount);
                  event Retrieved(PositionId indexed positionId, IERC20 indexed token, uint256 amount);
              }
              interface IMoneyMarket is IERC165, IMoneyMarketEvents {
                  error InvalidMoneyMarketId();
                  error InvalidPositionId(PositionId positionId);
                  error TokenCantBeRetrieved(IERC20 token);
                  /// @dev indicates whether the money market requires an underlying account to be created
                  /// if true, the money market must be cloned to create an underlying position
                  /// otherwise the money market can be used directly as it know how to isolate positions
                  function NEEDS_ACCOUNT() external view returns (bool);
                  function moneyMarketId() external view returns (MoneyMarketId);
                  function initialise(PositionId positionId, IERC20 collateralAsset, IERC20 debtAsset) external;
                  function lend(PositionId positionId, IERC20 asset, uint256 amount) external returns (uint256 actualAmount);
                  function withdraw(PositionId positionId, IERC20 asset, uint256 amount, address to) external returns (uint256 actualAmount);
                  function borrow(PositionId positionId, IERC20 asset, uint256 amount, address to) external returns (uint256 actualAmount);
                  function repay(PositionId positionId, IERC20 asset, uint256 amount) external returns (uint256 actualAmount);
                  function claimRewards(PositionId positionId, IERC20 collateralAsset, IERC20 debtAsset, address to) external;
                  function collateralBalance(PositionId positionId, IERC20 asset) external returns (uint256 balance);
                  function debtBalance(PositionId positionId, IERC20 asset) external returns (uint256 balance);
                  function retrieve(PositionId positionId, IERC20 token) external returns (uint256 amount);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
              pragma solidity ^0.8.0;
              import "../../utils/introspection/IERC165.sol";
              /**
               * @dev Required interface of an ERC721 compliant contract.
               */
              interface IERC721 is IERC165 {
                  /**
                   * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
                   */
                  event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
                  /**
                   * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
                   */
                  event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
                  /**
                   * @dev Returns the number of tokens in ``owner``'s account.
                   */
                  function balanceOf(address owner) external view returns (uint256 balance);
                  /**
                   * @dev Returns the owner of the `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function ownerOf(uint256 tokenId) external view returns (address owner);
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
                  /**
                   * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
                   * are aware of the ERC721 protocol to prevent tokens from being forever locked.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must exist and be owned by `from`.
                   * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
                   * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
                   *
                   * Emits a {Transfer} event.
                   */
                  function safeTransferFrom(address from, address to, uint256 tokenId) external;
                  /**
                   * @dev Transfers `tokenId` token from `from` to `to`.
                   *
                   * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
                   * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
                   * understand this adds an external call which potentially creates a reentrancy vulnerability.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `tokenId` token must be owned by `from`.
                   * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(address from, address to, uint256 tokenId) external;
                  /**
                   * @dev Gives permission to `to` to transfer `tokenId` token to another account.
                   * The approval is cleared when the token is transferred.
                   *
                   * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
                   *
                   * Requirements:
                   *
                   * - The caller must own the token or be an approved operator.
                   * - `tokenId` must exist.
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address to, uint256 tokenId) external;
                  /**
                   * @dev Approve or remove `operator` as an operator for the caller.
                   * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
                   *
                   * Requirements:
                   *
                   * - The `operator` cannot be the caller.
                   *
                   * Emits an {ApprovalForAll} event.
                   */
                  function setApprovalForAll(address operator, bool approved) external;
                  /**
                   * @dev Returns the account approved for `tokenId` token.
                   *
                   * Requirements:
                   *
                   * - `tokenId` must exist.
                   */
                  function getApproved(uint256 tokenId) external view returns (address operator);
                  /**
                   * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
                   *
                   * See {setApprovalForAll}
                   */
                  function isApprovedForAll(address owner, address operator) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
              pragma solidity ^0.8.0;
              /**
               * @title ERC721 token receiver interface
               * @dev Interface for any contract that wants to support safeTransfers
               * from ERC721 asset contracts.
               */
              interface IERC721Receiver {
                  /**
                   * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
                   * by `operator` from `from`, this function is called.
                   *
                   * It must return its Solidity selector to confirm the token transfer.
                   * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
                   *
                   * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
                   */
                  function onERC721Received(
                      address operator,
                      address from,
                      uint256 tokenId,
                      bytes calldata data
                  ) external returns (bytes4);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
              pragma solidity ^0.8.0;
              import "../IERC721.sol";
              /**
               * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
               * @dev See https://eips.ethereum.org/EIPS/eip-721
               */
              interface IERC721Metadata is IERC721 {
                  /**
                   * @dev Returns the token collection name.
                   */
                  function name() external view returns (string memory);
                  /**
                   * @dev Returns the token collection symbol.
                   */
                  function symbol() external view returns (string memory);
                  /**
                   * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
                   */
                  function tokenURI(uint256 tokenId) external view returns (string memory);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.data;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
              pragma solidity ^0.8.0;
              import "./math/Math.sol";
              import "./math/SignedMath.sol";
              /**
               * @dev String operations.
               */
              library Strings {
                  bytes16 private constant _SYMBOLS = "0123456789abcdef";
                  uint8 private constant _ADDRESS_LENGTH = 20;
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` decimal representation.
                   */
                  function toString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          uint256 length = Math.log10(value) + 1;
                          string memory buffer = new string(length);
                          uint256 ptr;
                          /// @solidity memory-safe-assembly
                          assembly {
                              ptr := add(buffer, add(32, length))
                          }
                          while (true) {
                              ptr--;
                              /// @solidity memory-safe-assembly
                              assembly {
                                  mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                              }
                              value /= 10;
                              if (value == 0) break;
                          }
                          return buffer;
                      }
                  }
                  /**
                   * @dev Converts a `int256` to its ASCII `string` decimal representation.
                   */
                  function toString(int256 value) internal pure returns (string memory) {
                      return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
                   */
                  function toHexString(uint256 value) internal pure returns (string memory) {
                      unchecked {
                          return toHexString(value, Math.log256(value) + 1);
                      }
                  }
                  /**
                   * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
                   */
                  function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
                      bytes memory buffer = new bytes(2 * length + 2);
                      buffer[0] = "0";
                      buffer[1] = "x";
                      for (uint256 i = 2 * length + 1; i > 1; --i) {
                          buffer[i] = _SYMBOLS[value & 0xf];
                          value >>= 4;
                      }
                      require(value == 0, "Strings: hex length insufficient");
                      return string(buffer);
                  }
                  /**
                   * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
                   */
                  function toHexString(address addr) internal pure returns (string memory) {
                      return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
                  }
                  /**
                   * @dev Returns true if the two strings are equal.
                   */
                  function equal(string memory a, string memory b) internal pure returns (bool) {
                      return keccak256(bytes(a)) == keccak256(bytes(b));
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
              pragma solidity ^0.8.0;
              import "./IERC165.sol";
              /**
               * @dev Implementation of the {IERC165} interface.
               *
               * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
               * for the additional interface id that will be supported. For example:
               *
               * ```solidity
               * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
               *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
               * }
               * ```
               *
               * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
               */
              abstract contract ERC165 is IERC165 {
                  /**
                   * @dev See {IERC165-supportsInterface}.
                   */
                  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
                      return interfaceId == type(IERC165).interfaceId;
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev External interface of AccessControl declared to support ERC165 detection.
               */
              interface IAccessControl {
                  /**
                   * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
                   *
                   * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
                   * {RoleAdminChanged} not being emitted signaling this.
                   *
                   * _Available since v3.1._
                   */
                  event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
                  /**
                   * @dev Emitted when `account` is granted `role`.
                   *
                   * `sender` is the account that originated the contract call, an admin role
                   * bearer except when using {AccessControl-_setupRole}.
                   */
                  event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Emitted when `account` is revoked `role`.
                   *
                   * `sender` is the account that originated the contract call:
                   *   - if using `revokeRole`, it is the admin role bearer
                   *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
                   */
                  event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
                  /**
                   * @dev Returns `true` if `account` has been granted `role`.
                   */
                  function hasRole(bytes32 role, address account) external view returns (bool);
                  /**
                   * @dev Returns the admin role that controls `role`. See {grantRole} and
                   * {revokeRole}.
                   *
                   * To change a role's admin, use {AccessControl-_setRoleAdmin}.
                   */
                  function getRoleAdmin(bytes32 role) external view returns (bytes32);
                  /**
                   * @dev Grants `role` to `account`.
                   *
                   * If `account` had not been already granted `role`, emits a {RoleGranted}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function grantRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from `account`.
                   *
                   * If `account` had been granted `role`, emits a {RoleRevoked} event.
                   *
                   * Requirements:
                   *
                   * - the caller must have ``role``'s admin role.
                   */
                  function revokeRole(bytes32 role, address account) external;
                  /**
                   * @dev Revokes `role` from the calling account.
                   *
                   * Roles are often managed via {grantRole} and {revokeRole}: this function's
                   * purpose is to provide a mechanism for accounts to lose their privileges
                   * if they are compromised (such as when a trusted device is misplaced).
                   *
                   * If the calling account had been granted `role`, emits a {RoleRevoked}
                   * event.
                   *
                   * Requirements:
                   *
                   * - the caller must be `account`.
                   */
                  function renounceRole(bytes32 role, address account) external;
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC165 standard, as defined in the
               * https://eips.ethereum.org/EIPS/eip-165[EIP].
               *
               * Implementers can declare support of contract interfaces, which can then be
               * queried by others ({ERC165Checker}).
               *
               * For an implementation, see {ERC165}.
               */
              interface IERC165 {
                  /**
                   * @dev Returns true if this contract implements the interface defined by
                   * `interfaceId`. See the corresponding
                   * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
                   * to learn more about how these ids are created.
                   *
                   * This function call must use less than 30 000 gas.
                   */
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Standard math utilities missing in the Solidity language.
               */
              library Math {
                  enum Rounding {
                      Down, // Toward negative infinity
                      Up, // Toward infinity
                      Zero // Toward zero
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a > b ? a : b;
                  }
                  /**
                   * @dev Returns the smallest of two numbers.
                   */
                  function min(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a < b ? a : b;
                  }
                  /**
                   * @dev Returns the average of two numbers. The result is rounded towards
                   * zero.
                   */
                  function average(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b) / 2 can overflow.
                      return (a & b) + (a ^ b) / 2;
                  }
                  /**
                   * @dev Returns the ceiling of the division of two numbers.
                   *
                   * This differs from standard division with `/` in that it rounds up instead
                   * of rounding down.
                   */
                  function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
                      // (a + b - 1) / b can overflow on addition, so we distribute.
                      return a == 0 ? 0 : (a - 1) / b + 1;
                  }
                  /**
                   * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                   * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
                   * with further edits by Uniswap Labs also under MIT license.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
                      unchecked {
                          // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
                          // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
                          // variables such that product = prod1 * 2^256 + prod0.
                          uint256 prod0; // Least significant 256 bits of the product
                          uint256 prod1; // Most significant 256 bits of the product
                          assembly {
                              let mm := mulmod(x, y, not(0))
                              prod0 := mul(x, y)
                              prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                          }
                          // Handle non-overflow cases, 256 by 256 division.
                          if (prod1 == 0) {
                              // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                              // The surrounding unchecked block does not change this fact.
                              // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                              return prod0 / denominator;
                          }
                          // Make sure the result is less than 2^256. Also prevents denominator == 0.
                          require(denominator > prod1, "Math: mulDiv overflow");
                          ///////////////////////////////////////////////
                          // 512 by 256 division.
                          ///////////////////////////////////////////////
                          // Make division exact by subtracting the remainder from [prod1 prod0].
                          uint256 remainder;
                          assembly {
                              // Compute remainder using mulmod.
                              remainder := mulmod(x, y, denominator)
                              // Subtract 256 bit number from 512 bit number.
                              prod1 := sub(prod1, gt(remainder, prod0))
                              prod0 := sub(prod0, remainder)
                          }
                          // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
                          // See https://cs.stackexchange.com/q/138556/92363.
                          // Does not overflow because the denominator cannot be zero at this stage in the function.
                          uint256 twos = denominator & (~denominator + 1);
                          assembly {
                              // Divide denominator by twos.
                              denominator := div(denominator, twos)
                              // Divide [prod1 prod0] by twos.
                              prod0 := div(prod0, twos)
                              // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                              twos := add(div(sub(0, twos), twos), 1)
                          }
                          // Shift in bits from prod1 into prod0.
                          prod0 |= prod1 * twos;
                          // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
                          // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
                          // four bits. That is, denominator * inv = 1 mod 2^4.
                          uint256 inverse = (3 * denominator) ^ 2;
                          // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
                          // in modular arithmetic, doubling the correct bits in each step.
                          inverse *= 2 - denominator * inverse; // inverse mod 2^8
                          inverse *= 2 - denominator * inverse; // inverse mod 2^16
                          inverse *= 2 - denominator * inverse; // inverse mod 2^32
                          inverse *= 2 - denominator * inverse; // inverse mod 2^64
                          inverse *= 2 - denominator * inverse; // inverse mod 2^128
                          inverse *= 2 - denominator * inverse; // inverse mod 2^256
                          // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
                          // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
                          // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
                          // is no longer required.
                          result = prod0 * inverse;
                          return result;
                      }
                  }
                  /**
                   * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
                   */
                  function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
                      uint256 result = mulDiv(x, y, denominator);
                      if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
                          result += 1;
                      }
                      return result;
                  }
                  /**
                   * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
                   *
                   * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
                   */
                  function sqrt(uint256 a) internal pure returns (uint256) {
                      if (a == 0) {
                          return 0;
                      }
                      // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
                      //
                      // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
                      // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
                      //
                      // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
                      // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
                      // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
                      //
                      // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
                      uint256 result = 1 << (log2(a) >> 1);
                      // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
                      // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
                      // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
                      // into the expected uint128 result.
                      unchecked {
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          result = (result + a / result) >> 1;
                          return min(result, a / result);
                      }
                  }
                  /**
                   * @notice Calculates sqrt(a), following the selected rounding direction.
                   */
                  function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = sqrt(a);
                          return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 2, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 128;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 64;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 32;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 16;
                          }
                          if (value >> 8 > 0) {
                              value >>= 8;
                              result += 8;
                          }
                          if (value >> 4 > 0) {
                              value >>= 4;
                              result += 4;
                          }
                          if (value >> 2 > 0) {
                              value >>= 2;
                              result += 2;
                          }
                          if (value >> 1 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log2(value);
                          return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 10, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >= 10 ** 64) {
                              value /= 10 ** 64;
                              result += 64;
                          }
                          if (value >= 10 ** 32) {
                              value /= 10 ** 32;
                              result += 32;
                          }
                          if (value >= 10 ** 16) {
                              value /= 10 ** 16;
                              result += 16;
                          }
                          if (value >= 10 ** 8) {
                              value /= 10 ** 8;
                              result += 8;
                          }
                          if (value >= 10 ** 4) {
                              value /= 10 ** 4;
                              result += 4;
                          }
                          if (value >= 10 ** 2) {
                              value /= 10 ** 2;
                              result += 2;
                          }
                          if (value >= 10 ** 1) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log10(value);
                          return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
                      }
                  }
                  /**
                   * @dev Return the log in base 256, rounded down, of a positive value.
                   * Returns 0 if given 0.
                   *
                   * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
                   */
                  function log256(uint256 value) internal pure returns (uint256) {
                      uint256 result = 0;
                      unchecked {
                          if (value >> 128 > 0) {
                              value >>= 128;
                              result += 16;
                          }
                          if (value >> 64 > 0) {
                              value >>= 64;
                              result += 8;
                          }
                          if (value >> 32 > 0) {
                              value >>= 32;
                              result += 4;
                          }
                          if (value >> 16 > 0) {
                              value >>= 16;
                              result += 2;
                          }
                          if (value >> 8 > 0) {
                              result += 1;
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
                   * Returns 0 if given 0.
                   */
                  function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
                      unchecked {
                          uint256 result = log256(value);
                          return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
                      }
                  }
              }