ETH Price: $1,951.91 (+7.03%)

Transaction Decoder

Block:
23144212 at Aug-15-2025 05:01:23 AM +UTC
Transaction Fee:
0.001248066406469376 ETH $2.44
Gas Used:
3,325,376 Gas / 0.375315876 Gwei

Emitted Events:

180 TransparentUpgradeableProxy.0x50a1a2d4fcb1c08863a0b14fcc7d9d728e2b21d8d7588b9cfa3991efe8112ee7( 0x50a1a2d4fcb1c08863a0b14fcc7d9d728e2b21d8d7588b9cfa3991efe8112ee7, 0x000000000000000000000000f8094570485b124b4f2abe98909a87511489c162, 00000000000000000000000000000000000000000000000000000000689ebf23, 00000000000000000000000000000000000000000000e116a13f44551a665aaa, 0000000000000000000000000000000000000000000000000000000000000000 )
181 PENDLE.Transfer( from=0xD9553D04F7464E6930031213F3494E98550F60E7, to=0x404598500880FC15797242F1F4d67D52C1AfA07b, value=31629840994974555384 )
182 0xd9553d04f7464e6930031213f3494e98550f60e7.0x7fe1dfb5fd2a01640e1b559e082ce63e369d4d61e01dc0caec9521e55a8d1e4f( 0x7fe1dfb5fd2a01640e1b559e082ce63e369d4d61e01dc0caec9521e55a8d1e4f, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 0x000000000000000000000000808507121b80c02388fad14726482e061b8da827, 000000000000000000000000000000000000000000000001b6f3c4cd878bdcf8 )
183 PENDLE.Approval( owner=0x404598500880FC15797242F1F4d67D52C1AfA07b, spender=SimpleSwapper, value=31629840994974555384 )
184 PENDLE.Transfer( from=0x404598500880FC15797242F1F4d67D52C1AfA07b, to=SimpleSwapper, value=31629840994974555384 )
185 PENDLE.Approval( owner=0x404598500880FC15797242F1F4d67D52C1AfA07b, spender=SimpleSwapper, value=0 )
186 WETH9.Transfer( src=0x57aF956d3E2cCa3B86f3D8C6772C03ddca3eAacB, dst=SimpleSwapper, wad=36791352173115779 )
187 PENDLE.Transfer( from=SimpleSwapper, to=0x57aF956d3E2cCa3B86f3D8C6772C03ddca3eAacB, value=31629840994974555384 )
188 PENDLE.Approval( owner=SimpleSwapper, spender=0xE592427A...C05861564, value=115792089237316195423570985008687907853269984665640563978426885995917118461253 )
189 0x57af956d3e2cca3b86f3d8c6772c03ddca3eaacb.0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67( 0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67, 0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000001b6f3c4cd878bdcf8, ffffffffffffffffffffffffffffffffffffffffffffffffff7d4a75caea467d, 000000000000000000000000000000000000000008be74575977ff2f3e49aaa2, 0000000000000000000000000000000000000000000005daf35d83f3ff76743d, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef82c )
190 WETH9.Transfer( src=SimpleSwapper, dst=0x404598500880FC15797242F1F4d67D52C1AfA07b, wad=36791352173115779 )
191 SimpleSwapper.Swap( caller=0x404598500880FC15797242F1F4d67D52C1AfA07b, fromToken=PENDLE, toToken=WETH9, amountIn=31629840994974555384, amountOut=36791352173115779 )
192 TransparentUpgradeableProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0000000000000000000000000000000000000000000000b21ac43abd07f05ca4 )
193 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0000000000000000000000000000000000000000000000b21ac43abd07f05ca4 )
194 WETH9.Transfer( src=UniswapV3Pool, dst=SimpleSwapper, wad=51158776270521601 )
195 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0x00000000000000000000000014154c15fc0fd3f91de557a1b6fdd2059972cd0b, 0000000000000000000000000000000000000000000000b21ac43abd07f05ca4 )
196 UniswapV3Pool.Swap( sender=0xE592427A...C05861564, recipient=SimpleSwapper, amount0=-51158776270521601, amount1=3285449176244278025380, sqrtPriceX96=19978727409254614868600456898655, liquidity=84022791336616246739170, tick=110607 )
197 WETH9.Transfer( src=SimpleSwapper, dst=0x404598500880FC15797242F1F4d67D52C1AfA07b, wad=51158776270521601 )
198 SimpleSwapper.Swap( caller=0x404598500880FC15797242F1F4d67D52C1AfA07b, fromToken=TransparentUpgradeableProxy, toToken=WETH9, amountIn=3285449176244278025380, amountOut=51158776270521601 )
199 WETH9.Transfer( src=0x404598500880FC15797242F1F4d67D52C1AfA07b, dst=GnosisSafeProxy, wad=439750642218186 )
200 WETH9.Transfer( src=0x404598500880FC15797242F1F4d67D52C1AfA07b, dst=BeefyFeeBatchV4, wad=7475760917709177 )
201 WETH9.Transfer( src=0x404598500880FC15797242F1F4d67D52C1AfA07b, dst=0x982F264ce97365864181df65dF4931C593A515ad, wad=439750642218186 )
202 0x404598500880fc15797242f1f4d67d52c1afa07b.0xd255b592c7f268a73e534da5219a60ff911b4cf6daae21c7d20527dd657bd99a( 0xd255b592c7f268a73e534da5219a60ff911b4cf6daae21c7d20527dd657bd99a, 00000000000000000000000000000000000000000000000000018ff36cd1b4ca, 000000000000000000000000000000000000000000000000001a8f2a39ed0179, 00000000000000000000000000000000000000000000000000018ff36cd1b4ca )
203 WETH9.Approval( src=0x404598500880FC15797242F1F4d67D52C1AfA07b, guy=SimpleSwapper, wad=79594866241491831 )
204 WETH9.Transfer( src=0x404598500880FC15797242F1F4d67D52C1AfA07b, dst=SimpleSwapper, wad=79594866241491831 )
205 WETH9.Transfer( src=SimpleSwapper, dst=CurveRouter v1.0, wad=79594866241491831 )
206 WETH9.Transfer( src=CurveRouter v1.0, dst=CurveTricryptoOptimizedWETH, wad=79594866241491831 )
207 WETH9.Withdrawal( src=CurveTricryptoOptimizedWETH, wad=79594866241491831 )
208 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000007f86bf177dd4f3494b841a37e810a34dd56c829b, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 0000000000000000000000000000000000000000000000000000000015ec7e7e )
209 CurveTricryptoOptimizedWETH.TokenExchange( buyer=CurveRouter v1.0, sold_id=2, tokens_sold=79594866241491831, bought_id=0, tokens_bought=367820414, fee=210328, packed_price_scale=1562172799764057773178636610973718026490126405613406937609232 )
210 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 0x00000000000000000000000014100f81e33c33ecc7cdac70181fb45b6e78569f, 0000000000000000000000000000000000000000000000000000000015ec7e7e )
211 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000014100f81e33c33ecc7cdac70181fb45b6e78569f, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
212 0x14100f81e33c33ecc7cdac70181fb45b6e78569f.0x8b3e96f2b889fa771c53c981b40daf005f63f637f1869f707052d15a3dd97140( 0x8b3e96f2b889fa771c53c981b40daf005f63f637f1869f707052d15a3dd97140, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000015ec7e7e, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
213 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
214 CurveRouter v1.0.Exchange( sender=SimpleSwapper, receiver=SimpleSwapper, route=[0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0x14100f81e33C33Ecc7CDac70181Fb45B6E78569F, 0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000], swap_params=[[2, 0, 1, 3, 3], [1, 0, 1, 1, 2], [0, 1, 1, 1, 2], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], pools=[0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000], in_amount=79594866241491831, out_amount=368370624852378791900 )
215 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
216 SimpleSwapper.Swap( caller=0x404598500880FC15797242F1F4d67D52C1AfA07b, fromToken=WETH9, toToken=TransparentUpgradeableProxy, amountIn=79594866241491831, amountOut=368370624852378791900 )
217 TransparentUpgradeableProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
218 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
219 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
220 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 0x00000000000000000000000052453825c287ddef62d647ce51c0979d27c461f7, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
221 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000052453825c287ddef62d647ce51c0979d27c461f7, 0x00000000000000000000000035d8949372d46b7a3d5a56006ae77b215fc69bc0, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
222 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000052453825c287ddef62d647ce51c0979d27c461f7, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
223 PendleUsualUSD0PPSY.Transfer( from=0x0000000000000000000000000000000000000000, to=PendleMarketV3, value=368370624852378791900 )
224 PendleUsualUSD0PPSY.Deposit( caller=PendleRouterV4, receiver=PendleMarketV3, tokenIn=TransparentUpgradeableProxy, amountDeposited=368370624852378791900, amountSyOut=368370624852378791900 )
225 PendleYieldToken.NewInterestIndex( newIndex=1000000000000000000 )
226 PendleUsualUSD0PPSY.Transfer( from=PendleMarketV3, to=Proxy, value=10984490515131128 )
227 PendleMarketV3.UpdateImpliedRate( timestamp=1755234083, lnLastImpliedRate=63887220122018455 )
228 PendleMarketV3.Swap( caller=PendleRouterV4, receiver=PendleMarketV3, netPtOut=56666867582565482610, netSyOut=-55660434537542697052, netSyFee=13730613143913910, netSyToReserve=10984490515131128 )
229 PENDLE.Transfer( from=ERC1967Proxy, to=PendleMarketV3, value=685936775784664080 )
230 ERC1967Proxy.0xf7823f78d472190ac0f94e11854ed334dce4a2e571e5f1bf7a8aec9469891d97( 0xf7823f78d472190ac0f94e11854ed334dce4a2e571e5f1bf7a8aec9469891d97, 0x000000000000000000000000f8094570485b124b4f2abe98909a87511489c162, 0000000000000000000000000000000000000000000000000984efdd03058c10 )
231 PendleMarketV3.Transfer( from=0x0000000000000000000000000000000000000000, to=SimpleSwapper, value=186178208253021561633 )
232 PendleMarketV3.UpdateImpliedRate( timestamp=1755234083, lnLastImpliedRate=63887220122018455 )
233 PendleMarketV3.Mint( receiver=SimpleSwapper, netLpMinted=186178208253021561633, netSyUsed=312710190314836094848, netPtUsed=56666835406779074995 )
234 PendleRouterV4.0x387bf301bf673df0120e2d57e639f0e05e5e03d5336577c4cd83c1bff96e8dee( 0x387bf301bf673df0120e2d57e639f0e05e5e03d5336577c4cd83c1bff96e8dee, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0x000000000000000000000000f8094570485b124b4f2abe98909a87511489c162, 0x00000000000000000000000073a15fed60bf67631dc6cd7bc5b6e8da8190acf5, 0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc, 00000000000000000000000000000000000000000000000a17bdddfde00ca321, 000000000000000000000000000000000000000000000013f82b5b978eb82fdc )
235 PendleMarketV3.Transfer( from=SimpleSwapper, to=0x404598500880FC15797242F1F4d67D52C1AfA07b, value=186178208253021561633 )
236 SimpleSwapper.Swap( caller=0x404598500880FC15797242F1F4d67D52C1AfA07b, fromToken=TransparentUpgradeableProxy, toToken=PendleMarketV3, amountIn=368370624852378791900, amountOut=186178208253021561633 )
237 PendleMarketV3.Approval( owner=0x404598500880FC15797242F1F4d67D52C1AfA07b, spender=TransparentUpgradeableProxy, value=186178208253021561633 )
238 PendleMarketV3.Approval( owner=0x404598500880FC15797242F1F4d67D52C1AfA07b, spender=TransparentUpgradeableProxy, value=0 )
239 PendleMarketV3.Transfer( from=0x404598500880FC15797242F1F4d67D52C1AfA07b, to=TransparentUpgradeableProxy, value=186178208253021561633 )
240 0x9b4786281aa433319861f309e02ba016325193ac.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 00000000000000000000000000000000000000000000000a17bdddfde00ca321 )
241 TransparentUpgradeableProxy.0xc9bc689e1fe6f1a599d618c1d5b7a496dfd42ddd4742c79b9e31265b5bb7322b( 0xc9bc689e1fe6f1a599d618c1d5b7a496dfd42ddd4742c79b9e31265b5bb7322b, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 0x000000000000000000000000f8094570485b124b4f2abe98909a87511489c162, 0x0000000000000000000000009b4786281aa433319861f309e02ba016325193ac, 00000000000000000000000000000000000000000000000a17bdddfde00ca321, 00000000000000000000000000000000000000000000000a17bdddfde00ca321 )
242 TransparentUpgradeableProxy.0x83df459d706116d1b3f1c7b7f77cfea4c635275f5d1e1a53827f536ef48db77d( 0x83df459d706116d1b3f1c7b7f77cfea4c635275f5d1e1a53827f536ef48db77d, 0x000000000000000000000000404598500880fc15797242f1f4d67d52c1afa07b, 0x000000000000000000000000f8094570485b124b4f2abe98909a87511489c162, 00000000000000000000000000000000000000000000000a17bdddfde00ca321 )
243 0x404598500880fc15797242f1f4d67d52c1afa07b.0x4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e38426( 0x4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e38426, 00000000000000000000000000000000000000000000336898d93df5534a0416 )
244 0x404598500880fc15797242f1f4d67d52c1afa07b.0x9bc239f1724cacfb88cb1d66a2dc437467699b68a8c90d7b63110cf4b6f92410( 0x9bc239f1724cacfb88cb1d66a2dc437467699b68a8c90d7b63110cf4b6f92410, 0x000000000000000000000000f2eec1bac39306c0761c816d1d33cf7c9ad6c0fe, 00000000000000000000000000000000000000000000000a17bdddfde00ca321, 00000000000000000000000000000000000000000000336898d93df5534a0416 )
245 PENDLE.Transfer( from=0x8D46eB11FD63Cc9167C9737a5621A2B1078Fa858, to=0x3006620924D41a8246864A0B7Cff112A83A22189, value=34372617812168822681 )
246 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000052f0bbe0325097ac93e1ec85c32a950e47789ca5, 0x0000000000000000000000004d32c8ff2facc771ec7efc70d6a8468bc30c26bf, 0000000000000000000000000000000000000000000000000000000000000000 )
247 TransparentUpgradeableProxy.0xc1dd44af29fde88feb16621a1f45f37857c28330bee5113257ddf833919e9308( 0xc1dd44af29fde88feb16621a1f45f37857c28330bee5113257ddf833919e9308, 0000000000000000000000000000000000000000000000000000000000001cb5 )
248 TransparentUpgradeableProxy.0x88cad96e50aa424ad96ef701e605ebc5e04a827d85690e4b9bfcf84603ecaf67( 0x88cad96e50aa424ad96ef701e605ebc5e04a827d85690e4b9bfcf84603ecaf67, 0000000000000000000000000000000000000000000000000000000000000001, 000000000000000000000000000000000000000000045c13e184e42f8f3b51e0 )
249 TransparentUpgradeableProxy.0x7002a7132fd3afea0f6ddc3dded6468b6ea8aca4a47543fea66b4f496f53e2e9( 0x7002a7132fd3afea0f6ddc3dded6468b6ea8aca4a47543fea66b4f496f53e2e9, 0000000000000000000000000000000000000000000565df528ad6056b2df62d )
250 TransparentUpgradeableProxy.0x2c52407333436f259bf6a809fe1ea51b8fbf1cde1a7d084fe1f1091b6502a204( 0x2c52407333436f259bf6a809fe1ea51b8fbf1cde1a7d084fe1f1091b6502a204, 000000000000000000000000000000000000000000045c13e184e42f8f3b51e0 )
251 TransparentUpgradeableProxy.0x30385c845b448a36257a6a1716e6ad2e1bc2cbe333cde1e69fe849ad6511adfe( 0x30385c845b448a36257a6a1716e6ad2e1bc2cbe333cde1e69fe849ad6511adfe, 0x0000000000000000000000004d32c8ff2facc771ec7efc70d6a8468bc30c26bf, 0000000000000000000000000000000000000000000000000000000000000000 )
252 TransparentUpgradeableProxy.0x73e52f1e16a2ca14e6073adf347cbfb544c812b1c1c91161c8d631929c1ed0bd( 0x73e52f1e16a2ca14e6073adf347cbfb544c812b1c1c91161c8d631929c1ed0bd, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
253 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000052f0bbe0325097ac93e1ec85c32a950e47789ca5, 0x0000000000000000000000004d32c8ff2facc771ec7efc70d6a8468bc30c26bf, 0000000000000000000000000000000000000000000000000000000000000000 )
254 TransparentUpgradeableProxy.0xc1dd44af29fde88feb16621a1f45f37857c28330bee5113257ddf833919e9308( 0xc1dd44af29fde88feb16621a1f45f37857c28330bee5113257ddf833919e9308, 0000000000000000000000000000000000000000000000000000000000001cb5 )
255 TransparentUpgradeableProxy.0x88cad96e50aa424ad96ef701e605ebc5e04a827d85690e4b9bfcf84603ecaf67( 0x88cad96e50aa424ad96ef701e605ebc5e04a827d85690e4b9bfcf84603ecaf67, 0000000000000000000000000000000000000000000000000000000000000001, 000000000000000000000000000000000000000000045c13e184e42f8f3b51e0 )
256 TransparentUpgradeableProxy.0x7002a7132fd3afea0f6ddc3dded6468b6ea8aca4a47543fea66b4f496f53e2e9( 0x7002a7132fd3afea0f6ddc3dded6468b6ea8aca4a47543fea66b4f496f53e2e9, 0000000000000000000000000000000000000000000565df528ad6056b2df62d )
257 TransparentUpgradeableProxy.0x2c52407333436f259bf6a809fe1ea51b8fbf1cde1a7d084fe1f1091b6502a204( 0x2c52407333436f259bf6a809fe1ea51b8fbf1cde1a7d084fe1f1091b6502a204, 000000000000000000000000000000000000000000045c13e184e42f8f3b51e0 )
258 TransparentUpgradeableProxy.0x30385c845b448a36257a6a1716e6ad2e1bc2cbe333cde1e69fe849ad6511adfe( 0x30385c845b448a36257a6a1716e6ad2e1bc2cbe333cde1e69fe849ad6511adfe, 0x0000000000000000000000004d32c8ff2facc771ec7efc70d6a8468bc30c26bf, 0000000000000000000000000000000000000000000000000000000000000000 )
259 TransparentUpgradeableProxy.0x73e52f1e16a2ca14e6073adf347cbfb544c812b1c1c91161c8d631929c1ed0bd( 0x73e52f1e16a2ca14e6073adf347cbfb544c812b1c1c91161c8d631929c1ed0bd, 0x000000000000000000000000a86c1de5647d43789204b5b7804218b64d50cb56, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
260 0x8d46eb11fd63cc9167c9737a5621a2b1078fa858.0x540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e( 0x540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 0x000000000000000000000000808507121b80c02388fad14726482e061b8da827, 000000000000000000000000000000000000000000000001dd0411c946964f99 )
261 PENDLE.Approval( owner=0x3006620924D41a8246864A0B7Cff112A83A22189, spender=SimpleSwapper, value=34372617812168822681 )
262 PENDLE.Transfer( from=0x3006620924D41a8246864A0B7Cff112A83A22189, to=SimpleSwapper, value=34372617812168822681 )
263 PENDLE.Approval( owner=0x3006620924D41a8246864A0B7Cff112A83A22189, spender=SimpleSwapper, value=0 )
264 WETH9.Transfer( src=0x57aF956d3E2cCa3B86f3D8C6772C03ddca3eAacB, dst=SimpleSwapper, wad=39978458819986370 )
265 PENDLE.Transfer( from=SimpleSwapper, to=0x57aF956d3E2cCa3B86f3D8C6772C03ddca3eAacB, value=34372617812168822681 )
266 PENDLE.Approval( owner=SimpleSwapper, spender=0xE592427A...C05861564, value=115792089237316195423570985008687907853269984665640563978392513378104949638572 )
267 0x57af956d3e2cca3b86f3d8c6772c03ddca3eaacb.0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67( 0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67, 0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000001dd0411c946964f99, ffffffffffffffffffffffffffffffffffffffffffffffffff71f7cdb35dfc3e, 000000000000000000000000000000000000000008be5c158821185c7b560731, 0000000000000000000000000000000000000000000005daf35d83f3ff76743d, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef82b )
268 WETH9.Transfer( src=SimpleSwapper, dst=0x3006620924D41a8246864A0B7Cff112A83A22189, wad=39978458819986370 )
269 SimpleSwapper.Swap( caller=0x3006620924D41a8246864A0B7Cff112A83A22189, fromToken=PENDLE, toToken=WETH9, amountIn=34372617812168822681, amountOut=39978458819986370 )
270 TransparentUpgradeableProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0000000000000000000000000000000000000000000000c1f64cdf56c298328c )
271 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0000000000000000000000000000000000000000000000c1f64cdf56c298328c )
272 WETH9.Transfer( src=UniswapV3Pool, dst=SimpleSwapper, wad=55695838051606551 )
273 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0x00000000000000000000000014154c15fc0fd3f91de557a1b6fdd2059972cd0b, 0000000000000000000000000000000000000000000000c1f64cdf56c298328c )
274 UniswapV3Pool.Swap( sender=0xE592427A...C05861564, recipient=SimpleSwapper, amount0=-55695838051606551, amount1=3577969412021228679820, sqrtPriceX96=19982067469471156584546108598419, liquidity=84022791336616246739170, tick=110610 )
275 WETH9.Transfer( src=SimpleSwapper, dst=0x3006620924D41a8246864A0B7Cff112A83A22189, wad=55695838051606551 )
276 SimpleSwapper.Swap( caller=0x3006620924D41a8246864A0B7Cff112A83A22189, fromToken=TransparentUpgradeableProxy, toToken=WETH9, amountIn=3577969412021228679820, amountOut=55695838051606551 )
277 WETH9.Transfer( src=0x3006620924D41a8246864A0B7Cff112A83A22189, dst=GnosisSafeProxy, wad=478371484357964 )
278 WETH9.Transfer( src=0x3006620924D41a8246864A0B7Cff112A83A22189, dst=BeefyFeeBatchV4, wad=8132315234085397 )
279 WETH9.Transfer( src=0x3006620924D41a8246864A0B7Cff112A83A22189, dst=0x982F264ce97365864181df65dF4931C593A515ad, wad=478371484357964 )
280 0x3006620924d41a8246864a0b7cff112a83a22189.0xd255b592c7f268a73e534da5219a60ff911b4cf6daae21c7d20527dd657bd99a( 0xd255b592c7f268a73e534da5219a60ff911b4cf6daae21c7d20527dd657bd99a, 0000000000000000000000000000000000000000000000000001b3138a65554c, 000000000000000000000000000000000000000000000000001ce44c30baaa15, 0000000000000000000000000000000000000000000000000001b3138a65554c )
281 WETH9.Approval( src=0x3006620924D41a8246864A0B7Cff112A83A22189, guy=SimpleSwapper, wad=86585238668791596 )
282 WETH9.Transfer( src=0x3006620924D41a8246864A0B7Cff112A83A22189, dst=SimpleSwapper, wad=86585238668791596 )
283 WETH9.Transfer( src=SimpleSwapper, dst=CurveRouter v1.0, wad=86585238668791596 )
284 WETH9.Transfer( src=CurveRouter v1.0, dst=CurveTricryptoOptimizedWETH, wad=86585238668791596 )
285 WETH9.Withdrawal( src=CurveTricryptoOptimizedWETH, wad=86585238668791596 )
286 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000007f86bf177dd4f3494b841a37e810a34dd56c829b, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 0000000000000000000000000000000000000000000000000000000017d8c051 )
287 CurveTricryptoOptimizedWETH.TokenExchange( buyer=CurveRouter v1.0, sold_id=2, tokens_sold=86585238668791596, bought_id=0, tokens_bought=400080977, fee=227369, packed_price_scale=1562172799764057773178636610973718026490126405613406937609232 )
288 FiatTokenProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 0x00000000000000000000000014100f81e33c33ecc7cdac70181fb45b6e78569f, 0000000000000000000000000000000000000000000000000000000017d8c051 )
289 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000014100f81e33c33ecc7cdac70181fb45b6e78569f, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
290 0x14100f81e33c33ecc7cdac70181fb45b6e78569f.0x8b3e96f2b889fa771c53c981b40daf005f63f637f1869f707052d15a3dd97140( 0x8b3e96f2b889fa771c53c981b40daf005f63f637f1869f707052d15a3dd97140, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000017d8c051, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
291 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000f0d4c12a5768d806021f80a262b4d39d26c58b8d, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
292 CurveRouter v1.0.Exchange( sender=SimpleSwapper, receiver=SimpleSwapper, route=[0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, 0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0x14100f81e33C33Ecc7CDac70181Fb45B6E78569F, 0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000], swap_params=[[2, 0, 1, 3, 3], [1, 0, 1, 1, 2], [0, 1, 1, 1, 2], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]], pools=[0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000], in_amount=86585238668791596, out_amount=400679262540966793697 )
293 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
294 SimpleSwapper.Swap( caller=0x3006620924D41a8246864A0B7Cff112A83A22189, fromToken=WETH9, toToken=TransparentUpgradeableProxy, amountIn=86585238668791596, amountOut=400679262540966793697 )
295 TransparentUpgradeableProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
296 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
297 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
298 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946, 0x00000000000000000000000052453825c287ddef62d647ce51c0979d27c461f7, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
299 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x00000000000000000000000052453825c287ddef62d647ce51c0979d27c461f7, 0x00000000000000000000000035d8949372d46b7a3d5a56006ae77b215fc69bc0, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
300 TransparentUpgradeableProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000052453825c287ddef62d647ce51c0979d27c461f7, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
301 PendleUsualUSD0PPSY.Transfer( from=0x0000000000000000000000000000000000000000, to=PendleMarketV3, value=400679262540966793697 )
302 PendleUsualUSD0PPSY.Deposit( caller=PendleRouterV4, receiver=PendleMarketV3, tokenIn=TransparentUpgradeableProxy, amountDeposited=400679262540966793697, amountSyOut=400679262540966793697 )
303 PendleUsualUSD0PPSY.Transfer( from=PendleMarketV3, to=Proxy, value=11947187083506340 )
304 PendleMarketV3.UpdateImpliedRate( timestamp=1755234083, lnLastImpliedRate=63884743124332680 )
305 PendleMarketV3.Swap( caller=PendleRouterV4, receiver=PendleMarketV3, netPtOut=61633190046646703039, netSyOut=-60538595181384430129, netSyFee=14933983854382925, netSyToReserve=11947187083506340 )
306 PendleMarketV3.Transfer( from=0x0000000000000000000000000000000000000000, to=SimpleSwapper, value=202507104425349468418 )
307 PendleMarketV3.UpdateImpliedRate( timestamp=1755234083, lnLastImpliedRate=63884743124332680 )
308 PendleMarketV3.Mint( receiver=SimpleSwapper, netLpMinted=202507104425349468418, netSyUsed=340140667359582363568, netPtUsed=61632767914975557306 )
309 PendleRouterV4.0x387bf301bf673df0120e2d57e639f0e05e5e03d5336577c4cd83c1bff96e8dee( 0x387bf301bf673df0120e2d57e639f0e05e5e03d5336577c4cd83c1bff96e8dee, 0x0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 0x000000000000000000000000f8094570485b124b4f2abe98909a87511489c162, 0x00000000000000000000000073a15fed60bf67631dc6cd7bc5b6e8da8190acf5, 0000000000000000000000008d6ce71ab8c98299c1956247ca9aaec080dd2df3, 000000000000000000000000000000000000000000000015b88ab2642bce11e1, 00000000000000000000000000000000000000000000000afa59c28bd5e3d102, 000000000000000000000000000000000000000000000015b88ab2642bce11e1 )
310 PendleMarketV3.Transfer( from=SimpleSwapper, to=0x3006620924D41a8246864A0B7Cff112A83A22189, value=202507104425349468418 )
311 SimpleSwapper.Swap( caller=0x3006620924D41a8246864A0B7Cff112A83A22189, fromToken=TransparentUpgradeableProxy, toToken=PendleMarketV3, amountIn=400679262540966793697, amountOut=202507104425349468418 )
312 PendleMarketV3.Approval( owner=0x3006620924D41a8246864A0B7Cff112A83A22189, spender=TransparentUpgradeableProxy, value=202507104425349468418 )
313 PENDLE.Transfer( from=PendleMarketV3, to=TransparentUpgradeableProxy, value=1840718028831195165 )
314 PendleMarketV3.RedeemRewards( user=TransparentUpgradeableProxy, rewardsOut=[1840718028831195165] )
315 PENDLE.Transfer( from=TransparentUpgradeableProxy, to=TransparentUpgradeableProxy, value=1840718028831195165 )
316 TransparentUpgradeableProxy.0xc53cb8bc1a7200a84d0b66a538905a245c4915aace7f1ce5dc4a0ba107ebc15c( 0xc53cb8bc1a7200a84d0b66a538905a245c4915aace7f1ce5dc4a0ba107ebc15c, 000000000000000000000000f8094570485b124b4f2abe98909a87511489c162, 0000000000000000000000000000000000000000000000000000000000000060, 00000000000000000000000000000000000000000000000000000000000000a0, 0000000000000000000000000000000000000000000000000000000000000001, 000000000000000000000000808507121b80c02388fad14726482e061b8da827, 0000000000000000000000000000000000000000000000000000000000000001, 000000000000000000000000000000000000000000000000198b8b4b7024001d )
317 TransparentUpgradeableProxy.0x24b5efa61dd1cfc659205a97fb8ed868f3cb8c81922bab2b96423e5de1de2cb7( 0x24b5efa61dd1cfc659205a97fb8ed868f3cb8c81922bab2b96423e5de1de2cb7, 0x000000000000000000000000808507121b80c02388fad14726482e061b8da827, 00000000000000000000000000000000000000000000000000000000000000d1, 000000000000000000000000000000000000000000000000198b8b4b7024001d )
318 PENDLE.Transfer( from=TransparentUpgradeableProxy, to=0xE21d3527B15F549Bd4ABB0f1008b02Ca25f0A8bc, value=46017950720779879 )
319 TransparentUpgradeableProxy.0x824a097e8d4e9580223eddbe4bf5cfd647cc4d997b60a0371177169fd3302c2d( 0x824a097e8d4e9580223eddbe4bf5cfd647cc4d997b60a0371177169fd3302c2d, 0x000000000000000000000000808507121b80c02388fad14726482e061b8da827, 00000000000000000000000000000000000000000000000000000000000000d1, 00000000000000000000000000000000000000000000000000a37d151600e667 )
320 PENDLE.Transfer( from=TransparentUpgradeableProxy, to=0x8D46eB11FD63Cc9167C9737a5621A2B1078Fa858, value=1426556472344176254 )
321 PENDLE.Approval( owner=TransparentUpgradeableProxy, spender=0x8D46eB11FD63Cc9167C9737a5621A2B1078Fa858, value=115792089237316195423570985008687907853269984665640564037543475531936864727139 )
322 0x8d46eb11fd63cc9167c9737a5621a2b1078fa858.0xac24935fd910bc682b5ccb1a07b718cadf8cf2f6d1404c4f3ddc3662dae40e29( 0xac24935fd910bc682b5ccb1a07b718cadf8cf2f6d1404c4f3ddc3662dae40e29, 0x000000000000000000000000808507121b80c02388fad14726482e061b8da827, 00000000000000000000000000000000000000000000000c28357372b28afd42 )
323 PENDLE.Transfer( from=TransparentUpgradeableProxy, to=0x2107BB3aD89A6670618Ac4FfD38B3B4f7b7978A8, value=138053852162339637 )
324 PENDLE.Transfer( from=TransparentUpgradeableProxy, to=0xD868a2D82Aceb578c5CD92631CcEC251a470B7bf, value=230089753603899395 )
325 PendleMarketV3.Approval( owner=0x3006620924D41a8246864A0B7Cff112A83A22189, spender=TransparentUpgradeableProxy, value=0 )
326 PendleMarketV3.Transfer( from=0x3006620924D41a8246864A0B7Cff112A83A22189, to=TransparentUpgradeableProxy, value=202507104425349468418 )
327 0x4dc7f0942d6f8620cd8d63ee99aca15ddca4c7a1.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000004d32c8ff2facc771ec7efc70d6a8468bc30c26bf, 00000000000000000000000000000000000000000000000afa59c28bd5e3d102 )
328 0x4dc7f0942d6f8620cd8d63ee99aca15ddca4c7a1.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000004d32c8ff2facc771ec7efc70d6a8468bc30c26bf, 0x0000000000000000000000008d46eb11fd63cc9167c9737a5621a2b1078fa858, 00000000000000000000000000000000000000000000000afa59c28bd5e3d102 )
329 0x8d46eb11fd63cc9167c9737a5621a2b1078fa858.0x9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d( 0x9e71bc8eea02a63969f509818f2dafb9254532904319f9dbda79b67bd34a5f3d, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 00000000000000000000000000000000000000000000000afa59c28bd5e3d102 )
330 TransparentUpgradeableProxy.0x73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca( 0x73a19dd210f1a7f902193214c0ee91dd35ee5b4d920cba8d519eca65a7b488ca, 0x0000000000000000000000003006620924d41a8246864a0b7cff112a83a22189, 0x00000000000000000000000000000000000000000000000000000000000000d1, 00000000000000000000000000000000000000000000000afa59c28bd5e3d102 )
331 0x3006620924d41a8246864a0b7cff112a83a22189.0x4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e38426( 0x4d6ce1e535dbade1c23defba91e23b8f791ce5edc0cc320257a2b364e4e38426, 0000000000000000000000000000000000000000000035a80d51d029768c106e )
332 0x3006620924d41a8246864a0b7cff112a83a22189.0x9bc239f1724cacfb88cb1d66a2dc437467699b68a8c90d7b63110cf4b6f92410( 0x9bc239f1724cacfb88cb1d66a2dc437467699b68a8c90d7b63110cf4b6f92410, 0x000000000000000000000000f2eec1bac39306c0761c816d1d33cf7c9ad6c0fe, 00000000000000000000000000000000000000000000000afa59c28bd5e3d102, 0000000000000000000000000000000000000000000035a80d51d029768c106e )
333 BeefyMultiStratHarvester.Harvest( 0xba9c6e28f44549b9a2c4c3c0d871188cb26e0e1662676a7146ed6cf5d5d70ef1, 0xb94a65c2620fd4d07ec6164d84aa7e69c0cf82a5a77a837dafecacf3a10db1e6, 00000000000000000000000000000000000000000000000000000000689ebf23 )
334 0x035066d430e5ca34e7bdb0756fce82186cafe1f0.0x8f8f4d49bbb03ffac818a5d588ec1786a4d2d17269871cbf5b1745f58b64c15d( 0x8f8f4d49bbb03ffac818a5d588ec1786a4d2d17269871cbf5b1745f58b64c15d, 0x000000000000000000000000f2eec1bac39306c0761c816d1d33cf7c9ad6c0fe, 0000000000000000000000000000000000000000000000000000000000000060, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000140, 00000000000000000000000000000000000000000000000000000000000000a4, 85ce71a300000000000000000000000000000000000000000000000000000000, 0000004000000000000000000000000000000000000000000000000000000000, 0000000200000000000000000000000000000000000000000000000000000000, 00000040000000000000000000000000404598500880fc15797242f1f4d67d52, c1afa07b0000000000000000000000003006620924d41a8246864a0b7cff112a, 83a2218900000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
335 EIP173Proxy.0x6bc93adab97dd835bee818087939b726558bc8d9177650a0018dd05eb00e56c8( 0x6bc93adab97dd835bee818087939b726558bc8d9177650a0018dd05eb00e56c8, e919ca3bd22d6779ed2a329a436dc428644396913586d006688acf0361942d9f, 46b34882591f8af76938838dc9e66c73c9fd7e0245693aab0d7a746279034914, 0000000000000000000000000000000000000000000000000000000000000001 )
336 Gelato.0xc6cfec28363edf86fe132edcedb30ccc6dbb89a7ec5f428aaeeb8ae5d901537d( 0xc6cfec28363edf86fe132edcedb30ccc6dbb89a7ec5f428aaeeb8ae5d901537d, 46b34882591f8af76938838dc9e66c73c9fd7e0245693aab0d7a746279034914, 0000000000000000000000002a6c106ae13b558bb9e2ec64bd2f1f7beff3a5e0, 000000000000000000000000df8f2aeea963803140df7b4ddd11216e584577b4, 000000000000000000000000c4e7263dd870a29f1cfe438d1a7db48547b16888 )

Account State Difference:

  Address   Before After State Difference Code
0x14100f81...B6E78569F
0x14154C15...59972Cd0B
(Uniswap V3: USUAL 2)
0x16296859...2dF6347d0
(Penpie: Master)
0x30066209...A83A22189
0x35D89493...15fc69bC0
0x3CACa7b4...9180E83b6
0x40459850...2C1AfA07b
0x47D74516...6Fcc24e57
0x4dc7f094...dDcA4c7A1
0x52453825...D27c461f7
0x57aF956d...dca3eAacB
(Uniswap V3: PENDLE)
0x73A15FeD...a8190aCF5
0x7F86Bf17...dD56c829B 1,599.307435743936408753 Eth1,599.47361584884669218 Eth0.166180104910283427
0x80850712...61B8da827
0x8D46eB11...1078Fa858
0x9b478628...6325193ac
0xA0b86991...E3606eB48
0xB4cD5C54...6b97B2ba5
0xC02aaA39...83C756Cc2 2,251,239.839494572272596551 Eth2,251,239.673314467362313124 Eth0.166180104910283427
0xC4441c2B...1Ea0DE38E
0xC4E7263D...547B16888
(Bundler: 0xc4e...888)
4.675911661812210173 Eth
Nonce: 50449
4.674663595405740797 Eth
Nonce: 50450
0.001248066406469376
0xD9553D04...8550F60E7
(BuilderNet)
118.391187599552909774 Eth118.391334140642924174 Eth0.0001465410900144
0xF8094570...11489C162

Execution Trace

Gelato.09c56431( )
  • 0x1c410b318a26c0d6223e8f79ce8c5fd099c529d4.09c56431( )
    • Null: 0x000...001.f67ae6ce( )
    • EIP173Proxy.80381407( )
      • Automate.execBypassModule( _taskCreator=0x96e9886A56726873CDC6Fc20FCDf806722F408d2, _execAddress=0x035066D430e5ca34e7BdB0756FCe82186CAfe1F0, _taskId=E919CA3BD22D6779ED2A329A436DC428644396913586D006688ACF0361942D9F, _correlationId=46B34882591F8AF76938838DC9E66C73C9FD7E0245693AAB0D7A746279034914, _execData=0x54132D78000000000000000000000000F2EEC1BAC39306C0761C816D1D33CF7C9AD6C0FE0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000A485CE71A3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000404598500880FC15797242F1F4D67D52C1AFA07B0000000000000000000000003006620924D41A8246864A0B7CFF112A83A2218900000000000000000000000000000000000000000000000000000000, _revertOnFailure=True, _singleExec=False )
        • 0x035066d430e5ca34e7bdb0756fce82186cafe1f0.54132d78( )
          • OpsProxy.executeCall( _target=0xf2EeC1baC39306C0761c816d1D33cF7C9Ad6C0Fe, _data=0x85CE71A3000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000404598500880FC15797242F1F4D67D52C1AFA07B0000000000000000000000003006620924D41A8246864A0B7CFF112A83A22189, _value=0 )
            • BeefyMultiStratHarvester.harvestMultiple( _data=0x000000000000000000000000404598500880FC15797242F1F4D67D52C1AFA07B0000000000000000000000003006620924D41A8246864A0B7CFF112A83A22189, num=2 )
              File 1 of 30: Gelato
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.0;
              import {LibDiamond} from "./libraries/standard/LibDiamond.sol";
              import {IDiamondLoupe} from "./interfaces/standard/IDiamondLoupe.sol";
              import {IDiamondCut} from "./interfaces/standard/IDiamondCut.sol";
              import {IERC173} from "./interfaces/standard/IERC173.sol";
              import {IERC165} from "./interfaces/standard/IERC165.sol";
              contract Gelato {
                  // more arguments are added to this struct
                  // this avoids stack too deep errors
                  struct DiamondArgs {
                      address owner;
                  }
                  constructor(
                      IDiamondCut.FacetCut[] memory _diamondCut,
                      DiamondArgs memory _args
                  ) payable {
                      LibDiamond.diamondCut(_diamondCut, address(0), new bytes(0));
                      LibDiamond.setContractOwner(_args.owner);
                      LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage();
                      // adding ERC165 data
                      ds.supportedInterfaces[type(IERC165).interfaceId] = true;
                      ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true;
                      ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true;
                      ds.supportedInterfaces[type(IERC173).interfaceId] = true;
                  }
                  // Find facet for function that is called and execute the
                  // function if a facet is found and return any value.
                  // solhint-disable-next-line no-complex-fallback
                  fallback() external payable {
                      LibDiamond.DiamondStorage storage ds;
                      bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
                      assembly {
                          ds.slot := position
                      }
                      address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
                      require(facet != address(0), "Gelato: Function does not exist");
                      assembly {
                          calldatacopy(0, 0, calldatasize())
                          let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
                          returndatacopy(0, 0, returndatasize())
                          switch result
                              case 0 {
                                  revert(0, returndatasize())
                              }
                              default {
                                  return(0, returndatasize())
                              }
                      }
                  }
                  // solhint-disable-next-line no-empty-blocks, ordering
                  receive() external payable {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.0;
              /******************************************************************************\\
              * Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
              * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
              /******************************************************************************/
              interface IDiamondCut {
                  enum FacetCutAction {Add, Replace, Remove}
                  // Add=0, Replace=1, Remove=2
                  struct FacetCut {
                      address facetAddress;
                      FacetCutAction action;
                      bytes4[] functionSelectors;
                  }
                  event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
                  /// @notice Add/replace/remove any number of functions and optionally execute
                  ///         a function with delegatecall
                  /// @param _diamondCut Contains the facet addresses and function selectors
                  /// @param _init The address of the contract or facet to execute _calldata
                  /// @param _calldata A function call, including function selector and arguments
                  ///                  _calldata is executed with delegatecall on _init
                  function diamondCut(
                      FacetCut[] calldata _diamondCut,
                      address _init,
                      bytes calldata _calldata
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.0;
              /******************************************************************************\\
              * Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
              * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
              /******************************************************************************/
              // A loupe is a small magnifying glass used to look at diamonds.
              // These functions look at diamonds
              interface IDiamondLoupe {
                  /// These functions are expected to be called frequently
                  /// by tools.
                  struct Facet {
                      address facetAddress;
                      bytes4[] functionSelectors;
                  }
                  /// @notice Gets all facet addresses and their four byte function selectors.
                  /// @return facets_ Facet
                  function facets() external view returns (Facet[] memory facets_);
                  /// @notice Gets all the function selectors supported by a specific facet.
                  /// @param _facet The facet address.
                  /// @return facetFunctionSelectors_
                  function facetFunctionSelectors(address _facet)
                      external
                      view
                      returns (bytes4[] memory facetFunctionSelectors_);
                  /// @notice Get all the facet addresses used by a diamond.
                  /// @return facetAddresses_
                  function facetAddresses()
                      external
                      view
                      returns (address[] memory facetAddresses_);
                  /// @notice Gets the facet that supports the given selector.
                  /// @dev If facet is not found return address(0).
                  /// @param _functionSelector The function selector.
                  /// @return facetAddress_ The facet address.
                  function facetAddress(bytes4 _functionSelector)
                      external
                      view
                      returns (address facetAddress_);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.0;
              interface IERC165 {
                  /// @notice Query if a contract implements an interface
                  /// @param interfaceId The interface identifier, as specified in ERC-165
                  /// @dev Interface identification is specified in ERC-165. This function
                  ///  uses less than 30,000 gas.
                  /// @return `true` if the contract implements `interfaceID` and
                  ///  `interfaceID` is not 0xffffffff, `false` otherwise
                  function supportsInterface(bytes4 interfaceId) external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.0;
              /// @title ERC-173 Contract Ownership Standard
              ///  Note: the ERC-165 identifier for this interface is 0x7f5828d0
              /* is ERC165 */
              interface IERC173 {
                  /// @dev This emits when ownership of a contract changes.
                  event OwnershipTransferred(
                      address indexed previousOwner,
                      address indexed newOwner
                  );
                  /// @notice Set the address of the new owner of the contract
                  /// @dev Set _newOwner to address(0) to renounce any ownership.
                  /// @param _newOwner The address of the new owner of the contract
                  function transferOwnership(address _newOwner) external;
                  /// @notice Get the address of the owner
                  /// @return owner_ The address of the owner.
                  function owner() external view returns (address owner_);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.0;
              // https://github.com/mudgen/diamond-3/blob/b009cd08b7822bad727bbcc47aa1b50d8b50f7f0/contracts/libraries/LibDiamond.sol#L1
              /******************************************************************************\\
              * Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
              * EIP-2535 Diamond Standard: https://eips.ethereum.org/EIPS/eip-2535
              /******************************************************************************/
              import "../../interfaces/standard/IDiamondCut.sol";
              // Custom due to incorrect string casting (non UTF-8 formatted)
              import {GelatoBytes} from "../../../../lib/GelatoBytes.sol";
              library LibDiamond {
                  bytes32 constant DIAMOND_STORAGE_POSITION =
                      keccak256("diamond.standard.diamond.storage");
                  struct FacetAddressAndPosition {
                      address facetAddress;
                      uint16 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
                  }
                  struct FacetFunctionSelectors {
                      bytes4[] functionSelectors;
                      uint16 facetAddressPosition; // position of facetAddress in facetAddresses array
                  }
                  struct DiamondStorage {
                      // maps function selector to the facet address and
                      // the position of the selector in the facetFunctionSelectors.selectors array
                      mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
                      // maps facet addresses to function selectors
                      mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
                      // facet addresses
                      address[] facetAddresses;
                      // Used to query if a contract implements an interface.
                      // Used to implement ERC-165.
                      mapping(bytes4 => bool) supportedInterfaces;
                      // owner of the contract
                      address contractOwner;
                  }
                  function diamondStorage()
                      internal
                      pure
                      returns (DiamondStorage storage ds)
                  {
                      bytes32 position = DIAMOND_STORAGE_POSITION;
                      assembly {
                          ds.slot := position
                      }
                  }
                  event OwnershipTransferred(
                      address indexed previousOwner,
                      address indexed newOwner
                  );
                  function setContractOwner(address _newOwner) internal {
                      DiamondStorage storage ds = diamondStorage();
                      address previousOwner = ds.contractOwner;
                      ds.contractOwner = _newOwner;
                      emit OwnershipTransferred(previousOwner, _newOwner);
                  }
                  function contractOwner() internal view returns (address contractOwner_) {
                      contractOwner_ = diamondStorage().contractOwner;
                  }
                  function isContractOwner(address _guy) internal view returns (bool) {
                      return _guy == contractOwner();
                  }
                  function enforceIsContractOwner() internal view {
                      require(
                          msg.sender == diamondStorage().contractOwner,
                          "LibDiamond: Must be contract owner"
                      );
                  }
                  event DiamondCut(
                      IDiamondCut.FacetCut[] _diamondCut,
                      address _init,
                      bytes _calldata
                  );
                  // Internal function version of diamondCut
                  function diamondCut(
                      IDiamondCut.FacetCut[] memory _diamondCut,
                      address _init,
                      bytes memory _calldata
                  ) internal {
                      for (
                          uint256 facetIndex;
                          facetIndex < _diamondCut.length;
                          facetIndex++
                      ) {
                          IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
                          if (action == IDiamondCut.FacetCutAction.Add) {
                              addFunctions(
                                  _diamondCut[facetIndex].facetAddress,
                                  _diamondCut[facetIndex].functionSelectors
                              );
                          } else if (action == IDiamondCut.FacetCutAction.Replace) {
                              replaceFunctions(
                                  _diamondCut[facetIndex].facetAddress,
                                  _diamondCut[facetIndex].functionSelectors
                              );
                          } else if (action == IDiamondCut.FacetCutAction.Remove) {
                              removeFunctions(
                                  _diamondCut[facetIndex].facetAddress,
                                  _diamondCut[facetIndex].functionSelectors
                              );
                          } else {
                              revert("LibDiamondCut: Incorrect FacetCutAction");
                          }
                      }
                      emit DiamondCut(_diamondCut, _init, _calldata);
                      initializeDiamondCut(_init, _calldata);
                  }
                  function addFunctions(
                      address _facetAddress,
                      bytes4[] memory _functionSelectors
                  ) internal {
                      require(
                          _functionSelectors.length > 0,
                          "LibDiamondCut: No selectors in facet to cut"
                      );
                      DiamondStorage storage ds = diamondStorage();
                      // uint16 selectorCount = uint16(diamondStorage().selectors.length);
                      require(
                          _facetAddress != address(0),
                          "LibDiamondCut: Add facet can't be address(0)"
                      );
                      uint16 selectorPosition =
                          uint16(
                              ds.facetFunctionSelectors[_facetAddress]
                                  .functionSelectors
                                  .length
                          );
                      // add new facet address if it does not exist
                      if (selectorPosition == 0) {
                          enforceHasContractCode(
                              _facetAddress,
                              "LibDiamondCut: New facet has no code"
                          );
                          ds.facetFunctionSelectors[_facetAddress]
                              .facetAddressPosition = uint16(ds.facetAddresses.length);
                          ds.facetAddresses.push(_facetAddress);
                      }
                      for (
                          uint256 selectorIndex;
                          selectorIndex < _functionSelectors.length;
                          selectorIndex++
                      ) {
                          bytes4 selector = _functionSelectors[selectorIndex];
                          address oldFacetAddress =
                              ds.selectorToFacetAndPosition[selector].facetAddress;
                          require(
                              oldFacetAddress == address(0),
                              "LibDiamondCut: Can't add function that already exists"
                          );
                          ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(
                              selector
                          );
                          ds.selectorToFacetAndPosition[selector]
                              .facetAddress = _facetAddress;
                          ds.selectorToFacetAndPosition[selector]
                              .functionSelectorPosition = selectorPosition;
                          selectorPosition++;
                      }
                  }
                  function replaceFunctions(
                      address _facetAddress,
                      bytes4[] memory _functionSelectors
                  ) internal {
                      require(
                          _functionSelectors.length > 0,
                          "LibDiamondCut: No selectors in facet to cut"
                      );
                      DiamondStorage storage ds = diamondStorage();
                      require(
                          _facetAddress != address(0),
                          "LibDiamondCut: Add facet can't be address(0)"
                      );
                      uint16 selectorPosition =
                          uint16(
                              ds.facetFunctionSelectors[_facetAddress]
                                  .functionSelectors
                                  .length
                          );
                      // add new facet address if it does not exist
                      if (selectorPosition == 0) {
                          enforceHasContractCode(
                              _facetAddress,
                              "LibDiamondCut: New facet has no code"
                          );
                          ds.facetFunctionSelectors[_facetAddress]
                              .facetAddressPosition = uint16(ds.facetAddresses.length);
                          ds.facetAddresses.push(_facetAddress);
                      }
                      for (
                          uint256 selectorIndex;
                          selectorIndex < _functionSelectors.length;
                          selectorIndex++
                      ) {
                          bytes4 selector = _functionSelectors[selectorIndex];
                          address oldFacetAddress =
                              ds.selectorToFacetAndPosition[selector].facetAddress;
                          require(
                              oldFacetAddress != _facetAddress,
                              "LibDiamondCut: Can't replace function with same function"
                          );
                          removeFunction(oldFacetAddress, selector);
                          // add function
                          ds.selectorToFacetAndPosition[selector]
                              .functionSelectorPosition = selectorPosition;
                          ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(
                              selector
                          );
                          ds.selectorToFacetAndPosition[selector]
                              .facetAddress = _facetAddress;
                          selectorPosition++;
                      }
                  }
                  function removeFunctions(
                      address _facetAddress,
                      bytes4[] memory _functionSelectors
                  ) internal {
                      require(
                          _functionSelectors.length > 0,
                          "LibDiamondCut: No selectors in facet to cut"
                      );
                      DiamondStorage storage ds = diamondStorage();
                      // if function does not exist then do nothing and return
                      require(
                          _facetAddress == address(0),
                          "LibDiamondCut: Remove facet address must be address(0)"
                      );
                      for (
                          uint256 selectorIndex;
                          selectorIndex < _functionSelectors.length;
                          selectorIndex++
                      ) {
                          bytes4 selector = _functionSelectors[selectorIndex];
                          address oldFacetAddress =
                              ds.selectorToFacetAndPosition[selector].facetAddress;
                          removeFunction(oldFacetAddress, selector);
                      }
                  }
                  function removeFunction(address _facetAddress, bytes4 _selector) internal {
                      DiamondStorage storage ds = diamondStorage();
                      require(
                          _facetAddress != address(0),
                          "LibDiamondCut: Can't remove function that doesn't exist"
                      );
                      // an immutable function is a function defined directly in a diamond
                      require(
                          _facetAddress != address(this),
                          "LibDiamondCut: Can't remove immutable function"
                      );
                      // replace selector with last selector, then delete last selector
                      uint256 selectorPosition =
                          ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
                      uint256 lastSelectorPosition =
                          ds.facetFunctionSelectors[_facetAddress].functionSelectors.length -
                              1;
                      // if not the same then replace _selector with lastSelector
                      if (selectorPosition != lastSelectorPosition) {
                          bytes4 lastSelector =
                              ds.facetFunctionSelectors[_facetAddress].functionSelectors[
                                  lastSelectorPosition
                              ];
                          ds.facetFunctionSelectors[_facetAddress].functionSelectors[
                              selectorPosition
                          ] = lastSelector;
                          ds.selectorToFacetAndPosition[lastSelector]
                              .functionSelectorPosition = uint16(selectorPosition);
                      }
                      // delete the last selector
                      ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
                      delete ds.selectorToFacetAndPosition[_selector];
                      // if no more selectors for facet address then delete the facet address
                      if (lastSelectorPosition == 0) {
                          // replace facet address with last facet address and delete last facet address
                          uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
                          uint256 facetAddressPosition =
                              ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
                          if (facetAddressPosition != lastFacetAddressPosition) {
                              address lastFacetAddress =
                                  ds.facetAddresses[lastFacetAddressPosition];
                              ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
                              ds.facetFunctionSelectors[lastFacetAddress]
                                  .facetAddressPosition = uint16(facetAddressPosition);
                          }
                          ds.facetAddresses.pop();
                          delete ds.facetFunctionSelectors[_facetAddress]
                              .facetAddressPosition;
                      }
                  }
                  function initializeDiamondCut(address _init, bytes memory _calldata)
                      internal
                  {
                      if (_init == address(0)) {
                          require(
                              _calldata.length == 0,
                              "LibDiamondCut: _init is address(0) but_calldata is not empty"
                          );
                      } else {
                          require(
                              _calldata.length > 0,
                              "LibDiamondCut: _calldata is empty but _init is not address(0)"
                          );
                          if (_init != address(this)) {
                              enforceHasContractCode(
                                  _init,
                                  "LibDiamondCut: _init address has no code"
                              );
                          }
                          (bool success, bytes memory error) = _init.delegatecall(_calldata);
                          if (!success) {
                              if (error.length > 0) {
                                  // bubble up the error
                                  GelatoBytes.revertWithError(error, "LibDiamondCut:_init:");
                              } else {
                                  revert("LibDiamondCut: _init function reverted");
                              }
                          }
                      }
                  }
                  function enforceHasContractCode(
                      address _contract,
                      string memory _errorMessage
                  ) internal view {
                      uint256 contractSize;
                      assembly {
                          contractSize := extcodesize(_contract)
                      }
                      require(contractSize > 0, _errorMessage);
                  }
              }
              // "SPDX-License-Identifier: UNLICENSED"
              pragma solidity 0.8.0;
              library GelatoBytes {
                  function calldataSliceSelector(bytes calldata _bytes)
                      internal
                      pure
                      returns (bytes4 selector)
                  {
                      selector =
                          _bytes[0] |
                          (bytes4(_bytes[1]) >> 8) |
                          (bytes4(_bytes[2]) >> 16) |
                          (bytes4(_bytes[3]) >> 24);
                  }
                  function memorySliceSelector(bytes memory _bytes)
                      internal
                      pure
                      returns (bytes4 selector)
                  {
                      selector =
                          _bytes[0] |
                          (bytes4(_bytes[1]) >> 8) |
                          (bytes4(_bytes[2]) >> 16) |
                          (bytes4(_bytes[3]) >> 24);
                  }
                  function revertWithError(bytes memory _bytes, string memory _tracingInfo)
                      internal
                      pure
                  {
                      // 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
                      if (_bytes.length % 32 == 4) {
                          bytes4 selector;
                          assembly {
                              selector := mload(add(0x20, _bytes))
                          }
                          if (selector == 0x08c379a0) {
                              // Function selector for Error(string)
                              assembly {
                                  _bytes := add(_bytes, 68)
                              }
                              revert(string(abi.encodePacked(_tracingInfo, string(_bytes))));
                          } else {
                              revert(
                                  string(abi.encodePacked(_tracingInfo, "NoErrorSelector"))
                              );
                          }
                      } else {
                          revert(
                              string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"))
                          );
                      }
                  }
                  function returnError(bytes memory _bytes, string memory _tracingInfo)
                      internal
                      pure
                      returns (string memory)
                  {
                      // 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
                      if (_bytes.length % 32 == 4) {
                          bytes4 selector;
                          assembly {
                              selector := mload(add(0x20, _bytes))
                          }
                          if (selector == 0x08c379a0) {
                              // Function selector for Error(string)
                              assembly {
                                  _bytes := add(_bytes, 68)
                              }
                              return string(abi.encodePacked(_tracingInfo, string(_bytes)));
                          } else {
                              return
                                  string(abi.encodePacked(_tracingInfo, "NoErrorSelector"));
                          }
                      } else {
                          return
                              string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"));
                      }
                  }
              }
              

              File 2 of 30: TransparentUpgradeableProxy
              // 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 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.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _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 Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev 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.7.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(
                      address _logic,
                      address admin_,
                      bytes memory _data
                  ) payable ERC1967Proxy(_logic, _data) {
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.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
                              /// @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.7.0) (utils/StorageSlot.sol)
              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:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @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
                      }
                  }
              }
              

              File 3 of 30: PENDLE
              {"Address.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity \u003e=0.6.2 \u003c0.8.0;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev Returns true if `account` is a contract.\n     *\n     * [IMPORTANT]\n     * ====\n     * It is unsafe to assume that an address for which this function returns\n     * false is an externally-owned account (EOA) and not a contract.\n     *\n     * Among others, `isContract` will return false for the following\n     * types of addresses:\n     *\n     *  - an externally-owned account\n     *  - a contract in construction\n     *  - an address where a contract will be created\n     *  - an address where a contract lived, but was destroyed\n     * ====\n     */\n    function isContract(address account) internal view returns (bool) {\n        // This method relies on extcodesize, which returns 0 for contracts in\n        // construction, since the code is only stored at the end of the\n        // constructor execution.\n\n        uint256 size;\n        // solhint-disable-next-line no-inline-assembly\n        assembly { size := extcodesize(account) }\n        return size \u003e 0;\n    }\n\n    /**\n     * @dev Replacement for Solidity\u0027s `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        require(address(this).balance \u003e= amount, \"Address: insufficient balance\");\n\n        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value\n        (bool success, ) = recipient.call{ value: amount }(\"\");\n        require(success, \"Address: unable to send value, recipient may have reverted\");\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain`call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason, it is bubbled up by this\n     * function (like regular Solidity function calls).\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n      return functionCall(target, data, \"Address: low-level call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n     * `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n     * with `errorMessage` as a fallback revert reason when `target` reverts.\n     *\n     * _Available since v3.1._\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {\n        require(address(this).balance \u003e= value, \"Address: insufficient balance for call\");\n        require(isContract(target), \"Address: call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.call{ value: value }(data);\n        return _verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        return functionStaticCall(target, data, \"Address: low-level static call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a static call.\n     *\n     * _Available since v3.3._\n     */\n    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {\n        require(isContract(target), \"Address: static call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return _verifyCallResult(success, returndata, errorMessage);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionDelegateCall(target, data, \"Address: low-level delegate call failed\");\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n     * but performing a delegate call.\n     *\n     * _Available since v3.4._\n     */\n    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {\n        require(isContract(target), \"Address: delegate call to non-contract\");\n\n        // solhint-disable-next-line avoid-low-level-calls\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return _verifyCallResult(success, returndata, errorMessage);\n    }\n\n    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {\n        if (success) {\n            return returndata;\n        } else {\n            // Look for revert reason and bubble it up if present\n            if (returndata.length \u003e 0) {\n                // The easiest way to bubble the revert reason is using memory via assembly\n\n                // solhint-disable-next-line no-inline-assembly\n                assembly {\n                    let returndata_size := mload(returndata)\n                    revert(add(32, returndata), returndata_size)\n                }\n            } else {\n                revert(errorMessage);\n            }\n        }\n    }\n}\n"},"IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity \u003e=0.6.0 \u003c0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller\u0027s account to `recipient`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller\u0027s tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender\u0027s allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\n     * allowance mechanism. `amount` is then deducted from the caller\u0027s\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"},"IPENDLE.sol":{"content":"// SPDX-License-Identifier: MIT\n/*\n * MIT License\n * ===========\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n */\n\npragma solidity 0.7.6;\n\nimport \"./IERC20.sol\";\n\ninterface IPENDLE is IERC20 {\n    function initiateConfigChanges(\n        uint256 _emissionRateMultiplierNumerator,\n        uint256 _terminalInflationRateNumerator,\n        address _liquidityIncentivesRecipient,\n        bool _isBurningAllowed\n    ) external;\n\n    function increaseAllowance(address spender, uint256 addedValue) external returns (bool);\n\n    function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);\n\n    function burn(uint256 amount) external returns (bool);\n\n    function applyConfigChanges() external;\n\n    function claimLiquidityEmissions() external returns (uint256 totalEmissions);\n\n    function isPendleToken() external view returns (bool);\n\n    function getPriorVotes(address account, uint256 blockNumber) external view returns (uint256);\n\n    function startTime() external view returns (uint256);\n\n    function configChangesInitiated() external view returns (uint256);\n\n    function emissionRateMultiplierNumerator() external view returns (uint256);\n\n    function terminalInflationRateNumerator() external view returns (uint256);\n\n    function liquidityIncentivesRecipient() external view returns (address);\n\n    function isBurningAllowed() external view returns (bool);\n\n    function pendingEmissionRateMultiplierNumerator() external view returns (uint256);\n\n    function pendingTerminalInflationRateNumerator() external view returns (uint256);\n\n    function pendingLiquidityIncentivesRecipient() external view returns (address);\n\n    function pendingIsBurningAllowed() external view returns (bool);\n}\n"},"PENDLE.sol":{"content":"/* solhint-disable  const-name-snakecase*/\n// SPDX-License-Identifier: MIT\n/*\n * MIT License\n * ===========\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n */\npragma solidity 0.7.6;\npragma experimental ABIEncoderV2;\n\nimport \"./IPENDLE.sol\";\nimport \"./Permissions.sol\";\nimport \"./Withdrawable.sol\";\nimport \"./SafeMath.sol\";\n\n/**\n * @notice The mechanics for delegating votes to other accounts is adapted from Compound\n *   https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/Comp.sol\n ***/\ncontract PENDLE is IPENDLE, Permissions, Withdrawable {\n    using SafeMath for uint256;\n\n    /// @notice A checkpoint for marking number of votes from a given block\n    struct Checkpoint {\n        uint32 fromBlock;\n        uint256 votes;\n    }\n\n    bool public constant override isPendleToken = true;\n    string public constant name = \"Pendle\";\n    string public constant symbol = \"PENDLE\";\n    uint8 public constant decimals = 18;\n    uint256 public override totalSupply;\n\n    uint256 private constant TEAM_INVESTOR_ADVISOR_AMOUNT = 94917125 * 1e18;\n    uint256 private constant ECOSYSTEM_FUND_TOKEN_AMOUNT = 46 * 1_000_000 * 1e18;\n    uint256 private constant PUBLIC_SALES_TOKEN_AMOUNT = 16582875 * 1e18;\n    uint256 private constant INITIAL_LIQUIDITY_EMISSION = 1200000 * 1e18;\n    uint256 private constant CONFIG_DENOMINATOR = 1_000_000_000_000;\n    uint256 private constant CONFIG_CHANGES_TIME_LOCK = 7 days;\n    uint256 public override emissionRateMultiplierNumerator;\n    uint256 public override terminalInflationRateNumerator;\n    address public override liquidityIncentivesRecipient;\n    bool public override isBurningAllowed;\n    uint256 public override pendingEmissionRateMultiplierNumerator;\n    uint256 public override pendingTerminalInflationRateNumerator;\n    address public override pendingLiquidityIncentivesRecipient;\n    bool public override pendingIsBurningAllowed;\n    uint256 public override configChangesInitiated;\n    uint256 public override startTime;\n    uint256 public lastWeeklyEmission;\n    uint256 public lastWeekEmissionSent;\n\n    mapping(address =\u003e mapping(address =\u003e uint256)) internal allowances;\n    mapping(address =\u003e uint256) internal balances;\n    mapping(address =\u003e address) public delegates;\n\n    /// @notice A record of votes checkpoints for each account, by index\n    mapping(address =\u003e mapping(uint32 =\u003e Checkpoint)) public checkpoints;\n\n    /// @notice The number of checkpoints for each account\n    mapping(address =\u003e uint32) public numCheckpoints;\n\n    /// @notice The EIP-712 typehash for the contract\u0027s domain\n    bytes32 public constant DOMAIN_TYPEHASH =\n        keccak256(\"EIP712Domain(string name,uint256 chainId,address verifyingContract)\");\n\n    /// @notice The EIP-712 typehash for the delegation struct used by the contract\n    bytes32 public constant DELEGATION_TYPEHASH =\n        keccak256(\"Delegation(address delegatee,uint256 nonce,uint256 expiry)\");\n\n    /// @notice A record of states for signing / validating signatures\n    mapping(address =\u003e uint256) public nonces;\n\n    /// @notice An event thats emitted when an account changes its delegate\n    event DelegateChanged(\n        address indexed delegator,\n        address indexed fromDelegate,\n        address indexed toDelegate\n    );\n\n    /// @notice An event thats emitted when a delegate account\u0027s vote balance changes\n    event DelegateVotesChanged(\n        address indexed delegate,\n        uint256 previousBalance,\n        uint256 newBalance\n    );\n\n    event PendingConfigChanges(\n        uint256 pendingEmissionRateMultiplierNumerator,\n        uint256 pendingTerminalInflationRateNumerator,\n        address pendingLiquidityIncentivesRecipient,\n        bool pendingIsBurningAllowed\n    );\n\n    event ConfigsChanged(\n        uint256 emissionRateMultiplierNumerator,\n        uint256 terminalInflationRateNumerator,\n        address liquidityIncentivesRecipient,\n        bool isBurningAllowed\n    );\n\n    /**\n     * @notice Construct a new PENDLE token\n     */\n    constructor(\n        address _governance,\n        address pendleTeamTokens,\n        address pendleEcosystemFund,\n        address salesMultisig,\n        address _liquidityIncentivesRecipient\n    ) Permissions(_governance) {\n        require(\n            pendleTeamTokens != address(0) \u0026\u0026\n                pendleEcosystemFund != address(0) \u0026\u0026\n                salesMultisig != address(0) \u0026\u0026\n                _liquidityIncentivesRecipient != address(0),\n            \"ZERO_ADDRESS\"\n        );\n        _mint(pendleTeamTokens, TEAM_INVESTOR_ADVISOR_AMOUNT);\n        _mint(pendleEcosystemFund, ECOSYSTEM_FUND_TOKEN_AMOUNT);\n        _mint(salesMultisig, PUBLIC_SALES_TOKEN_AMOUNT);\n        _mint(_liquidityIncentivesRecipient, INITIAL_LIQUIDITY_EMISSION * 26);\n        emissionRateMultiplierNumerator = (CONFIG_DENOMINATOR * 989) / 1000; // emission rate = 98.9% -\u003e 1.1% decay\n        terminalInflationRateNumerator = 379848538; // terminal inflation rate = 2% =\u003e weekly inflation = 0.0379848538%\n        liquidityIncentivesRecipient = _liquidityIncentivesRecipient;\n        startTime = block.timestamp;\n        lastWeeklyEmission = INITIAL_LIQUIDITY_EMISSION;\n        lastWeekEmissionSent = 26; // already done liquidity emissions for the first 26 weeks\n    }\n\n    /**\n     * @notice Approve `spender` to transfer up to `amount` from `src`\n     * @dev This will overwrite the approval amount for `spender`\n     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)\n     * @param spender The address of the account which may transfer tokens\n     * @param amount The number of tokens that are approved\n     * @return Whether or not the approval succeeded\n     **/\n    function approve(address spender, uint256 amount) external override returns (bool) {\n        _approve(msg.sender, spender, amount);\n        return true;\n    }\n\n    /**\n     * @notice Transfer `amount` tokens from `msg.sender` to `dst`\n     * @param dst The address of the destination account\n     * @param amount The number of tokens to transfer\n     * @return Whether or not the transfer succeeded\n     */\n    function transfer(address dst, uint256 amount) external override returns (bool) {\n        _transfer(msg.sender, dst, amount);\n        return true;\n    }\n\n    /**\n     * @notice Transfer `amount` tokens from `src` to `dst`\n     * @param src The address of the source account\n     * @param dst The address of the destination account\n     * @param amount The number of tokens to transfer\n     * @return Whether or not the transfer succeeded\n     */\n    function transferFrom(\n        address src,\n        address dst,\n        uint256 amount\n    ) external override returns (bool) {\n        _transfer(src, dst, amount);\n        _approve(\n            src,\n            msg.sender,\n            allowances[src][msg.sender].sub(amount, \"TRANSFER_EXCEED_ALLOWANCE\")\n        );\n        return true;\n    }\n\n    /**\n     * @dev Increases the allowance granted to spender by the caller.\n     * @param spender The address to increase the allowance from.\n     * @param addedValue The amount allowance to add.\n     * @return returns true if allowance has increased, otherwise false\n     **/\n    function increaseAllowance(address spender, uint256 addedValue)\n        public\n        override\n        returns (bool)\n    {\n        _approve(msg.sender, spender, allowances[msg.sender][spender].add(addedValue));\n        return true;\n    }\n\n    /**\n     * @dev Decreases the allowance granted to spender by the caller.\n     * @param spender The address to reduce the allowance from.\n     * @param subtractedValue The amount allowance to subtract.\n     * @return Returns true if allowance has decreased, otherwise false.\n     **/\n    function decreaseAllowance(address spender, uint256 subtractedValue)\n        public\n        override\n        returns (bool)\n    {\n        _approve(\n            msg.sender,\n            spender,\n            allowances[msg.sender][spender].sub(subtractedValue, \"NEGATIVE_ALLOWANCE\")\n        );\n        return true;\n    }\n\n    /**\n     * @dev Burns an amount of tokens from the msg.sender\n     * @param amount The amount to burn\n     * @return Returns true if the operation is successful\n     **/\n    function burn(uint256 amount) public override returns (bool) {\n        require(isBurningAllowed, \"BURNING_NOT_ALLOWED\");\n        _burn(msg.sender, amount);\n        return true;\n    }\n\n    /**\n     * @notice Get the number of tokens `spender` is approved to spend on behalf of `account`\n     * @param account The address of the account holding the funds\n     * @param spender The address of the account spending the funds\n     * @return The number of tokens approved\n     **/\n    function allowance(address account, address spender) external view override returns (uint256) {\n        return allowances[account][spender];\n    }\n\n    /**\n     * @notice Get the number of tokens held by the `account`\n     * @param account The address of the account to get the balance of\n     * @return The number of tokens held\n     */\n    function balanceOf(address account) external view override returns (uint256) {\n        return balances[account];\n    }\n\n    /**\n     * @notice Gets the current votes balance for `account`\n     * @param account The address to get votes balance\n     * @return The number of current votes for `account`\n     */\n    function getCurrentVotes(address account) external view returns (uint256) {\n        uint32 nCheckpoints = numCheckpoints[account];\n        return nCheckpoints \u003e 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;\n    }\n\n    /**\n     * @notice Delegate votes from `msg.sender` to `delegatee`\n     * @param delegatee The address to delegate votes to\n     */\n    function delegate(address delegatee) public {\n        return _delegate(msg.sender, delegatee);\n    }\n\n    /**\n     * @notice Delegates votes from signatory to `delegatee`\n     * @param delegatee The address to delegate votes to\n     * @param nonce The contract state required to match the signature\n     * @param expiry The time at which to expire the signature\n     * @param v The recovery byte of the signature\n     * @param r Half of the ECDSA signature pair\n     * @param s Half of the ECDSA signature pair\n     */\n    function delegateBySig(\n        address delegatee,\n        uint256 nonce,\n        uint256 expiry,\n        uint8 v,\n        bytes32 r,\n        bytes32 s\n    ) public {\n        bytes32 domainSeparator =\n            keccak256(\n                abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainId(), address(this))\n            );\n        bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));\n        bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", domainSeparator, structHash));\n        address signatory = ecrecover(digest, v, r, s);\n        require(signatory != address(0), \"INVALID_SIGNATURE\");\n        require(nonce == nonces[signatory]++, \"INVALID_NONCE\");\n        require(block.timestamp \u003c= expiry, \"SIGNATURE_EXPIRED\");\n        return _delegate(signatory, delegatee);\n    }\n\n    /**\n     * @notice Determine the prior number of votes for an account as of a block number\n     * @dev Block number must be a finalized block or else\n                this function will revert to prevent misinformation\n     * @param account The address of the account to check\n     * @param blockNumber The block number to get the vote balance at\n     * @return The number of votes the account had as of the given block\n     */\n    function getPriorVotes(address account, uint256 blockNumber)\n        public\n        view\n        override\n        returns (uint256)\n    {\n        require(blockNumber \u003c block.number, \"NOT_YET_DETERMINED\");\n\n        uint32 nCheckpoints = numCheckpoints[account];\n        if (nCheckpoints == 0) {\n            return 0;\n        }\n\n        // First check most recent balance\n        if (checkpoints[account][nCheckpoints - 1].fromBlock \u003c= blockNumber) {\n            return checkpoints[account][nCheckpoints - 1].votes;\n        }\n\n        // Next check implicit zero balance\n        if (checkpoints[account][0].fromBlock \u003e blockNumber) {\n            return 0;\n        }\n\n        uint32 lower = 0;\n        uint32 upper = nCheckpoints - 1;\n        while (upper \u003e lower) {\n            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow\n            Checkpoint memory cp = checkpoints[account][center];\n            if (cp.fromBlock == blockNumber) {\n                return cp.votes;\n            } else if (cp.fromBlock \u003c blockNumber) {\n                lower = center;\n            } else {\n                upper = center - 1;\n            }\n        }\n        return checkpoints[account][lower].votes;\n    }\n\n    function _delegate(address delegator, address delegatee) internal {\n        address currentDelegate = delegates[delegator];\n        uint256 delegatorBalance = balances[delegator];\n        delegates[delegator] = delegatee;\n\n        emit DelegateChanged(delegator, currentDelegate, delegatee);\n\n        _moveDelegates(currentDelegate, delegatee, delegatorBalance);\n    }\n\n    function _transfer(\n        address src,\n        address dst,\n        uint256 amount\n    ) internal {\n        require(src != address(0), \"SENDER_ZERO_ADDR\");\n        require(dst != address(0), \"RECEIVER_ZERO_ADDR\");\n        require(dst != address(this), \"SEND_TO_TOKEN_CONTRACT\");\n\n        balances[src] = balances[src].sub(amount, \"TRANSFER_EXCEED_BALANCE\");\n        balances[dst] = balances[dst].add(amount);\n        emit Transfer(src, dst, amount);\n\n        _moveDelegates(delegates[src], delegates[dst], amount);\n    }\n\n    function _approve(\n        address src,\n        address dst,\n        uint256 amount\n    ) internal virtual {\n        require(src != address(0), \"OWNER_ZERO_ADDR\");\n        require(dst != address(0), \"SPENDER_ZERO_ADDR\");\n\n        allowances[src][dst] = amount;\n        emit Approval(src, dst, amount);\n    }\n\n    function _moveDelegates(\n        address srcRep,\n        address dstRep,\n        uint256 amount\n    ) internal {\n        if (srcRep != dstRep \u0026\u0026 amount \u003e 0) {\n            if (srcRep != address(0)) {\n                uint32 srcRepNum = numCheckpoints[srcRep];\n                uint256 srcRepOld = srcRepNum \u003e 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;\n                uint256 srcRepNew = srcRepOld.sub(amount);\n                _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);\n            }\n\n            if (dstRep != address(0)) {\n                uint32 dstRepNum = numCheckpoints[dstRep];\n                uint256 dstRepOld = dstRepNum \u003e 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;\n                uint256 dstRepNew = dstRepOld.add(amount);\n                _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);\n            }\n        }\n    }\n\n    function _writeCheckpoint(\n        address delegatee,\n        uint32 nCheckpoints,\n        uint256 oldVotes,\n        uint256 newVotes\n    ) internal {\n        uint32 blockNumber = safe32(block.number, \"BLOCK_NUM_EXCEED_32_BITS\");\n\n        if (\n            nCheckpoints \u003e 0 \u0026\u0026 checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber\n        ) {\n            checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;\n        } else {\n            checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);\n            numCheckpoints[delegatee] = nCheckpoints + 1;\n        }\n\n        emit DelegateVotesChanged(delegatee, oldVotes, newVotes);\n    }\n\n    function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {\n        require(n \u003c 2**32, errorMessage);\n        return uint32(n);\n    }\n\n    function getChainId() internal pure returns (uint256) {\n        uint256 chainId;\n        assembly {\n            chainId := chainid()\n        }\n        return chainId;\n    }\n\n    function initiateConfigChanges(\n        uint256 _emissionRateMultiplierNumerator,\n        uint256 _terminalInflationRateNumerator,\n        address _liquidityIncentivesRecipient,\n        bool _isBurningAllowed\n    ) external override onlyGovernance {\n        require(_liquidityIncentivesRecipient != address(0), \"ZERO_ADDRESS\");\n        pendingEmissionRateMultiplierNumerator = _emissionRateMultiplierNumerator;\n        pendingTerminalInflationRateNumerator = _terminalInflationRateNumerator;\n        pendingLiquidityIncentivesRecipient = _liquidityIncentivesRecipient;\n        pendingIsBurningAllowed = _isBurningAllowed;\n        emit PendingConfigChanges(\n            _emissionRateMultiplierNumerator,\n            _terminalInflationRateNumerator,\n            _liquidityIncentivesRecipient,\n            _isBurningAllowed\n        );\n        configChangesInitiated = block.timestamp;\n    }\n\n    function applyConfigChanges() external override {\n        require(configChangesInitiated != 0, \"UNINITIATED_CONFIG_CHANGES\");\n        require(\n            block.timestamp \u003e configChangesInitiated + CONFIG_CHANGES_TIME_LOCK,\n            \"TIMELOCK_IS_NOT_OVER\"\n        );\n\n        _mintLiquidityEmissions(); // We must settle the pending liquidity emissions first, to make sure the weeks in the past follow the old configs\n\n        emissionRateMultiplierNumerator = pendingEmissionRateMultiplierNumerator;\n        terminalInflationRateNumerator = pendingTerminalInflationRateNumerator;\n        liquidityIncentivesRecipient = pendingLiquidityIncentivesRecipient;\n        isBurningAllowed = pendingIsBurningAllowed;\n        configChangesInitiated = 0;\n        emit ConfigsChanged(\n            emissionRateMultiplierNumerator,\n            terminalInflationRateNumerator,\n            liquidityIncentivesRecipient,\n            isBurningAllowed\n        );\n    }\n\n    function claimLiquidityEmissions() external override returns (uint256 totalEmissions) {\n        require(msg.sender == liquidityIncentivesRecipient, \"NOT_INCENTIVES_RECIPIENT\");\n        totalEmissions = _mintLiquidityEmissions();\n    }\n\n    function _mintLiquidityEmissions() internal returns (uint256 totalEmissions) {\n        uint256 _currentWeek = _getCurrentWeek();\n        if (_currentWeek \u003c= lastWeekEmissionSent) {\n            return 0;\n        }\n        for (uint256 i = lastWeekEmissionSent + 1; i \u003c= _currentWeek; i++) {\n            if (i \u003c= 259) {\n                lastWeeklyEmission = lastWeeklyEmission.mul(emissionRateMultiplierNumerator).div(\n                    CONFIG_DENOMINATOR\n                );\n            } else {\n                lastWeeklyEmission = totalSupply.mul(terminalInflationRateNumerator).div(\n                    CONFIG_DENOMINATOR\n                );\n            }\n            _mint(liquidityIncentivesRecipient, lastWeeklyEmission);\n            totalEmissions = totalEmissions.add(lastWeeklyEmission);\n        }\n        lastWeekEmissionSent = _currentWeek;\n    }\n\n    // get current 1-indexed week id\n    function _getCurrentWeek() internal view returns (uint256 weekId) {\n        weekId = (block.timestamp - startTime) / (7 days) + 1;\n    }\n\n    function _mint(address account, uint256 amount) internal {\n        require(account != address(0), \"MINT_TO_ZERO_ADDR\");\n\n        totalSupply = totalSupply.add(amount);\n        balances[account] = balances[account].add(amount);\n        emit Transfer(address(0), account, amount);\n    }\n\n    function _burn(address account, uint256 amount) internal {\n        require(account != address(0), \"BURN_FROM_ZERO_ADDRESS\");\n\n        uint256 accountBalance = balances[account];\n        require(accountBalance \u003e= amount, \"BURN_EXCEED_BALANCE\");\n        balances[account] = accountBalance.sub(amount);\n        totalSupply = totalSupply.sub(amount);\n\n        emit Transfer(account, address(0), amount);\n    }\n}\n"},"Permissions.sol":{"content":"// SPDX-License-Identifier: MIT\n/*\n * MIT License\n * ===========\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n */\npragma solidity 0.7.6;\n\nimport \"./IERC20.sol\";\n\nabstract contract Permissions {\n    address public governance;\n    address public pendingGovernance;\n    address internal initializer;\n\n    event GovernanceClaimed(address newGovernance, address previousGovernance);\n\n    event TransferGovernancePending(address pendingGovernance);\n\n    constructor(address _governance) {\n        require(_governance != address(0), \"ZERO_ADDRESS\");\n        initializer = msg.sender;\n        governance = _governance;\n    }\n\n    modifier initialized() {\n        require(initializer == address(0), \"NOT_INITIALIZED\");\n        _;\n    }\n\n    modifier onlyGovernance() {\n        require(msg.sender == governance, \"ONLY_GOVERNANCE\");\n        _;\n    }\n\n    /**\n     * @dev Allows the pendingGovernance address to finalize the change governance process.\n     */\n    function claimGovernance() public {\n        require(pendingGovernance == msg.sender, \"WRONG_GOVERNANCE\");\n        emit GovernanceClaimed(pendingGovernance, governance);\n        governance = pendingGovernance;\n        pendingGovernance = address(0);\n    }\n\n    /**\n     * @dev Allows the current governance to set the pendingGovernance address.\n     * @param _governance The address to transfer ownership to.\n     */\n    function transferGovernance(address _governance) public onlyGovernance {\n        require(_governance != address(0), \"ZERO_ADDRESS\");\n        pendingGovernance = _governance;\n\n        emit TransferGovernancePending(pendingGovernance);\n    }\n}\n"},"SafeERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity \u003e=0.6.0 \u003c0.8.0;\n\nimport \"./IERC20.sol\";\nimport \"./SafeMath.sol\";\nimport \"./Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using SafeMath for uint256;\n    using Address for address;\n\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));\n    }\n\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));\n    }\n\n    /**\n     * @dev Deprecated. This function has issues similar to the ones found in\n     * {IERC20-approve}, and its usage is discouraged.\n     *\n     * Whenever possible, use {safeIncreaseAllowance} and\n     * {safeDecreaseAllowance} instead.\n     */\n    function safeApprove(IERC20 token, address spender, uint256 value) internal {\n        // safeApprove should only be called when setting an initial allowance,\n        // or when resetting it to zero. To increase and decrease it, use\n        // \u0027safeIncreaseAllowance\u0027 and \u0027safeDecreaseAllowance\u0027\n        // solhint-disable-next-line max-line-length\n        require((value == 0) || (token.allowance(address(this), spender) == 0),\n            \"SafeERC20: approve from non-zero to non-zero allowance\"\n        );\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));\n    }\n\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 newAllowance = token.allowance(address(this), spender).add(value);\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 newAllowance = token.allowance(address(this), spender).sub(value, \"SafeERC20: decreased allowance below zero\");\n        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity\u0027s return data size checking mechanism, since\n        // we\u0027re implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data, \"SafeERC20: low-level call failed\");\n        if (returndata.length \u003e 0) { // Return data is optional\n            // solhint-disable-next-line max-line-length\n            require(abi.decode(returndata, (bool)), \"SafeERC20: ERC20 operation did not succeed\");\n        }\n    }\n}\n"},"SafeMath.sol":{"content":"// SPDX-License-Identifier: MIT\n\npragma solidity \u003e=0.6.0 \u003c0.8.0;\n\n/**\n * @dev Wrappers over Solidity\u0027s arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it\u0027s recommended to use it always.\n */\nlibrary SafeMath {\n    /**\n     * @dev Returns the addition of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        uint256 c = a + b;\n        if (c \u003c a) return (false, 0);\n        return (true, c);\n    }\n\n    /**\n     * @dev Returns the substraction of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        if (b \u003e a) return (false, 0);\n        return (true, a - b);\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        // Gas optimization: this is cheaper than requiring \u0027a\u0027 not being zero, but the\n        // benefit is lost if \u0027b\u0027 is also tested.\n        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n        if (a == 0) return (true, 0);\n        uint256 c = a * b;\n        if (c / a != b) return (false, 0);\n        return (true, c);\n    }\n\n    /**\n     * @dev Returns the division of two unsigned integers, with a division by zero flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        if (b == 0) return (false, 0);\n        return (true, a / b);\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.\n     *\n     * _Available since v3.4._\n     */\n    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {\n        if (b == 0) return (false, 0);\n        return (true, a % b);\n    }\n\n    /**\n     * @dev Returns the addition of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity\u0027s `+` operator.\n     *\n     * Requirements:\n     *\n     * - Addition cannot overflow.\n     */\n    function add(uint256 a, uint256 b) internal pure returns (uint256) {\n        uint256 c = a + b;\n        require(c \u003e= a, \"SafeMath: addition overflow\");\n        return c;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity\u0027s `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n        require(b \u003c= a, \"SafeMath: subtraction overflow\");\n        return a - b;\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity\u0027s `*` operator.\n     *\n     * Requirements:\n     *\n     * - Multiplication cannot overflow.\n     */\n    function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n        if (a == 0) return 0;\n        uint256 c = a * b;\n        require(c / a == b, \"SafeMath: multiplication overflow\");\n        return c;\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers, reverting on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity\u0027s `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b) internal pure returns (uint256) {\n        require(b \u003e 0, \"SafeMath: division by zero\");\n        return a / b;\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * reverting when dividing by zero.\n     *\n     * Counterpart to Solidity\u0027s `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n        require(b \u003e 0, \"SafeMath: modulo by zero\");\n        return a % b;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n     * overflow (when the result is negative).\n     *\n     * CAUTION: This function is deprecated because it requires allocating memory for the error\n     * message unnecessarily. For custom revert reasons use {trySub}.\n     *\n     * Counterpart to Solidity\u0027s `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b \u003c= a, errorMessage);\n        return a - b;\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers, reverting with custom message on\n     * division by zero. The result is rounded towards zero.\n     *\n     * CAUTION: This function is deprecated because it requires allocating memory for the error\n     * message unnecessarily. For custom revert reasons use {tryDiv}.\n     *\n     * Counterpart to Solidity\u0027s `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b \u003e 0, errorMessage);\n        return a / b;\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * reverting with custom message when dividing by zero.\n     *\n     * CAUTION: This function is deprecated because it requires allocating memory for the error\n     * message unnecessarily. For custom revert reasons use {tryMod}.\n     *\n     * Counterpart to Solidity\u0027s `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b \u003e 0, errorMessage);\n        return a % b;\n    }\n}\n"},"Withdrawable.sol":{"content":"// SPDX-License-Identifier: MIT\n/*\n * MIT License\n * ===========\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n */\npragma solidity 0.7.6;\n\nimport \"./IERC20.sol\";\nimport \"./SafeERC20.sol\";\nimport \"./Permissions.sol\";\n\nabstract contract Withdrawable is Permissions {\n    using SafeERC20 for IERC20;\n\n    event EtherWithdraw(uint256 amount, address sendTo);\n    event TokenWithdraw(IERC20 token, uint256 amount, address sendTo);\n\n    /**\n     * @dev Allows governance to withdraw Ether in a Pendle contract\n     *      in case of accidental ETH transfer into the contract.\n     * @param amount The amount of Ether to withdraw.\n     * @param sendTo The recipient address.\n     */\n    function withdrawEther(uint256 amount, address payable sendTo) external onlyGovernance {\n        (bool success, ) = sendTo.call{value: amount}(\"\");\n        require(success, \"WITHDRAW_FAILED\");\n        emit EtherWithdraw(amount, sendTo);\n    }\n\n    /**\n     * @dev Allows governance to withdraw all IERC20 compatible tokens in a Pendle\n     *      contract in case of accidental token transfer into the contract.\n     * @param token IERC20 The address of the token contract.\n     * @param amount The amount of IERC20 tokens to withdraw.\n     * @param sendTo The recipient address.\n     */\n    function withdrawToken(\n        IERC20 token,\n        uint256 amount,\n        address sendTo\n    ) external onlyGovernance {\n        token.safeTransfer(sendTo, amount);\n        emit TokenWithdraw(token, amount, sendTo);\n    }\n}\n"}}

              File 4 of 30: SimpleSwapper
              /**
               *Submitted for verification at Arbiscan.io on 2023-12-20
              */
              
              // SPDX-License-Identifier: MIT
              // File: @openzeppelin-4/contracts/token/ERC20/IERC20.sol
              
              // 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);
              }
              
              // File: @openzeppelin-4/contracts/token/ERC20/extensions/draft-IERC20Permit.sol
              
              // 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);
              }
              
              // File: @openzeppelin-4/contracts/utils/Address.sol
              
              // OpenZeppelin Contracts (last updated v4.7.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
                              /// @solidity memory-safe-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              
              // File: @openzeppelin-4/contracts/token/ERC20/utils/SafeERC20.sol
              
              // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol)
              
              pragma solidity ^0.8.0;
              
              
              
              /**
               * @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));
                      }
                  }
              
                  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");
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              
              // File: contracts/BIFI/utils/BytesLib.sol
              
              /*
               * @title Solidity Bytes Arrays Utils
               * @author Gonçalo Sá <goncalo.sa@consensys.net>
               *
               * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
               *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
               */
              pragma solidity >=0.8.0 <0.9.0;
              
              
              library BytesLib {
                  function concat(
                      bytes memory _preBytes,
                      bytes memory _postBytes
                  )
                  internal
                  pure
                  returns (bytes memory)
                  {
                      bytes memory tempBytes;
              
                      assembly {
                      // Get a location of some free memory and store it in tempBytes as
                      // Solidity does for memory variables.
                          tempBytes := mload(0x40)
              
                      // Store the length of the first bytes array at the beginning of
                      // the memory for tempBytes.
                          let length := mload(_preBytes)
                          mstore(tempBytes, length)
              
                      // Maintain a memory counter for the current write location in the
                      // temp bytes array by adding the 32 bytes for the array length to
                      // the starting location.
                          let mc := add(tempBytes, 0x20)
                      // Stop copying when the memory counter reaches the length of the
                      // first bytes array.
                          let end := add(mc, length)
              
                          for {
                          // Initialize a copy counter to the start of the _preBytes data,
                          // 32 bytes into its memory.
                              let cc := add(_preBytes, 0x20)
                          } lt(mc, end) {
                          // Increase both counters by 32 bytes each iteration.
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                          // Write the _preBytes data into the tempBytes memory 32 bytes
                          // at a time.
                              mstore(mc, mload(cc))
                          }
              
                      // Add the length of _postBytes to the current length of tempBytes
                      // and store it as the new length in the first 32 bytes of the
                      // tempBytes memory.
                          length := mload(_postBytes)
                          mstore(tempBytes, add(length, mload(tempBytes)))
              
                      // Move the memory counter back from a multiple of 0x20 to the
                      // actual end of the _preBytes data.
                          mc := end
                      // Stop copying when the memory counter reaches the new combined
                      // length of the arrays.
                          end := add(mc, length)
              
                          for {
                              let cc := add(_postBytes, 0x20)
                          } lt(mc, end) {
                              mc := add(mc, 0x20)
                              cc := add(cc, 0x20)
                          } {
                              mstore(mc, mload(cc))
                          }
              
                      // Update the free-memory pointer by padding our last write location
                      // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
                      // next 32 byte block, then round down to the nearest multiple of
                      // 32. If the sum of the length of the two arrays is zero then add
                      // one before rounding down to leave a blank 32 bytes (the length block with 0).
                          mstore(0x40, and(
                          add(add(end, iszero(add(length, mload(_preBytes)))), 31),
                          not(31) // Round down to the nearest 32 bytes.
                          ))
                      }
              
                      return tempBytes;
                  }
              
                  function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
                      assembly {
                      // Read the first 32 bytes of _preBytes storage, which is the length
                      // of the array. (We don't need to use the offset into the slot
                      // because arrays use the entire slot.)
                          let fslot := sload(_preBytes.slot)
                      // Arrays of 31 bytes or less have an even value in their slot,
                      // while longer arrays have an odd value. The actual length is
                      // the slot divided by two for odd values, and the lowest order
                      // byte divided by two for even values.
                      // If the slot is even, bitwise and the slot with 255 and divide by
                      // two to get the length. If the slot is odd, bitwise and the slot
                      // with -1 and divide by two.
                          let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                          let mlength := mload(_postBytes)
                          let newlength := add(slength, mlength)
                      // slength can contain both the length and contents of the array
                      // if length < 32 bytes so let's prepare for that
                      // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                          switch add(lt(slength, 32), lt(newlength, 32))
                          case 2 {
                          // Since the new array still fits in the slot, we just need to
                          // update the contents of the slot.
                          // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                              sstore(
                              _preBytes.slot,
                              // all the modifications to the slot are inside this
                              // next block
                              add(
                              // we can just add to the slot contents because the
                              // bytes we want to change are the LSBs
                              fslot,
                              add(
                              mul(
                              div(
                              // load the bytes from memory
                              mload(add(_postBytes, 0x20)),
                              // zero all bytes to the right
                              exp(0x100, sub(32, mlength))
                              ),
                              // and now shift left the number of bytes to
                              // leave space for the length in the slot
                              exp(0x100, sub(32, newlength))
                              ),
                              // increase length by the double of the memory
                              // bytes length
                              mul(mlength, 2)
                              )
                              )
                              )
                          }
                          case 1 {
                          // The stored value fits in the slot, but the combined value
                          // will exceed it.
                          // get the keccak hash to get the contents of the array
                              mstore(0x0, _preBytes.slot)
                              let sc := add(keccak256(0x0, 0x20), div(slength, 32))
              
                          // save new length
                              sstore(_preBytes.slot, add(mul(newlength, 2), 1))
              
                          // The contents of the _postBytes array start 32 bytes into
                          // the structure. Our first read should obtain the `submod`
                          // bytes that can fit into the unused space in the last word
                          // of the stored array. To get this, we read 32 bytes starting
                          // from `submod`, so the data we read overlaps with the array
                          // contents by `submod` bytes. Masking the lowest-order
                          // `submod` bytes allows us to add that value directly to the
                          // stored value.
              
                              let submod := sub(32, slength)
                              let mc := add(_postBytes, submod)
                              let end := add(_postBytes, mlength)
                              let mask := sub(exp(0x100, submod), 1)
              
                              sstore(
                              sc,
                              add(
                              and(
                              fslot,
                              0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                              ),
                              and(mload(mc), mask)
                              )
                              )
              
                              for {
                                  mc := add(mc, 0x20)
                                  sc := add(sc, 1)
                              } lt(mc, end) {
                                  sc := add(sc, 1)
                                  mc := add(mc, 0x20)
                              } {
                                  sstore(sc, mload(mc))
                              }
              
                              mask := exp(0x100, sub(mc, end))
              
                              sstore(sc, mul(div(mload(mc), mask), mask))
                          }
                          default {
                          // get the keccak hash to get the contents of the array
                              mstore(0x0, _preBytes.slot)
                          // Start copying to the last used word of the stored array.
                              let sc := add(keccak256(0x0, 0x20), div(slength, 32))
              
                          // save new length
                              sstore(_preBytes.slot, add(mul(newlength, 2), 1))
              
                          // Copy over the first `submod` bytes of the new data as in
                          // case 1 above.
                              let slengthmod := mod(slength, 32)
                              let mlengthmod := mod(mlength, 32)
                              let submod := sub(32, slengthmod)
                              let mc := add(_postBytes, submod)
                              let end := add(_postBytes, mlength)
                              let mask := sub(exp(0x100, submod), 1)
              
                              sstore(sc, add(sload(sc), and(mload(mc), mask)))
              
                              for {
                                  sc := add(sc, 1)
                                  mc := add(mc, 0x20)
                              } lt(mc, end) {
                                  sc := add(sc, 1)
                                  mc := add(mc, 0x20)
                              } {
                                  sstore(sc, mload(mc))
                              }
              
                              mask := exp(0x100, sub(mc, end))
              
                              sstore(sc, mul(div(mload(mc), mask), mask))
                          }
                      }
                  }
              
                  function slice(
                      bytes memory _bytes,
                      uint256 _start,
                      uint256 _length
                  )
                  internal
                  pure
                  returns (bytes memory)
                  {
                      require(_length + 31 >= _length, "slice_overflow");
                      require(_bytes.length >= _start + _length, "slice_outOfBounds");
              
                      bytes memory tempBytes;
              
                      assembly {
                          switch iszero(_length)
                          case 0 {
                          // Get a location of some free memory and store it in tempBytes as
                          // Solidity does for memory variables.
                              tempBytes := mload(0x40)
              
                          // The first word of the slice result is potentially a partial
                          // word read from the original array. To read it, we calculate
                          // the length of that partial word and start copying that many
                          // bytes into the array. The first word we copy will start with
                          // data we don't care about, but the last `lengthmod` bytes will
                          // land at the beginning of the contents of the new array. When
                          // we're done copying, we overwrite the full first word with
                          // the actual length of the slice.
                              let lengthmod := and(_length, 31)
              
                          // The multiplication in the next line is necessary
                          // because when slicing multiples of 32 bytes (lengthmod == 0)
                          // the following copy loop was copying the origin's length
                          // and then ending prematurely not copying everything it should.
                              let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                              let end := add(mc, _length)
              
                              for {
                              // The multiplication in the next line has the same exact purpose
                              // as the one above.
                                  let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                              } lt(mc, end) {
                                  mc := add(mc, 0x20)
                                  cc := add(cc, 0x20)
                              } {
                                  mstore(mc, mload(cc))
                              }
              
                              mstore(tempBytes, _length)
              
                          //update free-memory pointer
                          //allocating the array padded to 32 bytes like the compiler does now
                              mstore(0x40, and(add(mc, 31), not(31)))
                          }
                          //if we want a zero-length slice let's just return a zero-length array
                          default {
                              tempBytes := mload(0x40)
                          //zero out the 32 bytes slice we are about to return
                          //we need to do it because Solidity does not garbage collect
                              mstore(tempBytes, 0)
              
                              mstore(0x40, add(tempBytes, 0x20))
                          }
                      }
              
                      return tempBytes;
                  }
              
                  function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
                      require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
                      address tempAddress;
              
                      assembly {
                          tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
                      }
              
                      return tempAddress;
                  }
              
                  function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
                      require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
                      uint8 tempUint;
              
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x1), _start))
                      }
              
                      return tempUint;
                  }
              
                  function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
                      require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
                      uint16 tempUint;
              
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x2), _start))
                      }
              
                      return tempUint;
                  }
              
                  function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) {
                      require(_start + 3 >= _start, 'toUint24_overflow');
                      require(_bytes.length >= _start + 3, 'toUint24_outOfBounds');
                      uint24 tempUint;
              
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x3), _start))
                      }
              
                      return tempUint;
                  }
              
                  function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
                      require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
                      uint32 tempUint;
              
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x4), _start))
                      }
              
                      return tempUint;
                  }
              
                  function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
                      require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
                      uint64 tempUint;
              
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x8), _start))
                      }
              
                      return tempUint;
                  }
              
                  function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
                      require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
                      uint96 tempUint;
              
                      assembly {
                          tempUint := mload(add(add(_bytes, 0xc), _start))
                      }
              
                      return tempUint;
                  }
              
                  function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
                      require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
                      uint128 tempUint;
              
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x10), _start))
                      }
              
                      return tempUint;
                  }
              
                  function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
                      require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
                      uint256 tempUint;
              
                      assembly {
                          tempUint := mload(add(add(_bytes, 0x20), _start))
                      }
              
                      return tempUint;
                  }
              
                  function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
                      require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
                      bytes32 tempBytes32;
              
                      assembly {
                          tempBytes32 := mload(add(add(_bytes, 0x20), _start))
                      }
              
                      return tempBytes32;
                  }
              
                  function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
                      bool success = true;
              
                      assembly {
                          let length := mload(_preBytes)
              
                      // if lengths don't match the arrays are not equal
                          switch eq(length, mload(_postBytes))
                          case 1 {
                          // cb is a circuit breaker in the for loop since there's
                          //  no said feature for inline assembly loops
                          // cb = 1 - don't breaker
                          // cb = 0 - break
                              let cb := 1
              
                              let mc := add(_preBytes, 0x20)
                              let end := add(mc, length)
              
                              for {
                                  let cc := add(_postBytes, 0x20)
                              // the next line is the loop condition:
                              // while(uint256(mc < end) + cb == 2)
                              } eq(add(lt(mc, end), cb), 2) {
                                  mc := add(mc, 0x20)
                                  cc := add(cc, 0x20)
                              } {
                              // if any of these checks fails then arrays are not equal
                                  if iszero(eq(mload(mc), mload(cc))) {
                                  // unsuccess:
                                      success := 0
                                      cb := 0
                                  }
                              }
                          }
                          default {
                          // unsuccess:
                              success := 0
                          }
                      }
              
                      return success;
                  }
              
                  function equalStorage(
                      bytes storage _preBytes,
                      bytes memory _postBytes
                  )
                  internal
                  view
                  returns (bool)
                  {
                      bool success = true;
              
                      assembly {
                      // we know _preBytes_offset is 0
                          let fslot := sload(_preBytes.slot)
                      // Decode the length of the stored array like in concatStorage().
                          let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
                          let mlength := mload(_postBytes)
              
                      // if lengths don't match the arrays are not equal
                          switch eq(slength, mlength)
                          case 1 {
                          // slength can contain both the length and contents of the array
                          // if length < 32 bytes so let's prepare for that
                          // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                              if iszero(iszero(slength)) {
                                  switch lt(slength, 32)
                                  case 1 {
                                  // blank the last byte which is the length
                                      fslot := mul(div(fslot, 0x100), 0x100)
              
                                      if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                                      // unsuccess:
                                          success := 0
                                      }
                                  }
                                  default {
                                  // cb is a circuit breaker in the for loop since there's
                                  //  no said feature for inline assembly loops
                                  // cb = 1 - don't breaker
                                  // cb = 0 - break
                                      let cb := 1
              
                                  // get the keccak hash to get the contents of the array
                                      mstore(0x0, _preBytes.slot)
                                      let sc := keccak256(0x0, 0x20)
              
                                      let mc := add(_postBytes, 0x20)
                                      let end := add(mc, mlength)
              
                                  // the next line is the loop condition:
                                  // while(uint256(mc < end) + cb == 2)
                                      for {} eq(add(lt(mc, end), cb), 2) {
                                          sc := add(sc, 1)
                                          mc := add(mc, 0x20)
                                      } {
                                          if iszero(eq(sload(sc), mload(mc))) {
                                          // unsuccess:
                                              success := 0
                                              cb := 0
                                          }
                                      }
                                  }
                              }
                          }
                          default {
                          // unsuccess:
                              success := 0
                          }
                      }
              
                      return success;
                  }
              }
              
              // File: contracts/BIFI/infra/SimpleSwapper.sol
              
              
              pragma solidity 0.8.19;
              
              
              contract SimpleSwapper {
                  using SafeERC20 for IERC20;
                  using BytesLib for bytes;
              
                  struct SwapInfo {
                      address router;
                      bytes data;
                      uint256 amountIndex;
                  }
              
                  mapping(address => mapping(address => SwapInfo)) public swapInfo;
              
                  address public native;
                  address public keeper;
                  address public deployer;
              
                  constructor(address _native, address _keeper) {
                      native = _native;
                      keeper = _keeper;
                      deployer = msg.sender;
                  }
              
                  modifier onlyManager() {
                      require(msg.sender == deployer || msg.sender == keeper, "!manager");
                      _;
                  }
              
                  error NoSwapData(address fromToken, address toToken);
                  error SwapFailed(address router, bytes data);
              
                  event Swap(address indexed caller, address indexed fromToken, address indexed toToken, uint256 amountIn, uint256 amountOut);
                  event SetSwapInfo(address indexed fromToken, address indexed toToken, SwapInfo swapInfo);
              
                  function swap(address _fromToken, address _toToken, uint256 _amountIn) external returns (uint256 amountOut) {
                      IERC20(_fromToken).safeTransferFrom(msg.sender, address(this), _amountIn);
                      _executeSwap(_fromToken, _toToken, _amountIn);
                      amountOut = IERC20(_toToken).balanceOf(address(this));
                      IERC20(_toToken).safeTransfer(msg.sender, amountOut);
                      emit Swap(msg.sender, _fromToken, _toToken, _amountIn, amountOut);
                  }
              
                  function _executeSwap(address _fromToken, address _toToken, uint256 _amountIn) private {
                      SwapInfo memory swapData = swapInfo[_fromToken][_toToken];
                      address router = swapData.router;
                      if (router == address(0)) revert NoSwapData(_fromToken, _toToken);
                      bytes memory data = swapData.data;
              
                      data = _insertData(data, swapData.amountIndex, abi.encode(_amountIn));
              
                      _approveTokenIfNeeded(_fromToken, router);
                      (bool success,) = router.call(data);
                      if (!success) revert SwapFailed(router, data);
                  }
              
                  function _insertData(bytes memory _data, uint256 _index, bytes memory _newData) private pure returns (bytes memory data) {
                      data = bytes.concat(
                          bytes.concat(
                              _data.slice(0, _index),
                              _newData
                          ),
                          _data.slice(_index + 32, _data.length - (_index + 32))
                      );
                  }
              
                  function setSwapInfo(address _fromToken, address _toToken, SwapInfo calldata _swapInfo) external onlyManager {
                      swapInfo[_fromToken][_toToken] = _swapInfo;
                      emit SetSwapInfo(_fromToken, _toToken, _swapInfo);
                  }
              
                  function setSwapInfos(address[] calldata _fromTokens, address[] calldata _toTokens, SwapInfo[] calldata _swapInfos) external onlyManager {
                      uint256 tokenLength = _fromTokens.length;
                      for (uint i; i < tokenLength;) {
                          swapInfo[_fromTokens[i]][_toTokens[i]] = _swapInfos[i];
                          emit SetSwapInfo(_fromTokens[i], _toTokens[i], _swapInfos[i]);
                          unchecked {++i;}
                      }
                  }
              
                  function _approveTokenIfNeeded(address token, address spender) private {
                      if (IERC20(token).allowance(address(this), spender) == 0) {
                          IERC20(token).safeApprove(spender, type(uint256).max);
                      }
                  }
              
                  function fromNative(address _token) external view returns (address router, bytes memory data, uint256 amountIndex) {
                      router = swapInfo[native][_token].router;
                      data = swapInfo[native][_token].data;
                      amountIndex = swapInfo[native][_token].amountIndex;
                  }
              
                  function toNative(address _token) external view returns (address router, bytes memory data, uint256 amountIndex) {
                      router = swapInfo[_token][native].router;
                      data = swapInfo[_token][native].data;
                      amountIndex = swapInfo[_token][native].amountIndex;
                  }
              
                  function setKeeper(address _keeper) external onlyManager {
                      keeper = _keeper;
                  }
              
                  function renounceDeployer() public onlyManager {
                      deployer = address(0);
                  }
              }

              File 5 of 30: WETH9
              // Copyright (C) 2015, 2016, 2017 Dapphub
              
              // This program is free software: you can redistribute it and/or modify
              // it under the terms of the GNU General Public License as published by
              // the Free Software Foundation, either version 3 of the License, or
              // (at your option) any later version.
              
              // This program is distributed in the hope that it will be useful,
              // but WITHOUT ANY WARRANTY; without even the implied warranty of
              // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
              // GNU General Public License for more details.
              
              // You should have received a copy of the GNU General Public License
              // along with this program.  If not, see <http://www.gnu.org/licenses/>.
              
              pragma solidity ^0.4.18;
              
              contract WETH9 {
                  string public name     = "Wrapped Ether";
                  string public symbol   = "WETH";
                  uint8  public decimals = 18;
              
                  event  Approval(address indexed src, address indexed guy, uint wad);
                  event  Transfer(address indexed src, address indexed dst, uint wad);
                  event  Deposit(address indexed dst, uint wad);
                  event  Withdrawal(address indexed src, uint wad);
              
                  mapping (address => uint)                       public  balanceOf;
                  mapping (address => mapping (address => uint))  public  allowance;
              
                  function() public payable {
                      deposit();
                  }
                  function deposit() public payable {
                      balanceOf[msg.sender] += msg.value;
                      Deposit(msg.sender, msg.value);
                  }
                  function withdraw(uint wad) public {
                      require(balanceOf[msg.sender] >= wad);
                      balanceOf[msg.sender] -= wad;
                      msg.sender.transfer(wad);
                      Withdrawal(msg.sender, wad);
                  }
              
                  function totalSupply() public view returns (uint) {
                      return this.balance;
                  }
              
                  function approve(address guy, uint wad) public returns (bool) {
                      allowance[msg.sender][guy] = wad;
                      Approval(msg.sender, guy, wad);
                      return true;
                  }
              
                  function transfer(address dst, uint wad) public returns (bool) {
                      return transferFrom(msg.sender, dst, wad);
                  }
              
                  function transferFrom(address src, address dst, uint wad)
                      public
                      returns (bool)
                  {
                      require(balanceOf[src] >= wad);
              
                      if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                          require(allowance[src][msg.sender] >= wad);
                          allowance[src][msg.sender] -= wad;
                      }
              
                      balanceOf[src] -= wad;
                      balanceOf[dst] += wad;
              
                      Transfer(src, dst, wad);
              
                      return true;
                  }
              }
              
              
              /*
                                  GNU GENERAL PUBLIC LICENSE
                                     Version 3, 29 June 2007
              
               Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
               Everyone is permitted to copy and distribute verbatim copies
               of this license document, but changing it is not allowed.
              
                                          Preamble
              
                The GNU General Public License is a free, copyleft license for
              software and other kinds of works.
              
                The licenses for most software and other practical works are designed
              to take away your freedom to share and change the works.  By contrast,
              the GNU General Public License is intended to guarantee your freedom to
              share and change all versions of a program--to make sure it remains free
              software for all its users.  We, the Free Software Foundation, use the
              GNU General Public License for most of our software; it applies also to
              any other work released this way by its authors.  You can apply it to
              your programs, too.
              
                When we speak of free software, we are referring to freedom, not
              price.  Our General Public Licenses are designed to make sure that you
              have the freedom to distribute copies of free software (and charge for
              them if you wish), that you receive source code or can get it if you
              want it, that you can change the software or use pieces of it in new
              free programs, and that you know you can do these things.
              
                To protect your rights, we need to prevent others from denying you
              these rights or asking you to surrender the rights.  Therefore, you have
              certain responsibilities if you distribute copies of the software, or if
              you modify it: responsibilities to respect the freedom of others.
              
                For example, if you distribute copies of such a program, whether
              gratis or for a fee, you must pass on to the recipients the same
              freedoms that you received.  You must make sure that they, too, receive
              or can get the source code.  And you must show them these terms so they
              know their rights.
              
                Developers that use the GNU GPL protect your rights with two steps:
              (1) assert copyright on the software, and (2) offer you this License
              giving you legal permission to copy, distribute and/or modify it.
              
                For the developers' and authors' protection, the GPL clearly explains
              that there is no warranty for this free software.  For both users' and
              authors' sake, the GPL requires that modified versions be marked as
              changed, so that their problems will not be attributed erroneously to
              authors of previous versions.
              
                Some devices are designed to deny users access to install or run
              modified versions of the software inside them, although the manufacturer
              can do so.  This is fundamentally incompatible with the aim of
              protecting users' freedom to change the software.  The systematic
              pattern of such abuse occurs in the area of products for individuals to
              use, which is precisely where it is most unacceptable.  Therefore, we
              have designed this version of the GPL to prohibit the practice for those
              products.  If such problems arise substantially in other domains, we
              stand ready to extend this provision to those domains in future versions
              of the GPL, as needed to protect the freedom of users.
              
                Finally, every program is threatened constantly by software patents.
              States should not allow patents to restrict development and use of
              software on general-purpose computers, but in those that do, we wish to
              avoid the special danger that patents applied to a free program could
              make it effectively proprietary.  To prevent this, the GPL assures that
              patents cannot be used to render the program non-free.
              
                The precise terms and conditions for copying, distribution and
              modification follow.
              
                                     TERMS AND CONDITIONS
              
                0. Definitions.
              
                "This License" refers to version 3 of the GNU General Public License.
              
                "Copyright" also means copyright-like laws that apply to other kinds of
              works, such as semiconductor masks.
              
                "The Program" refers to any copyrightable work licensed under this
              License.  Each licensee is addressed as "you".  "Licensees" and
              "recipients" may be individuals or organizations.
              
                To "modify" a work means to copy from or adapt all or part of the work
              in a fashion requiring copyright permission, other than the making of an
              exact copy.  The resulting work is called a "modified version" of the
              earlier work or a work "based on" the earlier work.
              
                A "covered work" means either the unmodified Program or a work based
              on the Program.
              
                To "propagate" a work means to do anything with it that, without
              permission, would make you directly or secondarily liable for
              infringement under applicable copyright law, except executing it on a
              computer or modifying a private copy.  Propagation includes copying,
              distribution (with or without modification), making available to the
              public, and in some countries other activities as well.
              
                To "convey" a work means any kind of propagation that enables other
              parties to make or receive copies.  Mere interaction with a user through
              a computer network, with no transfer of a copy, is not conveying.
              
                An interactive user interface displays "Appropriate Legal Notices"
              to the extent that it includes a convenient and prominently visible
              feature that (1) displays an appropriate copyright notice, and (2)
              tells the user that there is no warranty for the work (except to the
              extent that warranties are provided), that licensees may convey the
              work under this License, and how to view a copy of this License.  If
              the interface presents a list of user commands or options, such as a
              menu, a prominent item in the list meets this criterion.
              
                1. Source Code.
              
                The "source code" for a work means the preferred form of the work
              for making modifications to it.  "Object code" means any non-source
              form of a work.
              
                A "Standard Interface" means an interface that either is an official
              standard defined by a recognized standards body, or, in the case of
              interfaces specified for a particular programming language, one that
              is widely used among developers working in that language.
              
                The "System Libraries" of an executable work include anything, other
              than the work as a whole, that (a) is included in the normal form of
              packaging a Major Component, but which is not part of that Major
              Component, and (b) serves only to enable use of the work with that
              Major Component, or to implement a Standard Interface for which an
              implementation is available to the public in source code form.  A
              "Major Component", in this context, means a major essential component
              (kernel, window system, and so on) of the specific operating system
              (if any) on which the executable work runs, or a compiler used to
              produce the work, or an object code interpreter used to run it.
              
                The "Corresponding Source" for a work in object code form means all
              the source code needed to generate, install, and (for an executable
              work) run the object code and to modify the work, including scripts to
              control those activities.  However, it does not include the work's
              System Libraries, or general-purpose tools or generally available free
              programs which are used unmodified in performing those activities but
              which are not part of the work.  For example, Corresponding Source
              includes interface definition files associated with source files for
              the work, and the source code for shared libraries and dynamically
              linked subprograms that the work is specifically designed to require,
              such as by intimate data communication or control flow between those
              subprograms and other parts of the work.
              
                The Corresponding Source need not include anything that users
              can regenerate automatically from other parts of the Corresponding
              Source.
              
                The Corresponding Source for a work in source code form is that
              same work.
              
                2. Basic Permissions.
              
                All rights granted under this License are granted for the term of
              copyright on the Program, and are irrevocable provided the stated
              conditions are met.  This License explicitly affirms your unlimited
              permission to run the unmodified Program.  The output from running a
              covered work is covered by this License only if the output, given its
              content, constitutes a covered work.  This License acknowledges your
              rights of fair use or other equivalent, as provided by copyright law.
              
                You may make, run and propagate covered works that you do not
              convey, without conditions so long as your license otherwise remains
              in force.  You may convey covered works to others for the sole purpose
              of having them make modifications exclusively for you, or provide you
              with facilities for running those works, provided that you comply with
              the terms of this License in conveying all material for which you do
              not control copyright.  Those thus making or running the covered works
              for you must do so exclusively on your behalf, under your direction
              and control, on terms that prohibit them from making any copies of
              your copyrighted material outside their relationship with you.
              
                Conveying under any other circumstances is permitted solely under
              the conditions stated below.  Sublicensing is not allowed; section 10
              makes it unnecessary.
              
                3. Protecting Users' Legal Rights From Anti-Circumvention Law.
              
                No covered work shall be deemed part of an effective technological
              measure under any applicable law fulfilling obligations under article
              11 of the WIPO copyright treaty adopted on 20 December 1996, or
              similar laws prohibiting or restricting circumvention of such
              measures.
              
                When you convey a covered work, you waive any legal power to forbid
              circumvention of technological measures to the extent such circumvention
              is effected by exercising rights under this License with respect to
              the covered work, and you disclaim any intention to limit operation or
              modification of the work as a means of enforcing, against the work's
              users, your or third parties' legal rights to forbid circumvention of
              technological measures.
              
                4. Conveying Verbatim Copies.
              
                You may convey verbatim copies of the Program's source code as you
              receive it, in any medium, provided that you conspicuously and
              appropriately publish on each copy an appropriate copyright notice;
              keep intact all notices stating that this License and any
              non-permissive terms added in accord with section 7 apply to the code;
              keep intact all notices of the absence of any warranty; and give all
              recipients a copy of this License along with the Program.
              
                You may charge any price or no price for each copy that you convey,
              and you may offer support or warranty protection for a fee.
              
                5. Conveying Modified Source Versions.
              
                You may convey a work based on the Program, or the modifications to
              produce it from the Program, in the form of source code under the
              terms of section 4, provided that you also meet all of these conditions:
              
                  a) The work must carry prominent notices stating that you modified
                  it, and giving a relevant date.
              
                  b) The work must carry prominent notices stating that it is
                  released under this License and any conditions added under section
                  7.  This requirement modifies the requirement in section 4 to
                  "keep intact all notices".
              
                  c) You must license the entire work, as a whole, under this
                  License to anyone who comes into possession of a copy.  This
                  License will therefore apply, along with any applicable section 7
                  additional terms, to the whole of the work, and all its parts,
                  regardless of how they are packaged.  This License gives no
                  permission to license the work in any other way, but it does not
                  invalidate such permission if you have separately received it.
              
                  d) If the work has interactive user interfaces, each must display
                  Appropriate Legal Notices; however, if the Program has interactive
                  interfaces that do not display Appropriate Legal Notices, your
                  work need not make them do so.
              
                A compilation of a covered work with other separate and independent
              works, which are not by their nature extensions of the covered work,
              and which are not combined with it such as to form a larger program,
              in or on a volume of a storage or distribution medium, is called an
              "aggregate" if the compilation and its resulting copyright are not
              used to limit the access or legal rights of the compilation's users
              beyond what the individual works permit.  Inclusion of a covered work
              in an aggregate does not cause this License to apply to the other
              parts of the aggregate.
              
                6. Conveying Non-Source Forms.
              
                You may convey a covered work in object code form under the terms
              of sections 4 and 5, provided that you also convey the
              machine-readable Corresponding Source under the terms of this License,
              in one of these ways:
              
                  a) Convey the object code in, or embodied in, a physical product
                  (including a physical distribution medium), accompanied by the
                  Corresponding Source fixed on a durable physical medium
                  customarily used for software interchange.
              
                  b) Convey the object code in, or embodied in, a physical product
                  (including a physical distribution medium), accompanied by a
                  written offer, valid for at least three years and valid for as
                  long as you offer spare parts or customer support for that product
                  model, to give anyone who possesses the object code either (1) a
                  copy of the Corresponding Source for all the software in the
                  product that is covered by this License, on a durable physical
                  medium customarily used for software interchange, for a price no
                  more than your reasonable cost of physically performing this
                  conveying of source, or (2) access to copy the
                  Corresponding Source from a network server at no charge.
              
                  c) Convey individual copies of the object code with a copy of the
                  written offer to provide the Corresponding Source.  This
                  alternative is allowed only occasionally and noncommercially, and
                  only if you received the object code with such an offer, in accord
                  with subsection 6b.
              
                  d) Convey the object code by offering access from a designated
                  place (gratis or for a charge), and offer equivalent access to the
                  Corresponding Source in the same way through the same place at no
                  further charge.  You need not require recipients to copy the
                  Corresponding Source along with the object code.  If the place to
                  copy the object code is a network server, the Corresponding Source
                  may be on a different server (operated by you or a third party)
                  that supports equivalent copying facilities, provided you maintain
                  clear directions next to the object code saying where to find the
                  Corresponding Source.  Regardless of what server hosts the
                  Corresponding Source, you remain obligated to ensure that it is
                  available for as long as needed to satisfy these requirements.
              
                  e) Convey the object code using peer-to-peer transmission, provided
                  you inform other peers where the object code and Corresponding
                  Source of the work are being offered to the general public at no
                  charge under subsection 6d.
              
                A separable portion of the object code, whose source code is excluded
              from the Corresponding Source as a System Library, need not be
              included in conveying the object code work.
              
                A "User Product" is either (1) a "consumer product", which means any
              tangible personal property which is normally used for personal, family,
              or household purposes, or (2) anything designed or sold for incorporation
              into a dwelling.  In determining whether a product is a consumer product,
              doubtful cases shall be resolved in favor of coverage.  For a particular
              product received by a particular user, "normally used" refers to a
              typical or common use of that class of product, regardless of the status
              of the particular user or of the way in which the particular user
              actually uses, or expects or is expected to use, the product.  A product
              is a consumer product regardless of whether the product has substantial
              commercial, industrial or non-consumer uses, unless such uses represent
              the only significant mode of use of the product.
              
                "Installation Information" for a User Product means any methods,
              procedures, authorization keys, or other information required to install
              and execute modified versions of a covered work in that User Product from
              a modified version of its Corresponding Source.  The information must
              suffice to ensure that the continued functioning of the modified object
              code is in no case prevented or interfered with solely because
              modification has been made.
              
                If you convey an object code work under this section in, or with, or
              specifically for use in, a User Product, and the conveying occurs as
              part of a transaction in which the right of possession and use of the
              User Product is transferred to the recipient in perpetuity or for a
              fixed term (regardless of how the transaction is characterized), the
              Corresponding Source conveyed under this section must be accompanied
              by the Installation Information.  But this requirement does not apply
              if neither you nor any third party retains the ability to install
              modified object code on the User Product (for example, the work has
              been installed in ROM).
              
                The requirement to provide Installation Information does not include a
              requirement to continue to provide support service, warranty, or updates
              for a work that has been modified or installed by the recipient, or for
              the User Product in which it has been modified or installed.  Access to a
              network may be denied when the modification itself materially and
              adversely affects the operation of the network or violates the rules and
              protocols for communication across the network.
              
                Corresponding Source conveyed, and Installation Information provided,
              in accord with this section must be in a format that is publicly
              documented (and with an implementation available to the public in
              source code form), and must require no special password or key for
              unpacking, reading or copying.
              
                7. Additional Terms.
              
                "Additional permissions" are terms that supplement the terms of this
              License by making exceptions from one or more of its conditions.
              Additional permissions that are applicable to the entire Program shall
              be treated as though they were included in this License, to the extent
              that they are valid under applicable law.  If additional permissions
              apply only to part of the Program, that part may be used separately
              under those permissions, but the entire Program remains governed by
              this License without regard to the additional permissions.
              
                When you convey a copy of a covered work, you may at your option
              remove any additional permissions from that copy, or from any part of
              it.  (Additional permissions may be written to require their own
              removal in certain cases when you modify the work.)  You may place
              additional permissions on material, added by you to a covered work,
              for which you have or can give appropriate copyright permission.
              
                Notwithstanding any other provision of this License, for material you
              add to a covered work, you may (if authorized by the copyright holders of
              that material) supplement the terms of this License with terms:
              
                  a) Disclaiming warranty or limiting liability differently from the
                  terms of sections 15 and 16 of this License; or
              
                  b) Requiring preservation of specified reasonable legal notices or
                  author attributions in that material or in the Appropriate Legal
                  Notices displayed by works containing it; or
              
                  c) Prohibiting misrepresentation of the origin of that material, or
                  requiring that modified versions of such material be marked in
                  reasonable ways as different from the original version; or
              
                  d) Limiting the use for publicity purposes of names of licensors or
                  authors of the material; or
              
                  e) Declining to grant rights under trademark law for use of some
                  trade names, trademarks, or service marks; or
              
                  f) Requiring indemnification of licensors and authors of that
                  material by anyone who conveys the material (or modified versions of
                  it) with contractual assumptions of liability to the recipient, for
                  any liability that these contractual assumptions directly impose on
                  those licensors and authors.
              
                All other non-permissive additional terms are considered "further
              restrictions" within the meaning of section 10.  If the Program as you
              received it, or any part of it, contains a notice stating that it is
              governed by this License along with a term that is a further
              restriction, you may remove that term.  If a license document contains
              a further restriction but permits relicensing or conveying under this
              License, you may add to a covered work material governed by the terms
              of that license document, provided that the further restriction does
              not survive such relicensing or conveying.
              
                If you add terms to a covered work in accord with this section, you
              must place, in the relevant source files, a statement of the
              additional terms that apply to those files, or a notice indicating
              where to find the applicable terms.
              
                Additional terms, permissive or non-permissive, may be stated in the
              form of a separately written license, or stated as exceptions;
              the above requirements apply either way.
              
                8. Termination.
              
                You may not propagate or modify a covered work except as expressly
              provided under this License.  Any attempt otherwise to propagate or
              modify it is void, and will automatically terminate your rights under
              this License (including any patent licenses granted under the third
              paragraph of section 11).
              
                However, if you cease all violation of this License, then your
              license from a particular copyright holder is reinstated (a)
              provisionally, unless and until the copyright holder explicitly and
              finally terminates your license, and (b) permanently, if the copyright
              holder fails to notify you of the violation by some reasonable means
              prior to 60 days after the cessation.
              
                Moreover, your license from a particular copyright holder is
              reinstated permanently if the copyright holder notifies you of the
              violation by some reasonable means, this is the first time you have
              received notice of violation of this License (for any work) from that
              copyright holder, and you cure the violation prior to 30 days after
              your receipt of the notice.
              
                Termination of your rights under this section does not terminate the
              licenses of parties who have received copies or rights from you under
              this License.  If your rights have been terminated and not permanently
              reinstated, you do not qualify to receive new licenses for the same
              material under section 10.
              
                9. Acceptance Not Required for Having Copies.
              
                You are not required to accept this License in order to receive or
              run a copy of the Program.  Ancillary propagation of a covered work
              occurring solely as a consequence of using peer-to-peer transmission
              to receive a copy likewise does not require acceptance.  However,
              nothing other than this License grants you permission to propagate or
              modify any covered work.  These actions infringe copyright if you do
              not accept this License.  Therefore, by modifying or propagating a
              covered work, you indicate your acceptance of this License to do so.
              
                10. Automatic Licensing of Downstream Recipients.
              
                Each time you convey a covered work, the recipient automatically
              receives a license from the original licensors, to run, modify and
              propagate that work, subject to this License.  You are not responsible
              for enforcing compliance by third parties with this License.
              
                An "entity transaction" is a transaction transferring control of an
              organization, or substantially all assets of one, or subdividing an
              organization, or merging organizations.  If propagation of a covered
              work results from an entity transaction, each party to that
              transaction who receives a copy of the work also receives whatever
              licenses to the work the party's predecessor in interest had or could
              give under the previous paragraph, plus a right to possession of the
              Corresponding Source of the work from the predecessor in interest, if
              the predecessor has it or can get it with reasonable efforts.
              
                You may not impose any further restrictions on the exercise of the
              rights granted or affirmed under this License.  For example, you may
              not impose a license fee, royalty, or other charge for exercise of
              rights granted under this License, and you may not initiate litigation
              (including a cross-claim or counterclaim in a lawsuit) alleging that
              any patent claim is infringed by making, using, selling, offering for
              sale, or importing the Program or any portion of it.
              
                11. Patents.
              
                A "contributor" is a copyright holder who authorizes use under this
              License of the Program or a work on which the Program is based.  The
              work thus licensed is called the contributor's "contributor version".
              
                A contributor's "essential patent claims" are all patent claims
              owned or controlled by the contributor, whether already acquired or
              hereafter acquired, that would be infringed by some manner, permitted
              by this License, of making, using, or selling its contributor version,
              but do not include claims that would be infringed only as a
              consequence of further modification of the contributor version.  For
              purposes of this definition, "control" includes the right to grant
              patent sublicenses in a manner consistent with the requirements of
              this License.
              
                Each contributor grants you a non-exclusive, worldwide, royalty-free
              patent license under the contributor's essential patent claims, to
              make, use, sell, offer for sale, import and otherwise run, modify and
              propagate the contents of its contributor version.
              
                In the following three paragraphs, a "patent license" is any express
              agreement or commitment, however denominated, not to enforce a patent
              (such as an express permission to practice a patent or covenant not to
              sue for patent infringement).  To "grant" such a patent license to a
              party means to make such an agreement or commitment not to enforce a
              patent against the party.
              
                If you convey a covered work, knowingly relying on a patent license,
              and the Corresponding Source of the work is not available for anyone
              to copy, free of charge and under the terms of this License, through a
              publicly available network server or other readily accessible means,
              then you must either (1) cause the Corresponding Source to be so
              available, or (2) arrange to deprive yourself of the benefit of the
              patent license for this particular work, or (3) arrange, in a manner
              consistent with the requirements of this License, to extend the patent
              license to downstream recipients.  "Knowingly relying" means you have
              actual knowledge that, but for the patent license, your conveying the
              covered work in a country, or your recipient's use of the covered work
              in a country, would infringe one or more identifiable patents in that
              country that you have reason to believe are valid.
              
                If, pursuant to or in connection with a single transaction or
              arrangement, you convey, or propagate by procuring conveyance of, a
              covered work, and grant a patent license to some of the parties
              receiving the covered work authorizing them to use, propagate, modify
              or convey a specific copy of the covered work, then the patent license
              you grant is automatically extended to all recipients of the covered
              work and works based on it.
              
                A patent license is "discriminatory" if it does not include within
              the scope of its coverage, prohibits the exercise of, or is
              conditioned on the non-exercise of one or more of the rights that are
              specifically granted under this License.  You may not convey a covered
              work if you are a party to an arrangement with a third party that is
              in the business of distributing software, under which you make payment
              to the third party based on the extent of your activity of conveying
              the work, and under which the third party grants, to any of the
              parties who would receive the covered work from you, a discriminatory
              patent license (a) in connection with copies of the covered work
              conveyed by you (or copies made from those copies), or (b) primarily
              for and in connection with specific products or compilations that
              contain the covered work, unless you entered into that arrangement,
              or that patent license was granted, prior to 28 March 2007.
              
                Nothing in this License shall be construed as excluding or limiting
              any implied license or other defenses to infringement that may
              otherwise be available to you under applicable patent law.
              
                12. No Surrender of Others' Freedom.
              
                If conditions are imposed on you (whether by court order, agreement or
              otherwise) that contradict the conditions of this License, they do not
              excuse you from the conditions of this License.  If you cannot convey a
              covered work so as to satisfy simultaneously your obligations under this
              License and any other pertinent obligations, then as a consequence you may
              not convey it at all.  For example, if you agree to terms that obligate you
              to collect a royalty for further conveying from those to whom you convey
              the Program, the only way you could satisfy both those terms and this
              License would be to refrain entirely from conveying the Program.
              
                13. Use with the GNU Affero General Public License.
              
                Notwithstanding any other provision of this License, you have
              permission to link or combine any covered work with a work licensed
              under version 3 of the GNU Affero General Public License into a single
              combined work, and to convey the resulting work.  The terms of this
              License will continue to apply to the part which is the covered work,
              but the special requirements of the GNU Affero General Public License,
              section 13, concerning interaction through a network will apply to the
              combination as such.
              
                14. Revised Versions of this License.
              
                The Free Software Foundation may publish revised and/or new versions of
              the GNU General Public License from time to time.  Such new versions will
              be similar in spirit to the present version, but may differ in detail to
              address new problems or concerns.
              
                Each version is given a distinguishing version number.  If the
              Program specifies that a certain numbered version of the GNU General
              Public License "or any later version" applies to it, you have the
              option of following the terms and conditions either of that numbered
              version or of any later version published by the Free Software
              Foundation.  If the Program does not specify a version number of the
              GNU General Public License, you may choose any version ever published
              by the Free Software Foundation.
              
                If the Program specifies that a proxy can decide which future
              versions of the GNU General Public License can be used, that proxy's
              public statement of acceptance of a version permanently authorizes you
              to choose that version for the Program.
              
                Later license versions may give you additional or different
              permissions.  However, no additional obligations are imposed on any
              author or copyright holder as a result of your choosing to follow a
              later version.
              
                15. Disclaimer of Warranty.
              
                THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
              APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
              HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
              OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
              THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
              PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
              IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
              ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
              
                16. Limitation of Liability.
              
                IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
              WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
              THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
              GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
              USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
              DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
              PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
              EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
              SUCH DAMAGES.
              
                17. Interpretation of Sections 15 and 16.
              
                If the disclaimer of warranty and limitation of liability provided
              above cannot be given local legal effect according to their terms,
              reviewing courts shall apply local law that most closely approximates
              an absolute waiver of all civil liability in connection with the
              Program, unless a warranty or assumption of liability accompanies a
              copy of the Program in return for a fee.
              
                                   END OF TERMS AND CONDITIONS
              
                          How to Apply These Terms to Your New Programs
              
                If you develop a new program, and you want it to be of the greatest
              possible use to the public, the best way to achieve this is to make it
              free software which everyone can redistribute and change under these terms.
              
                To do so, attach the following notices to the program.  It is safest
              to attach them to the start of each source file to most effectively
              state the exclusion of warranty; and each file should have at least
              the "copyright" line and a pointer to where the full notice is found.
              
                  <one line to give the program's name and a brief idea of what it does.>
                  Copyright (C) <year>  <name of author>
              
                  This program is free software: you can redistribute it and/or modify
                  it under the terms of the GNU General Public License as published by
                  the Free Software Foundation, either version 3 of the License, or
                  (at your option) any later version.
              
                  This program is distributed in the hope that it will be useful,
                  but WITHOUT ANY WARRANTY; without even the implied warranty of
                  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                  GNU General Public License for more details.
              
                  You should have received a copy of the GNU General Public License
                  along with this program.  If not, see <http://www.gnu.org/licenses/>.
              
              Also add information on how to contact you by electronic and paper mail.
              
                If the program does terminal interaction, make it output a short
              notice like this when it starts in an interactive mode:
              
                  <program>  Copyright (C) <year>  <name of author>
                  This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
                  This is free software, and you are welcome to redistribute it
                  under certain conditions; type `show c' for details.
              
              The hypothetical commands `show w' and `show c' should show the appropriate
              parts of the General Public License.  Of course, your program's commands
              might be different; for a GUI interface, you would use an "about box".
              
                You should also get your employer (if you work as a programmer) or school,
              if any, to sign a "copyright disclaimer" for the program, if necessary.
              For more information on this, and how to apply and follow the GNU GPL, see
              <http://www.gnu.org/licenses/>.
              
                The GNU General Public License does not permit incorporating your program
              into proprietary programs.  If your program is a subroutine library, you
              may consider it more useful to permit linking proprietary applications with
              the library.  If this is what you want to do, use the GNU Lesser General
              Public License instead of this License.  But first, please read
              <http://www.gnu.org/philosophy/why-not-lgpl.html>.
              
              */

              File 6 of 30: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
              pragma solidity ^0.8.20;
              import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
              import {ERC1967Proxy} from "../ERC1967/ERC1967Proxy.sol";
              import {IERC1967} from "../../interfaces/IERC1967.sol";
              import {ProxyAdmin} from "./ProxyAdmin.sol";
              /**
               * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
               * does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch
               * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
               * include them in the ABI so this interface must be used to interact with it.
               */
              interface ITransparentUpgradeableProxy is IERC1967 {
                  function upgradeToAndCall(address, bytes calldata) external payable;
              }
              /**
               * @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to
               * the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating
               * the proxy admin cannot fallback to the target implementation.
               *
               * These properties mean that the admin account can only be used for upgrading the proxy, so it's best if it's a
               * dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to
               * call a function from the proxy implementation. For this reason, the proxy deploys an instance of {ProxyAdmin} and
               * allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative
               * interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership.
               *
               * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
               * inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch
               * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
               * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
               * implementation.
               *
               * NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a
               * meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract.
               *
               * IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an
               * immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be
               * overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an
               * undesirable state where the admin slot is different from the actual admin.
               *
               * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the
               * compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new
               * function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This
               * could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  // An immutable address for the admin to avoid unnecessary SLOADs before each call
                  // at the expense of removing the ability to change the admin once it's set.
                  // This is acceptable if the admin is always a ProxyAdmin instance or similar contract
                  // with its own ability to transfer the permissions to another account.
                  address private immutable _admin;
                  /**
                   * @dev The proxy caller is the current admin, and can't fallback to the proxy target.
                   */
                  error ProxyDeniedAdminAccess();
                  /**
                   * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
                   * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
                   * {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      _admin = address(new ProxyAdmin(initialOwner));
                      // Set the storage value and emit an event for ERC-1967 compatibility
                      ERC1967Utils.changeAdmin(_proxyAdmin());
                  }
                  /**
                   * @dev Returns the admin of this proxy.
                   */
                  function _proxyAdmin() internal virtual returns (address) {
                      return _admin;
                  }
                  /**
                   * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior.
                   */
                  function _fallback() internal virtual override {
                      if (msg.sender == _proxyAdmin()) {
                          if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
                              revert ProxyDeniedAdminAccess();
                          } else {
                              _dispatchUpgradeToAndCall();
                          }
                      } else {
                          super._fallback();
                      }
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  function _dispatchUpgradeToAndCall() private {
                      (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
                      ERC1967Utils.upgradeToAndCall(newImplementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
              pragma solidity ^0.8.20;
              import {IBeacon} from "../beacon/IBeacon.sol";
              import {Address} from "../../utils/Address.sol";
              import {StorageSlot} from "../../utils/StorageSlot.sol";
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               */
              library ERC1967Utils {
                  // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
                  // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
                  /**
                   * @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);
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev The `implementation` of the proxy is invalid.
                   */
                  error ERC1967InvalidImplementation(address implementation);
                  /**
                   * @dev The `admin` of the proxy is invalid.
                   */
                  error ERC1967InvalidAdmin(address admin);
                  /**
                   * @dev The `beacon` of the proxy is invalid.
                   */
                  error ERC1967InvalidBeacon(address beacon);
                  /**
                   * @dev An upgrade function sees `msg.value > 0` that may be lost.
                   */
                  error ERC1967NonPayable();
                  /**
                   * @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 {
                      if (newImplementation.code.length == 0) {
                          revert ERC1967InvalidImplementation(newImplementation);
                      }
                      StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Performs implementation upgrade with additional setup call if data is nonempty.
                   * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                   * to avoid stuck value in the contract.
                   *
                   * Emits an {IERC1967-Upgraded} event.
                   */
                  function upgradeToAndCall(address newImplementation, bytes memory data) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                      if (data.length > 0) {
                          Address.functionDelegateCall(newImplementation, data);
                      } else {
                          _checkNonPayable();
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Returns the current admin.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
                   * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function 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 {
                      if (newAdmin == address(0)) {
                          revert ERC1967InvalidAdmin(address(0));
                      }
                      StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {IERC1967-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 the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  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 {
                      if (newBeacon.code.length == 0) {
                          revert ERC1967InvalidBeacon(newBeacon);
                      }
                      StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
                      address beaconImplementation = IBeacon(newBeacon).implementation();
                      if (beaconImplementation.code.length == 0) {
                          revert ERC1967InvalidImplementation(beaconImplementation);
                      }
                  }
                  /**
                   * @dev Change the beacon and trigger a setup call if data is nonempty.
                   * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                   * to avoid stuck value in the contract.
                   *
                   * Emits an {IERC1967-BeaconUpgraded} event.
                   *
                   * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
                   * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
                   * efficiency.
                   */
                  function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      } else {
                          _checkNonPayable();
                      }
                  }
                  /**
                   * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
                   * if an upgrade doesn't perform an initialization call.
                   */
                  function _checkNonPayable() private {
                      if (msg.value > 0) {
                          revert ERC1967NonPayable();
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.20;
              import {Proxy} from "../Proxy.sol";
              import {ERC1967Utils} from "./ERC1967Utils.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 {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
                   * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
                   *
                   * Requirements:
                   *
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  constructor(address implementation, bytes memory _data) payable {
                      ERC1967Utils.upgradeToAndCall(implementation, _data);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
                   * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function _implementation() internal view virtual override returns (address) {
                      return ERC1967Utils.getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               */
              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 v5.0.0) (proxy/transparent/ProxyAdmin.sol)
              pragma solidity ^0.8.20;
              import {ITransparentUpgradeableProxy} from "./TransparentUpgradeableProxy.sol";
              import {Ownable} from "../../access/Ownable.sol";
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
                  /**
                   * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address)`
                   * and `upgradeAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
                   * while `upgradeAndCall` will invoke the `receive` function if the second argument is the empty byte string.
                   * If the getter returns `"5.0.0"`, only `upgradeAndCall(address,bytes)` is present, and the second argument must
                   * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
                   * during an upgrade.
                   */
                  string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
                  /**
                   * @dev Sets the initial owner who can perform upgrades.
                   */
                  constructor(address initialOwner) Ownable(initialOwner) {}
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
                   * See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  function upgradeAndCall(
                      ITransparentUpgradeableProxy proxy,
                      address implementation,
                      bytes memory data
                  ) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.20;
              /**
               * @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.
                   *
                   * {UpgradeableBeacon} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev The ETH balance of the account is not enough to perform the operation.
                   */
                  error AddressInsufficientBalance(address account);
                  /**
                   * @dev There's no code at `target` (it is not a contract).
                   */
                  error AddressEmptyCode(address target);
                  /**
                   * @dev A call to an address target failed. The target may have reverted.
                   */
                  error FailedInnerCall();
                  /**
                   * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      if (address(this).balance < amount) {
                          revert AddressInsufficientBalance(address(this));
                      }
                      (bool success, ) = recipient.call{value: amount}("");
                      if (!success) {
                          revert FailedInnerCall();
                      }
                  }
                  /**
                   * @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 or custom error, it is bubbled
                   * up by this function (like regular Solidity function calls). However, if
                   * the call reverted with no returned reason, this function reverts with a
                   * {FailedInnerCall} error.
                   *
                   * 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.
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0);
                  }
                  /**
                   * @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`.
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      if (address(this).balance < value) {
                          revert AddressInsufficientBalance(address(this));
                      }
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                   * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
                   * unsuccessful call.
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata
                  ) internal view returns (bytes memory) {
                      if (!success) {
                          _revert(returndata);
                      } else {
                          // only check if target is a contract if the call was successful and the return data is empty
                          // otherwise we already know that it was a contract
                          if (returndata.length == 0 && target.code.length == 0) {
                              revert AddressEmptyCode(target);
                          }
                          return returndata;
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                   * revert reason or with a default {FailedInnerCall} error.
                   */
                  function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                      if (!success) {
                          _revert(returndata);
                      } else {
                          return returndata;
                      }
                  }
                  /**
                   * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
                   */
                  function _revert(bytes memory returndata) 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 FailedInnerCall();
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.20;
              /**
               * @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(newImplementation.code.length > 0);
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               */
              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 (last updated v5.0.0) (proxy/Proxy.sol)
              pragma solidity ^0.8.20;
              /**
               * @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 {
                      _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();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
              pragma solidity ^0.8.20;
              import {Context} from "../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.
               *
               * The initial owner is set to the address provided by the deployer. 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;
                  /**
                   * @dev The caller account is not authorized to perform an operation.
                   */
                  error OwnableUnauthorizedAccount(address account);
                  /**
                   * @dev The owner is not a valid owner account. (eg. `address(0)`)
                   */
                  error OwnableInvalidOwner(address owner);
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
                   */
                  constructor(address initialOwner) {
                      if (initialOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _transferOwnership(initialOwner);
                  }
                  /**
                   * @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 {
                      if (owner() != _msgSender()) {
                          revert OwnableUnauthorizedAccount(_msgSender());
                      }
                  }
                  /**
                   * @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 {
                      if (newOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _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 v5.0.1) (utils/Context.sol)
              pragma solidity ^0.8.20;
              /**
               * @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;
                  }
              }
              

              File 7 of 30: UniswapV3Pool
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity =0.7.6;
              import './interfaces/IUniswapV3Pool.sol';
              import './NoDelegateCall.sol';
              import './libraries/LowGasSafeMath.sol';
              import './libraries/SafeCast.sol';
              import './libraries/Tick.sol';
              import './libraries/TickBitmap.sol';
              import './libraries/Position.sol';
              import './libraries/Oracle.sol';
              import './libraries/FullMath.sol';
              import './libraries/FixedPoint128.sol';
              import './libraries/TransferHelper.sol';
              import './libraries/TickMath.sol';
              import './libraries/LiquidityMath.sol';
              import './libraries/SqrtPriceMath.sol';
              import './libraries/SwapMath.sol';
              import './interfaces/IUniswapV3PoolDeployer.sol';
              import './interfaces/IUniswapV3Factory.sol';
              import './interfaces/IERC20Minimal.sol';
              import './interfaces/callback/IUniswapV3MintCallback.sol';
              import './interfaces/callback/IUniswapV3SwapCallback.sol';
              import './interfaces/callback/IUniswapV3FlashCallback.sol';
              contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
                  using LowGasSafeMath for uint256;
                  using LowGasSafeMath for int256;
                  using SafeCast for uint256;
                  using SafeCast for int256;
                  using Tick for mapping(int24 => Tick.Info);
                  using TickBitmap for mapping(int16 => uint256);
                  using Position for mapping(bytes32 => Position.Info);
                  using Position for Position.Info;
                  using Oracle for Oracle.Observation[65535];
                  /// @inheritdoc IUniswapV3PoolImmutables
                  address public immutable override factory;
                  /// @inheritdoc IUniswapV3PoolImmutables
                  address public immutable override token0;
                  /// @inheritdoc IUniswapV3PoolImmutables
                  address public immutable override token1;
                  /// @inheritdoc IUniswapV3PoolImmutables
                  uint24 public immutable override fee;
                  /// @inheritdoc IUniswapV3PoolImmutables
                  int24 public immutable override tickSpacing;
                  /// @inheritdoc IUniswapV3PoolImmutables
                  uint128 public immutable override maxLiquidityPerTick;
                  struct Slot0 {
                      // the current price
                      uint160 sqrtPriceX96;
                      // the current tick
                      int24 tick;
                      // the most-recently updated index of the observations array
                      uint16 observationIndex;
                      // the current maximum number of observations that are being stored
                      uint16 observationCardinality;
                      // the next maximum number of observations to store, triggered in observations.write
                      uint16 observationCardinalityNext;
                      // the current protocol fee as a percentage of the swap fee taken on withdrawal
                      // represented as an integer denominator (1/x)%
                      uint8 feeProtocol;
                      // whether the pool is locked
                      bool unlocked;
                  }
                  /// @inheritdoc IUniswapV3PoolState
                  Slot0 public override slot0;
                  /// @inheritdoc IUniswapV3PoolState
                  uint256 public override feeGrowthGlobal0X128;
                  /// @inheritdoc IUniswapV3PoolState
                  uint256 public override feeGrowthGlobal1X128;
                  // accumulated protocol fees in token0/token1 units
                  struct ProtocolFees {
                      uint128 token0;
                      uint128 token1;
                  }
                  /// @inheritdoc IUniswapV3PoolState
                  ProtocolFees public override protocolFees;
                  /// @inheritdoc IUniswapV3PoolState
                  uint128 public override liquidity;
                  /// @inheritdoc IUniswapV3PoolState
                  mapping(int24 => Tick.Info) public override ticks;
                  /// @inheritdoc IUniswapV3PoolState
                  mapping(int16 => uint256) public override tickBitmap;
                  /// @inheritdoc IUniswapV3PoolState
                  mapping(bytes32 => Position.Info) public override positions;
                  /// @inheritdoc IUniswapV3PoolState
                  Oracle.Observation[65535] public override observations;
                  /// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance
                  /// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because
                  /// we use balance checks to determine the payment status of interactions such as mint, swap and flash.
                  modifier lock() {
                      require(slot0.unlocked, 'LOK');
                      slot0.unlocked = false;
                      _;
                      slot0.unlocked = true;
                  }
                  /// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner()
                  modifier onlyFactoryOwner() {
                      require(msg.sender == IUniswapV3Factory(factory).owner());
                      _;
                  }
                  constructor() {
                      int24 _tickSpacing;
                      (factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters();
                      tickSpacing = _tickSpacing;
                      maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing);
                  }
                  /// @dev Common checks for valid tick inputs.
                  function checkTicks(int24 tickLower, int24 tickUpper) private pure {
                      require(tickLower < tickUpper, 'TLU');
                      require(tickLower >= TickMath.MIN_TICK, 'TLM');
                      require(tickUpper <= TickMath.MAX_TICK, 'TUM');
                  }
                  /// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests.
                  function _blockTimestamp() internal view virtual returns (uint32) {
                      return uint32(block.timestamp); // truncation is desired
                  }
                  /// @dev Get the pool's balance of token0
                  /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
                  /// check
                  function balance0() private view returns (uint256) {
                      (bool success, bytes memory data) =
                          token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
                      require(success && data.length >= 32);
                      return abi.decode(data, (uint256));
                  }
                  /// @dev Get the pool's balance of token1
                  /// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
                  /// check
                  function balance1() private view returns (uint256) {
                      (bool success, bytes memory data) =
                          token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
                      require(success && data.length >= 32);
                      return abi.decode(data, (uint256));
                  }
                  /// @inheritdoc IUniswapV3PoolDerivedState
                  function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
                      external
                      view
                      override
                      noDelegateCall
                      returns (
                          int56 tickCumulativeInside,
                          uint160 secondsPerLiquidityInsideX128,
                          uint32 secondsInside
                      )
                  {
                      checkTicks(tickLower, tickUpper);
                      int56 tickCumulativeLower;
                      int56 tickCumulativeUpper;
                      uint160 secondsPerLiquidityOutsideLowerX128;
                      uint160 secondsPerLiquidityOutsideUpperX128;
                      uint32 secondsOutsideLower;
                      uint32 secondsOutsideUpper;
                      {
                          Tick.Info storage lower = ticks[tickLower];
                          Tick.Info storage upper = ticks[tickUpper];
                          bool initializedLower;
                          (tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = (
                              lower.tickCumulativeOutside,
                              lower.secondsPerLiquidityOutsideX128,
                              lower.secondsOutside,
                              lower.initialized
                          );
                          require(initializedLower);
                          bool initializedUpper;
                          (tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = (
                              upper.tickCumulativeOutside,
                              upper.secondsPerLiquidityOutsideX128,
                              upper.secondsOutside,
                              upper.initialized
                          );
                          require(initializedUpper);
                      }
                      Slot0 memory _slot0 = slot0;
                      if (_slot0.tick < tickLower) {
                          return (
                              tickCumulativeLower - tickCumulativeUpper,
                              secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128,
                              secondsOutsideLower - secondsOutsideUpper
                          );
                      } else if (_slot0.tick < tickUpper) {
                          uint32 time = _blockTimestamp();
                          (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
                              observations.observeSingle(
                                  time,
                                  0,
                                  _slot0.tick,
                                  _slot0.observationIndex,
                                  liquidity,
                                  _slot0.observationCardinality
                              );
                          return (
                              tickCumulative - tickCumulativeLower - tickCumulativeUpper,
                              secondsPerLiquidityCumulativeX128 -
                                  secondsPerLiquidityOutsideLowerX128 -
                                  secondsPerLiquidityOutsideUpperX128,
                              time - secondsOutsideLower - secondsOutsideUpper
                          );
                      } else {
                          return (
                              tickCumulativeUpper - tickCumulativeLower,
                              secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128,
                              secondsOutsideUpper - secondsOutsideLower
                          );
                      }
                  }
                  /// @inheritdoc IUniswapV3PoolDerivedState
                  function observe(uint32[] calldata secondsAgos)
                      external
                      view
                      override
                      noDelegateCall
                      returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
                  {
                      return
                          observations.observe(
                              _blockTimestamp(),
                              secondsAgos,
                              slot0.tick,
                              slot0.observationIndex,
                              liquidity,
                              slot0.observationCardinality
                          );
                  }
                  /// @inheritdoc IUniswapV3PoolActions
                  function increaseObservationCardinalityNext(uint16 observationCardinalityNext)
                      external
                      override
                      lock
                      noDelegateCall
                  {
                      uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event
                      uint16 observationCardinalityNextNew =
                          observations.grow(observationCardinalityNextOld, observationCardinalityNext);
                      slot0.observationCardinalityNext = observationCardinalityNextNew;
                      if (observationCardinalityNextOld != observationCardinalityNextNew)
                          emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew);
                  }
                  /// @inheritdoc IUniswapV3PoolActions
                  /// @dev not locked because it initializes unlocked
                  function initialize(uint160 sqrtPriceX96) external override {
                      require(slot0.sqrtPriceX96 == 0, 'AI');
                      int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
                      (uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());
                      slot0 = Slot0({
                          sqrtPriceX96: sqrtPriceX96,
                          tick: tick,
                          observationIndex: 0,
                          observationCardinality: cardinality,
                          observationCardinalityNext: cardinalityNext,
                          feeProtocol: 0,
                          unlocked: true
                      });
                      emit Initialize(sqrtPriceX96, tick);
                  }
                  struct ModifyPositionParams {
                      // the address that owns the position
                      address owner;
                      // the lower and upper tick of the position
                      int24 tickLower;
                      int24 tickUpper;
                      // any change in liquidity
                      int128 liquidityDelta;
                  }
                  /// @dev Effect some changes to a position
                  /// @param params the position details and the change to the position's liquidity to effect
                  /// @return position a storage pointer referencing the position with the given owner and tick range
                  /// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient
                  /// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient
                  function _modifyPosition(ModifyPositionParams memory params)
                      private
                      noDelegateCall
                      returns (
                          Position.Info storage position,
                          int256 amount0,
                          int256 amount1
                      )
                  {
                      checkTicks(params.tickLower, params.tickUpper);
                      Slot0 memory _slot0 = slot0; // SLOAD for gas optimization
                      position = _updatePosition(
                          params.owner,
                          params.tickLower,
                          params.tickUpper,
                          params.liquidityDelta,
                          _slot0.tick
                      );
                      if (params.liquidityDelta != 0) {
                          if (_slot0.tick < params.tickLower) {
                              // current tick is below the passed range; liquidity can only become in range by crossing from left to
                              // right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it
                              amount0 = SqrtPriceMath.getAmount0Delta(
                                  TickMath.getSqrtRatioAtTick(params.tickLower),
                                  TickMath.getSqrtRatioAtTick(params.tickUpper),
                                  params.liquidityDelta
                              );
                          } else if (_slot0.tick < params.tickUpper) {
                              // current tick is inside the passed range
                              uint128 liquidityBefore = liquidity; // SLOAD for gas optimization
                              // write an oracle entry
                              (slot0.observationIndex, slot0.observationCardinality) = observations.write(
                                  _slot0.observationIndex,
                                  _blockTimestamp(),
                                  _slot0.tick,
                                  liquidityBefore,
                                  _slot0.observationCardinality,
                                  _slot0.observationCardinalityNext
                              );
                              amount0 = SqrtPriceMath.getAmount0Delta(
                                  _slot0.sqrtPriceX96,
                                  TickMath.getSqrtRatioAtTick(params.tickUpper),
                                  params.liquidityDelta
                              );
                              amount1 = SqrtPriceMath.getAmount1Delta(
                                  TickMath.getSqrtRatioAtTick(params.tickLower),
                                  _slot0.sqrtPriceX96,
                                  params.liquidityDelta
                              );
                              liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);
                          } else {
                              // current tick is above the passed range; liquidity can only become in range by crossing from right to
                              // left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it
                              amount1 = SqrtPriceMath.getAmount1Delta(
                                  TickMath.getSqrtRatioAtTick(params.tickLower),
                                  TickMath.getSqrtRatioAtTick(params.tickUpper),
                                  params.liquidityDelta
                              );
                          }
                      }
                  }
                  /// @dev Gets and updates a position with the given liquidity delta
                  /// @param owner the owner of the position
                  /// @param tickLower the lower tick of the position's tick range
                  /// @param tickUpper the upper tick of the position's tick range
                  /// @param tick the current tick, passed to avoid sloads
                  function _updatePosition(
                      address owner,
                      int24 tickLower,
                      int24 tickUpper,
                      int128 liquidityDelta,
                      int24 tick
                  ) private returns (Position.Info storage position) {
                      position = positions.get(owner, tickLower, tickUpper);
                      uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization
                      uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization
                      // if we need to update the ticks, do it
                      bool flippedLower;
                      bool flippedUpper;
                      if (liquidityDelta != 0) {
                          uint32 time = _blockTimestamp();
                          (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
                              observations.observeSingle(
                                  time,
                                  0,
                                  slot0.tick,
                                  slot0.observationIndex,
                                  liquidity,
                                  slot0.observationCardinality
                              );
                          flippedLower = ticks.update(
                              tickLower,
                              tick,
                              liquidityDelta,
                              _feeGrowthGlobal0X128,
                              _feeGrowthGlobal1X128,
                              secondsPerLiquidityCumulativeX128,
                              tickCumulative,
                              time,
                              false,
                              maxLiquidityPerTick
                          );
                          flippedUpper = ticks.update(
                              tickUpper,
                              tick,
                              liquidityDelta,
                              _feeGrowthGlobal0X128,
                              _feeGrowthGlobal1X128,
                              secondsPerLiquidityCumulativeX128,
                              tickCumulative,
                              time,
                              true,
                              maxLiquidityPerTick
                          );
                          if (flippedLower) {
                              tickBitmap.flipTick(tickLower, tickSpacing);
                          }
                          if (flippedUpper) {
                              tickBitmap.flipTick(tickUpper, tickSpacing);
                          }
                      }
                      (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =
                          ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128);
                      position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
                      // clear any tick data that is no longer needed
                      if (liquidityDelta < 0) {
                          if (flippedLower) {
                              ticks.clear(tickLower);
                          }
                          if (flippedUpper) {
                              ticks.clear(tickUpper);
                          }
                      }
                  }
                  /// @inheritdoc IUniswapV3PoolActions
                  /// @dev noDelegateCall is applied indirectly via _modifyPosition
                  function mint(
                      address recipient,
                      int24 tickLower,
                      int24 tickUpper,
                      uint128 amount,
                      bytes calldata data
                  ) external override lock returns (uint256 amount0, uint256 amount1) {
                      require(amount > 0);
                      (, int256 amount0Int, int256 amount1Int) =
                          _modifyPosition(
                              ModifyPositionParams({
                                  owner: recipient,
                                  tickLower: tickLower,
                                  tickUpper: tickUpper,
                                  liquidityDelta: int256(amount).toInt128()
                              })
                          );
                      amount0 = uint256(amount0Int);
                      amount1 = uint256(amount1Int);
                      uint256 balance0Before;
                      uint256 balance1Before;
                      if (amount0 > 0) balance0Before = balance0();
                      if (amount1 > 0) balance1Before = balance1();
                      IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
                      if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
                      if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');
                      emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
                  }
                  /// @inheritdoc IUniswapV3PoolActions
                  function collect(
                      address recipient,
                      int24 tickLower,
                      int24 tickUpper,
                      uint128 amount0Requested,
                      uint128 amount1Requested
                  ) external override lock returns (uint128 amount0, uint128 amount1) {
                      // we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1}
                      Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);
                      amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;
                      amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;
                      if (amount0 > 0) {
                          position.tokensOwed0 -= amount0;
                          TransferHelper.safeTransfer(token0, recipient, amount0);
                      }
                      if (amount1 > 0) {
                          position.tokensOwed1 -= amount1;
                          TransferHelper.safeTransfer(token1, recipient, amount1);
                      }
                      emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1);
                  }
                  /// @inheritdoc IUniswapV3PoolActions
                  /// @dev noDelegateCall is applied indirectly via _modifyPosition
                  function burn(
                      int24 tickLower,
                      int24 tickUpper,
                      uint128 amount
                  ) external override lock returns (uint256 amount0, uint256 amount1) {
                      (Position.Info storage position, int256 amount0Int, int256 amount1Int) =
                          _modifyPosition(
                              ModifyPositionParams({
                                  owner: msg.sender,
                                  tickLower: tickLower,
                                  tickUpper: tickUpper,
                                  liquidityDelta: -int256(amount).toInt128()
                              })
                          );
                      amount0 = uint256(-amount0Int);
                      amount1 = uint256(-amount1Int);
                      if (amount0 > 0 || amount1 > 0) {
                          (position.tokensOwed0, position.tokensOwed1) = (
                              position.tokensOwed0 + uint128(amount0),
                              position.tokensOwed1 + uint128(amount1)
                          );
                      }
                      emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);
                  }
                  struct SwapCache {
                      // the protocol fee for the input token
                      uint8 feeProtocol;
                      // liquidity at the beginning of the swap
                      uint128 liquidityStart;
                      // the timestamp of the current block
                      uint32 blockTimestamp;
                      // the current value of the tick accumulator, computed only if we cross an initialized tick
                      int56 tickCumulative;
                      // the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick
                      uint160 secondsPerLiquidityCumulativeX128;
                      // whether we've computed and cached the above two accumulators
                      bool computedLatestObservation;
                  }
                  // the top level state of the swap, the results of which are recorded in storage at the end
                  struct SwapState {
                      // the amount remaining to be swapped in/out of the input/output asset
                      int256 amountSpecifiedRemaining;
                      // the amount already swapped out/in of the output/input asset
                      int256 amountCalculated;
                      // current sqrt(price)
                      uint160 sqrtPriceX96;
                      // the tick associated with the current price
                      int24 tick;
                      // the global fee growth of the input token
                      uint256 feeGrowthGlobalX128;
                      // amount of input token paid as protocol fee
                      uint128 protocolFee;
                      // the current liquidity in range
                      uint128 liquidity;
                  }
                  struct StepComputations {
                      // the price at the beginning of the step
                      uint160 sqrtPriceStartX96;
                      // the next tick to swap to from the current tick in the swap direction
                      int24 tickNext;
                      // whether tickNext is initialized or not
                      bool initialized;
                      // sqrt(price) for the next tick (1/0)
                      uint160 sqrtPriceNextX96;
                      // how much is being swapped in in this step
                      uint256 amountIn;
                      // how much is being swapped out
                      uint256 amountOut;
                      // how much fee is being paid in
                      uint256 feeAmount;
                  }
                  /// @inheritdoc IUniswapV3PoolActions
                  function swap(
                      address recipient,
                      bool zeroForOne,
                      int256 amountSpecified,
                      uint160 sqrtPriceLimitX96,
                      bytes calldata data
                  ) external override noDelegateCall returns (int256 amount0, int256 amount1) {
                      require(amountSpecified != 0, 'AS');
                      Slot0 memory slot0Start = slot0;
                      require(slot0Start.unlocked, 'LOK');
                      require(
                          zeroForOne
                              ? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO
                              : sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,
                          'SPL'
                      );
                      slot0.unlocked = false;
                      SwapCache memory cache =
                          SwapCache({
                              liquidityStart: liquidity,
                              blockTimestamp: _blockTimestamp(),
                              feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4),
                              secondsPerLiquidityCumulativeX128: 0,
                              tickCumulative: 0,
                              computedLatestObservation: false
                          });
                      bool exactInput = amountSpecified > 0;
                      SwapState memory state =
                          SwapState({
                              amountSpecifiedRemaining: amountSpecified,
                              amountCalculated: 0,
                              sqrtPriceX96: slot0Start.sqrtPriceX96,
                              tick: slot0Start.tick,
                              feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128,
                              protocolFee: 0,
                              liquidity: cache.liquidityStart
                          });
                      // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit
                      while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {
                          StepComputations memory step;
                          step.sqrtPriceStartX96 = state.sqrtPriceX96;
                          (step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord(
                              state.tick,
                              tickSpacing,
                              zeroForOne
                          );
                          // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds
                          if (step.tickNext < TickMath.MIN_TICK) {
                              step.tickNext = TickMath.MIN_TICK;
                          } else if (step.tickNext > TickMath.MAX_TICK) {
                              step.tickNext = TickMath.MAX_TICK;
                          }
                          // get the price for the next tick
                          step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
                          // compute values to swap to the target tick, price limit, or point where input/output amount is exhausted
                          (state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
                              state.sqrtPriceX96,
                              (zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)
                                  ? sqrtPriceLimitX96
                                  : step.sqrtPriceNextX96,
                              state.liquidity,
                              state.amountSpecifiedRemaining,
                              fee
                          );
                          if (exactInput) {
                              state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256();
                              state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256());
                          } else {
                              state.amountSpecifiedRemaining += step.amountOut.toInt256();
                              state.amountCalculated = state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256());
                          }
                          // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee
                          if (cache.feeProtocol > 0) {
                              uint256 delta = step.feeAmount / cache.feeProtocol;
                              step.feeAmount -= delta;
                              state.protocolFee += uint128(delta);
                          }
                          // update global fee tracker
                          if (state.liquidity > 0)
                              state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity);
                          // shift tick if we reached the next price
                          if (state.sqrtPriceX96 == step.sqrtPriceNextX96) {
                              // if the tick is initialized, run the tick transition
                              if (step.initialized) {
                                  // check for the placeholder value, which we replace with the actual value the first time the swap
                                  // crosses an initialized tick
                                  if (!cache.computedLatestObservation) {
                                      (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observations.observeSingle(
                                          cache.blockTimestamp,
                                          0,
                                          slot0Start.tick,
                                          slot0Start.observationIndex,
                                          cache.liquidityStart,
                                          slot0Start.observationCardinality
                                      );
                                      cache.computedLatestObservation = true;
                                  }
                                  int128 liquidityNet =
                                      ticks.cross(
                                          step.tickNext,
                                          (zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128),
                                          (zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128),
                                          cache.secondsPerLiquidityCumulativeX128,
                                          cache.tickCumulative,
                                          cache.blockTimestamp
                                      );
                                  // if we're moving leftward, we interpret liquidityNet as the opposite sign
                                  // safe because liquidityNet cannot be type(int128).min
                                  if (zeroForOne) liquidityNet = -liquidityNet;
                                  state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet);
                              }
                              state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;
                          } else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) {
                              // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved
                              state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);
                          }
                      }
                      // update tick and write an oracle entry if the tick change
                      if (state.tick != slot0Start.tick) {
                          (uint16 observationIndex, uint16 observationCardinality) =
                              observations.write(
                                  slot0Start.observationIndex,
                                  cache.blockTimestamp,
                                  slot0Start.tick,
                                  cache.liquidityStart,
                                  slot0Start.observationCardinality,
                                  slot0Start.observationCardinalityNext
                              );
                          (slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = (
                              state.sqrtPriceX96,
                              state.tick,
                              observationIndex,
                              observationCardinality
                          );
                      } else {
                          // otherwise just update the price
                          slot0.sqrtPriceX96 = state.sqrtPriceX96;
                      }
                      // update liquidity if it changed
                      if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity;
                      // update fee growth global and, if necessary, protocol fees
                      // overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees
                      if (zeroForOne) {
                          feeGrowthGlobal0X128 = state.feeGrowthGlobalX128;
                          if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee;
                      } else {
                          feeGrowthGlobal1X128 = state.feeGrowthGlobalX128;
                          if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee;
                      }
                      (amount0, amount1) = zeroForOne == exactInput
                          ? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated)
                          : (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining);
                      // do the transfers and collect payment
                      if (zeroForOne) {
                          if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1));
                          uint256 balance0Before = balance0();
                          IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data);
                          require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA');
                      } else {
                          if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0));
                          uint256 balance1Before = balance1();
                          IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data);
                          require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA');
                      }
                      emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick);
                      slot0.unlocked = true;
                  }
                  /// @inheritdoc IUniswapV3PoolActions
                  function flash(
                      address recipient,
                      uint256 amount0,
                      uint256 amount1,
                      bytes calldata data
                  ) external override lock noDelegateCall {
                      uint128 _liquidity = liquidity;
                      require(_liquidity > 0, 'L');
                      uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6);
                      uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6);
                      uint256 balance0Before = balance0();
                      uint256 balance1Before = balance1();
                      if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0);
                      if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1);
                      IUniswapV3FlashCallback(msg.sender).uniswapV3FlashCallback(fee0, fee1, data);
                      uint256 balance0After = balance0();
                      uint256 balance1After = balance1();
                      require(balance0Before.add(fee0) <= balance0After, 'F0');
                      require(balance1Before.add(fee1) <= balance1After, 'F1');
                      // sub is safe because we know balanceAfter is gt balanceBefore by at least fee
                      uint256 paid0 = balance0After - balance0Before;
                      uint256 paid1 = balance1After - balance1Before;
                      if (paid0 > 0) {
                          uint8 feeProtocol0 = slot0.feeProtocol % 16;
                          uint256 fees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0;
                          if (uint128(fees0) > 0) protocolFees.token0 += uint128(fees0);
                          feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - fees0, FixedPoint128.Q128, _liquidity);
                      }
                      if (paid1 > 0) {
                          uint8 feeProtocol1 = slot0.feeProtocol >> 4;
                          uint256 fees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1;
                          if (uint128(fees1) > 0) protocolFees.token1 += uint128(fees1);
                          feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - fees1, FixedPoint128.Q128, _liquidity);
                      }
                      emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1);
                  }
                  /// @inheritdoc IUniswapV3PoolOwnerActions
                  function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override lock onlyFactoryOwner {
                      require(
                          (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) &&
                              (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10))
                      );
                      uint8 feeProtocolOld = slot0.feeProtocol;
                      slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4);
                      emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1);
                  }
                  /// @inheritdoc IUniswapV3PoolOwnerActions
                  function collectProtocol(
                      address recipient,
                      uint128 amount0Requested,
                      uint128 amount1Requested
                  ) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) {
                      amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested;
                      amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested;
                      if (amount0 > 0) {
                          if (amount0 == protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings
                          protocolFees.token0 -= amount0;
                          TransferHelper.safeTransfer(token0, recipient, amount0);
                      }
                      if (amount1 > 0) {
                          if (amount1 == protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings
                          protocolFees.token1 -= amount1;
                          TransferHelper.safeTransfer(token1, recipient, amount1);
                      }
                      emit CollectProtocol(msg.sender, recipient, amount0, amount1);
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              import './pool/IUniswapV3PoolImmutables.sol';
              import './pool/IUniswapV3PoolState.sol';
              import './pool/IUniswapV3PoolDerivedState.sol';
              import './pool/IUniswapV3PoolActions.sol';
              import './pool/IUniswapV3PoolOwnerActions.sol';
              import './pool/IUniswapV3PoolEvents.sol';
              /// @title The interface for a Uniswap V3 Pool
              /// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
              /// to the ERC20 specification
              /// @dev The pool interface is broken up into many smaller pieces
              interface IUniswapV3Pool is
                  IUniswapV3PoolImmutables,
                  IUniswapV3PoolState,
                  IUniswapV3PoolDerivedState,
                  IUniswapV3PoolActions,
                  IUniswapV3PoolOwnerActions,
                  IUniswapV3PoolEvents
              {
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity =0.7.6;
              /// @title Prevents delegatecall to a contract
              /// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract
              abstract contract NoDelegateCall {
                  /// @dev The original address of this contract
                  address private immutable original;
                  constructor() {
                      // Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode.
                      // In other words, this variable won't change when it's checked at runtime.
                      original = address(this);
                  }
                  /// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method,
                  ///     and the use of immutable means the address bytes are copied in every place the modifier is used.
                  function checkNotDelegateCall() private view {
                      require(address(this) == original);
                  }
                  /// @notice Prevents delegatecall into the modified method
                  modifier noDelegateCall() {
                      checkNotDelegateCall();
                      _;
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.7.0;
              /// @title Optimized overflow and underflow safe math operations
              /// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost
              library LowGasSafeMath {
                  /// @notice Returns x + y, reverts if sum overflows uint256
                  /// @param x The augend
                  /// @param y The addend
                  /// @return z The sum of x and y
                  function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
                      require((z = x + y) >= x);
                  }
                  /// @notice Returns x - y, reverts if underflows
                  /// @param x The minuend
                  /// @param y The subtrahend
                  /// @return z The difference of x and y
                  function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
                      require((z = x - y) <= x);
                  }
                  /// @notice Returns x * y, reverts if overflows
                  /// @param x The multiplicand
                  /// @param y The multiplier
                  /// @return z The product of x and y
                  function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
                      require(x == 0 || (z = x * y) / x == y);
                  }
                  /// @notice Returns x + y, reverts if overflows or underflows
                  /// @param x The augend
                  /// @param y The addend
                  /// @return z The sum of x and y
                  function add(int256 x, int256 y) internal pure returns (int256 z) {
                      require((z = x + y) >= x == (y >= 0));
                  }
                  /// @notice Returns x - y, reverts if overflows or underflows
                  /// @param x The minuend
                  /// @param y The subtrahend
                  /// @return z The difference of x and y
                  function sub(int256 x, int256 y) internal pure returns (int256 z) {
                      require((z = x - y) <= x == (y >= 0));
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Safe casting methods
              /// @notice Contains methods for safely casting between types
              library SafeCast {
                  /// @notice Cast a uint256 to a uint160, revert on overflow
                  /// @param y The uint256 to be downcasted
                  /// @return z The downcasted integer, now type uint160
                  function toUint160(uint256 y) internal pure returns (uint160 z) {
                      require((z = uint160(y)) == y);
                  }
                  /// @notice Cast a int256 to a int128, revert on overflow or underflow
                  /// @param y The int256 to be downcasted
                  /// @return z The downcasted integer, now type int128
                  function toInt128(int256 y) internal pure returns (int128 z) {
                      require((z = int128(y)) == y);
                  }
                  /// @notice Cast a uint256 to a int256, revert on overflow
                  /// @param y The uint256 to be casted
                  /// @return z The casted integer, now type int256
                  function toInt256(uint256 y) internal pure returns (int256 z) {
                      require(y < 2**255);
                      z = int256(y);
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity >=0.5.0;
              import './LowGasSafeMath.sol';
              import './SafeCast.sol';
              import './TickMath.sol';
              import './LiquidityMath.sol';
              /// @title Tick
              /// @notice Contains functions for managing tick processes and relevant calculations
              library Tick {
                  using LowGasSafeMath for int256;
                  using SafeCast for int256;
                  // info stored for each initialized individual tick
                  struct Info {
                      // the total position liquidity that references this tick
                      uint128 liquidityGross;
                      // amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left),
                      int128 liquidityNet;
                      // fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
                      // only has relative meaning, not absolute — the value depends on when the tick is initialized
                      uint256 feeGrowthOutside0X128;
                      uint256 feeGrowthOutside1X128;
                      // the cumulative tick value on the other side of the tick
                      int56 tickCumulativeOutside;
                      // the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick)
                      // only has relative meaning, not absolute — the value depends on when the tick is initialized
                      uint160 secondsPerLiquidityOutsideX128;
                      // the seconds spent on the other side of the tick (relative to the current tick)
                      // only has relative meaning, not absolute — the value depends on when the tick is initialized
                      uint32 secondsOutside;
                      // true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0
                      // these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks
                      bool initialized;
                  }
                  /// @notice Derives max liquidity per tick from given tick spacing
                  /// @dev Executed within the pool constructor
                  /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing`
                  ///     e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ...
                  /// @return The max liquidity per tick
                  function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) {
                      int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing;
                      int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
                      uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1;
                      return type(uint128).max / numTicks;
                  }
                  /// @notice Retrieves fee growth data
                  /// @param self The mapping containing all tick information for initialized ticks
                  /// @param tickLower The lower tick boundary of the position
                  /// @param tickUpper The upper tick boundary of the position
                  /// @param tickCurrent The current tick
                  /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
                  /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
                  /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
                  /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
                  function getFeeGrowthInside(
                      mapping(int24 => Tick.Info) storage self,
                      int24 tickLower,
                      int24 tickUpper,
                      int24 tickCurrent,
                      uint256 feeGrowthGlobal0X128,
                      uint256 feeGrowthGlobal1X128
                  ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
                      Info storage lower = self[tickLower];
                      Info storage upper = self[tickUpper];
                      // calculate fee growth below
                      uint256 feeGrowthBelow0X128;
                      uint256 feeGrowthBelow1X128;
                      if (tickCurrent >= tickLower) {
                          feeGrowthBelow0X128 = lower.feeGrowthOutside0X128;
                          feeGrowthBelow1X128 = lower.feeGrowthOutside1X128;
                      } else {
                          feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128;
                          feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128;
                      }
                      // calculate fee growth above
                      uint256 feeGrowthAbove0X128;
                      uint256 feeGrowthAbove1X128;
                      if (tickCurrent < tickUpper) {
                          feeGrowthAbove0X128 = upper.feeGrowthOutside0X128;
                          feeGrowthAbove1X128 = upper.feeGrowthOutside1X128;
                      } else {
                          feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128;
                          feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128;
                      }
                      feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128;
                      feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128;
                  }
                  /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa
                  /// @param self The mapping containing all tick information for initialized ticks
                  /// @param tick The tick that will be updated
                  /// @param tickCurrent The current tick
                  /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left)
                  /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
                  /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
                  /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool
                  /// @param time The current block timestamp cast to a uint32
                  /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick
                  /// @param maxLiquidity The maximum liquidity allocation for a single tick
                  /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa
                  function update(
                      mapping(int24 => Tick.Info) storage self,
                      int24 tick,
                      int24 tickCurrent,
                      int128 liquidityDelta,
                      uint256 feeGrowthGlobal0X128,
                      uint256 feeGrowthGlobal1X128,
                      uint160 secondsPerLiquidityCumulativeX128,
                      int56 tickCumulative,
                      uint32 time,
                      bool upper,
                      uint128 maxLiquidity
                  ) internal returns (bool flipped) {
                      Tick.Info storage info = self[tick];
                      uint128 liquidityGrossBefore = info.liquidityGross;
                      uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta);
                      require(liquidityGrossAfter <= maxLiquidity, 'LO');
                      flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0);
                      if (liquidityGrossBefore == 0) {
                          // by convention, we assume that all growth before a tick was initialized happened _below_ the tick
                          if (tick <= tickCurrent) {
                              info.feeGrowthOutside0X128 = feeGrowthGlobal0X128;
                              info.feeGrowthOutside1X128 = feeGrowthGlobal1X128;
                              info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128;
                              info.tickCumulativeOutside = tickCumulative;
                              info.secondsOutside = time;
                          }
                          info.initialized = true;
                      }
                      info.liquidityGross = liquidityGrossAfter;
                      // when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed)
                      info.liquidityNet = upper
                          ? int256(info.liquidityNet).sub(liquidityDelta).toInt128()
                          : int256(info.liquidityNet).add(liquidityDelta).toInt128();
                  }
                  /// @notice Clears tick data
                  /// @param self The mapping containing all initialized tick information for initialized ticks
                  /// @param tick The tick that will be cleared
                  function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal {
                      delete self[tick];
                  }
                  /// @notice Transitions to next tick as needed by price movement
                  /// @param self The mapping containing all tick information for initialized ticks
                  /// @param tick The destination tick of the transition
                  /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
                  /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
                  /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity
                  /// @param time The current block.timestamp
                  /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left)
                  function cross(
                      mapping(int24 => Tick.Info) storage self,
                      int24 tick,
                      uint256 feeGrowthGlobal0X128,
                      uint256 feeGrowthGlobal1X128,
                      uint160 secondsPerLiquidityCumulativeX128,
                      int56 tickCumulative,
                      uint32 time
                  ) internal returns (int128 liquidityNet) {
                      Tick.Info storage info = self[tick];
                      info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128;
                      info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128;
                      info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128;
                      info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside;
                      info.secondsOutside = time - info.secondsOutside;
                      liquidityNet = info.liquidityNet;
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity >=0.5.0;
              import './BitMath.sol';
              /// @title Packed tick initialized state library
              /// @notice Stores a packed mapping of tick index to its initialized state
              /// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.
              library TickBitmap {
                  /// @notice Computes the position in the mapping where the initialized bit for a tick lives
                  /// @param tick The tick for which to compute the position
                  /// @return wordPos The key in the mapping containing the word in which the bit is stored
                  /// @return bitPos The bit position in the word where the flag is stored
                  function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) {
                      wordPos = int16(tick >> 8);
                      bitPos = uint8(tick % 256);
                  }
                  /// @notice Flips the initialized state for a given tick from false to true, or vice versa
                  /// @param self The mapping in which to flip the tick
                  /// @param tick The tick to flip
                  /// @param tickSpacing The spacing between usable ticks
                  function flipTick(
                      mapping(int16 => uint256) storage self,
                      int24 tick,
                      int24 tickSpacing
                  ) internal {
                      require(tick % tickSpacing == 0); // ensure that the tick is spaced
                      (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing);
                      uint256 mask = 1 << bitPos;
                      self[wordPos] ^= mask;
                  }
                  /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either
                  /// to the left (less than or equal to) or right (greater than) of the given tick
                  /// @param self The mapping in which to compute the next initialized tick
                  /// @param tick The starting tick
                  /// @param tickSpacing The spacing between usable ticks
                  /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick)
                  /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick
                  /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks
                  function nextInitializedTickWithinOneWord(
                      mapping(int16 => uint256) storage self,
                      int24 tick,
                      int24 tickSpacing,
                      bool lte
                  ) internal view returns (int24 next, bool initialized) {
                      int24 compressed = tick / tickSpacing;
                      if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity
                      if (lte) {
                          (int16 wordPos, uint8 bitPos) = position(compressed);
                          // all the 1s at or to the right of the current bitPos
                          uint256 mask = (1 << bitPos) - 1 + (1 << bitPos);
                          uint256 masked = self[wordPos] & mask;
                          // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word
                          initialized = masked != 0;
                          // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
                          next = initialized
                              ? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing
                              : (compressed - int24(bitPos)) * tickSpacing;
                      } else {
                          // start from the word of the next tick, since the current tick state doesn't matter
                          (int16 wordPos, uint8 bitPos) = position(compressed + 1);
                          // all the 1s at or to the left of the bitPos
                          uint256 mask = ~((1 << bitPos) - 1);
                          uint256 masked = self[wordPos] & mask;
                          // if there are no initialized ticks to the left of the current tick, return leftmost in the word
                          initialized = masked != 0;
                          // overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
                          next = initialized
                              ? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing
                              : (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing;
                      }
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity >=0.5.0;
              import './FullMath.sol';
              import './FixedPoint128.sol';
              import './LiquidityMath.sol';
              /// @title Position
              /// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary
              /// @dev Positions store additional state for tracking fees owed to the position
              library Position {
                  // info stored for each user's position
                  struct Info {
                      // the amount of liquidity owned by this position
                      uint128 liquidity;
                      // fee growth per unit of liquidity as of the last update to liquidity or fees owed
                      uint256 feeGrowthInside0LastX128;
                      uint256 feeGrowthInside1LastX128;
                      // the fees owed to the position owner in token0/token1
                      uint128 tokensOwed0;
                      uint128 tokensOwed1;
                  }
                  /// @notice Returns the Info struct of a position, given an owner and position boundaries
                  /// @param self The mapping containing all user positions
                  /// @param owner The address of the position owner
                  /// @param tickLower The lower tick boundary of the position
                  /// @param tickUpper The upper tick boundary of the position
                  /// @return position The position info struct of the given owners' position
                  function get(
                      mapping(bytes32 => Info) storage self,
                      address owner,
                      int24 tickLower,
                      int24 tickUpper
                  ) internal view returns (Position.Info storage position) {
                      position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))];
                  }
                  /// @notice Credits accumulated fees to a user's position
                  /// @param self The individual position to update
                  /// @param liquidityDelta The change in pool liquidity as a result of the position update
                  /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
                  /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
                  function update(
                      Info storage self,
                      int128 liquidityDelta,
                      uint256 feeGrowthInside0X128,
                      uint256 feeGrowthInside1X128
                  ) internal {
                      Info memory _self = self;
                      uint128 liquidityNext;
                      if (liquidityDelta == 0) {
                          require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions
                          liquidityNext = _self.liquidity;
                      } else {
                          liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta);
                      }
                      // calculate accumulated fees
                      uint128 tokensOwed0 =
                          uint128(
                              FullMath.mulDiv(
                                  feeGrowthInside0X128 - _self.feeGrowthInside0LastX128,
                                  _self.liquidity,
                                  FixedPoint128.Q128
                              )
                          );
                      uint128 tokensOwed1 =
                          uint128(
                              FullMath.mulDiv(
                                  feeGrowthInside1X128 - _self.feeGrowthInside1LastX128,
                                  _self.liquidity,
                                  FixedPoint128.Q128
                              )
                          );
                      // update the position
                      if (liquidityDelta != 0) self.liquidity = liquidityNext;
                      self.feeGrowthInside0LastX128 = feeGrowthInside0X128;
                      self.feeGrowthInside1LastX128 = feeGrowthInside1X128;
                      if (tokensOwed0 > 0 || tokensOwed1 > 0) {
                          // overflow is acceptable, have to withdraw before you hit type(uint128).max fees
                          self.tokensOwed0 += tokensOwed0;
                          self.tokensOwed1 += tokensOwed1;
                      }
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity >=0.5.0;
              /// @title Oracle
              /// @notice Provides price and liquidity data useful for a wide variety of system designs
              /// @dev Instances of stored oracle data, "observations", are collected in the oracle array
              /// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the
              /// maximum length of the oracle array. New slots will be added when the array is fully populated.
              /// Observations are overwritten when the full length of the oracle array is populated.
              /// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe()
              library Oracle {
                  struct Observation {
                      // the block timestamp of the observation
                      uint32 blockTimestamp;
                      // the tick accumulator, i.e. tick * time elapsed since the pool was first initialized
                      int56 tickCumulative;
                      // the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized
                      uint160 secondsPerLiquidityCumulativeX128;
                      // whether or not the observation is initialized
                      bool initialized;
                  }
                  /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values
                  /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows
                  /// @param last The specified observation to be transformed
                  /// @param blockTimestamp The timestamp of the new observation
                  /// @param tick The active tick at the time of the new observation
                  /// @param liquidity The total in-range liquidity at the time of the new observation
                  /// @return Observation The newly populated observation
                  function transform(
                      Observation memory last,
                      uint32 blockTimestamp,
                      int24 tick,
                      uint128 liquidity
                  ) private pure returns (Observation memory) {
                      uint32 delta = blockTimestamp - last.blockTimestamp;
                      return
                          Observation({
                              blockTimestamp: blockTimestamp,
                              tickCumulative: last.tickCumulative + int56(tick) * delta,
                              secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 +
                                  ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)),
                              initialized: true
                          });
                  }
                  /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array
                  /// @param self The stored oracle array
                  /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32
                  /// @return cardinality The number of populated elements in the oracle array
                  /// @return cardinalityNext The new length of the oracle array, independent of population
                  function initialize(Observation[65535] storage self, uint32 time)
                      internal
                      returns (uint16 cardinality, uint16 cardinalityNext)
                  {
                      self[0] = Observation({
                          blockTimestamp: time,
                          tickCumulative: 0,
                          secondsPerLiquidityCumulativeX128: 0,
                          initialized: true
                      });
                      return (1, 1);
                  }
                  /// @notice Writes an oracle observation to the array
                  /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally.
                  /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality
                  /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering.
                  /// @param self The stored oracle array
                  /// @param index The index of the observation that was most recently written to the observations array
                  /// @param blockTimestamp The timestamp of the new observation
                  /// @param tick The active tick at the time of the new observation
                  /// @param liquidity The total in-range liquidity at the time of the new observation
                  /// @param cardinality The number of populated elements in the oracle array
                  /// @param cardinalityNext The new length of the oracle array, independent of population
                  /// @return indexUpdated The new index of the most recently written element in the oracle array
                  /// @return cardinalityUpdated The new cardinality of the oracle array
                  function write(
                      Observation[65535] storage self,
                      uint16 index,
                      uint32 blockTimestamp,
                      int24 tick,
                      uint128 liquidity,
                      uint16 cardinality,
                      uint16 cardinalityNext
                  ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) {
                      Observation memory last = self[index];
                      // early return if we've already written an observation this block
                      if (last.blockTimestamp == blockTimestamp) return (index, cardinality);
                      // if the conditions are right, we can bump the cardinality
                      if (cardinalityNext > cardinality && index == (cardinality - 1)) {
                          cardinalityUpdated = cardinalityNext;
                      } else {
                          cardinalityUpdated = cardinality;
                      }
                      indexUpdated = (index + 1) % cardinalityUpdated;
                      self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity);
                  }
                  /// @notice Prepares the oracle array to store up to `next` observations
                  /// @param self The stored oracle array
                  /// @param current The current next cardinality of the oracle array
                  /// @param next The proposed next cardinality which will be populated in the oracle array
                  /// @return next The next cardinality which will be populated in the oracle array
                  function grow(
                      Observation[65535] storage self,
                      uint16 current,
                      uint16 next
                  ) internal returns (uint16) {
                      require(current > 0, 'I');
                      // no-op if the passed next value isn't greater than the current next value
                      if (next <= current) return current;
                      // store in each slot to prevent fresh SSTOREs in swaps
                      // this data will not be used because the initialized boolean is still false
                      for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1;
                      return next;
                  }
                  /// @notice comparator for 32-bit timestamps
                  /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time
                  /// @param time A timestamp truncated to 32 bits
                  /// @param a A comparison timestamp from which to determine the relative position of `time`
                  /// @param b From which to determine the relative position of `time`
                  /// @return bool Whether `a` is chronologically <= `b`
                  function lte(
                      uint32 time,
                      uint32 a,
                      uint32 b
                  ) private pure returns (bool) {
                      // if there hasn't been overflow, no need to adjust
                      if (a <= time && b <= time) return a <= b;
                      uint256 aAdjusted = a > time ? a : a + 2**32;
                      uint256 bAdjusted = b > time ? b : b + 2**32;
                      return aAdjusted <= bAdjusted;
                  }
                  /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied.
                  /// The result may be the same observation, or adjacent observations.
                  /// @dev The answer must be contained in the array, used when the target is located within the stored observation
                  /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation
                  /// @param self The stored oracle array
                  /// @param time The current block.timestamp
                  /// @param target The timestamp at which the reserved observation should be for
                  /// @param index The index of the observation that was most recently written to the observations array
                  /// @param cardinality The number of populated elements in the oracle array
                  /// @return beforeOrAt The observation recorded before, or at, the target
                  /// @return atOrAfter The observation recorded at, or after, the target
                  function binarySearch(
                      Observation[65535] storage self,
                      uint32 time,
                      uint32 target,
                      uint16 index,
                      uint16 cardinality
                  ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
                      uint256 l = (index + 1) % cardinality; // oldest observation
                      uint256 r = l + cardinality - 1; // newest observation
                      uint256 i;
                      while (true) {
                          i = (l + r) / 2;
                          beforeOrAt = self[i % cardinality];
                          // we've landed on an uninitialized tick, keep searching higher (more recently)
                          if (!beforeOrAt.initialized) {
                              l = i + 1;
                              continue;
                          }
                          atOrAfter = self[(i + 1) % cardinality];
                          bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target);
                          // check if we've found the answer!
                          if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break;
                          if (!targetAtOrAfter) r = i - 1;
                          else l = i + 1;
                      }
                  }
                  /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied
                  /// @dev Assumes there is at least 1 initialized observation.
                  /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp.
                  /// @param self The stored oracle array
                  /// @param time The current block.timestamp
                  /// @param target The timestamp at which the reserved observation should be for
                  /// @param tick The active tick at the time of the returned or simulated observation
                  /// @param index The index of the observation that was most recently written to the observations array
                  /// @param liquidity The total pool liquidity at the time of the call
                  /// @param cardinality The number of populated elements in the oracle array
                  /// @return beforeOrAt The observation which occurred at, or before, the given timestamp
                  /// @return atOrAfter The observation which occurred at, or after, the given timestamp
                  function getSurroundingObservations(
                      Observation[65535] storage self,
                      uint32 time,
                      uint32 target,
                      int24 tick,
                      uint16 index,
                      uint128 liquidity,
                      uint16 cardinality
                  ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
                      // optimistically set before to the newest observation
                      beforeOrAt = self[index];
                      // if the target is chronologically at or after the newest observation, we can early return
                      if (lte(time, beforeOrAt.blockTimestamp, target)) {
                          if (beforeOrAt.blockTimestamp == target) {
                              // if newest observation equals target, we're in the same block, so we can ignore atOrAfter
                              return (beforeOrAt, atOrAfter);
                          } else {
                              // otherwise, we need to transform
                              return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity));
                          }
                      }
                      // now, set before to the oldest observation
                      beforeOrAt = self[(index + 1) % cardinality];
                      if (!beforeOrAt.initialized) beforeOrAt = self[0];
                      // ensure that the target is chronologically at or after the oldest observation
                      require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD');
                      // if we've reached this point, we have to binary search
                      return binarySearch(self, time, target, index, cardinality);
                  }
                  /// @dev Reverts if an observation at or before the desired observation timestamp does not exist.
                  /// 0 may be passed as `secondsAgo' to return the current cumulative values.
                  /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values
                  /// at exactly the timestamp between the two observations.
                  /// @param self The stored oracle array
                  /// @param time The current block timestamp
                  /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation
                  /// @param tick The current tick
                  /// @param index The index of the observation that was most recently written to the observations array
                  /// @param liquidity The current in-range pool liquidity
                  /// @param cardinality The number of populated elements in the oracle array
                  /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo`
                  /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo`
                  function observeSingle(
                      Observation[65535] storage self,
                      uint32 time,
                      uint32 secondsAgo,
                      int24 tick,
                      uint16 index,
                      uint128 liquidity,
                      uint16 cardinality
                  ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) {
                      if (secondsAgo == 0) {
                          Observation memory last = self[index];
                          if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity);
                          return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128);
                      }
                      uint32 target = time - secondsAgo;
                      (Observation memory beforeOrAt, Observation memory atOrAfter) =
                          getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality);
                      if (target == beforeOrAt.blockTimestamp) {
                          // we're at the left boundary
                          return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128);
                      } else if (target == atOrAfter.blockTimestamp) {
                          // we're at the right boundary
                          return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128);
                      } else {
                          // we're in the middle
                          uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp;
                          uint32 targetDelta = target - beforeOrAt.blockTimestamp;
                          return (
                              beforeOrAt.tickCumulative +
                                  ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) *
                                  targetDelta,
                              beforeOrAt.secondsPerLiquidityCumulativeX128 +
                                  uint160(
                                      (uint256(
                                          atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128
                                      ) * targetDelta) / observationTimeDelta
                                  )
                          );
                      }
                  }
                  /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
                  /// @dev Reverts if `secondsAgos` > oldest observation
                  /// @param self The stored oracle array
                  /// @param time The current block.timestamp
                  /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation
                  /// @param tick The current tick
                  /// @param index The index of the observation that was most recently written to the observations array
                  /// @param liquidity The current in-range pool liquidity
                  /// @param cardinality The number of populated elements in the oracle array
                  /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo`
                  /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo`
                  function observe(
                      Observation[65535] storage self,
                      uint32 time,
                      uint32[] memory secondsAgos,
                      int24 tick,
                      uint16 index,
                      uint128 liquidity,
                      uint16 cardinality
                  ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) {
                      require(cardinality > 0, 'I');
                      tickCumulatives = new int56[](secondsAgos.length);
                      secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length);
                      for (uint256 i = 0; i < secondsAgos.length; i++) {
                          (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle(
                              self,
                              time,
                              secondsAgos[i],
                              tick,
                              index,
                              liquidity,
                              cardinality
                          );
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.4.0;
              /// @title Contains 512-bit math functions
              /// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
              /// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
              library FullMath {
                  /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                  /// @param a The multiplicand
                  /// @param b The multiplier
                  /// @param denominator The divisor
                  /// @return result The 256-bit result
                  /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
                  function mulDiv(
                      uint256 a,
                      uint256 b,
                      uint256 denominator
                  ) internal pure returns (uint256 result) {
                      // 512-bit multiply [prod1 prod0] = a * b
                      // Compute the product mod 2**256 and mod 2**256 - 1
                      // then 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(a, b, not(0))
                          prod0 := mul(a, b)
                          prod1 := sub(sub(mm, prod0), lt(mm, prod0))
                      }
                      // Handle non-overflow cases, 256 by 256 division
                      if (prod1 == 0) {
                          require(denominator > 0);
                          assembly {
                              result := div(prod0, denominator)
                          }
                          return result;
                      }
                      // Make sure the result is less than 2**256.
                      // Also prevents denominator == 0
                      require(denominator > prod1);
                      ///////////////////////////////////////////////
                      // 512 by 256 division.
                      ///////////////////////////////////////////////
                      // Make division exact by subtracting the remainder from [prod1 prod0]
                      // Compute remainder using mulmod
                      uint256 remainder;
                      assembly {
                          remainder := mulmod(a, b, denominator)
                      }
                      // Subtract 256 bit number from 512 bit number
                      assembly {
                          prod1 := sub(prod1, gt(remainder, prod0))
                          prod0 := sub(prod0, remainder)
                      }
                      // Factor powers of two out of denominator
                      // Compute largest power of two divisor of denominator.
                      // Always >= 1.
                      uint256 twos = -denominator & denominator;
                      // Divide denominator by power of two
                      assembly {
                          denominator := div(denominator, twos)
                      }
                      // Divide [prod1 prod0] by the factors of two
                      assembly {
                          prod0 := div(prod0, twos)
                      }
                      // Shift in bits from prod1 into prod0. For this we need
                      // to flip `twos` such that it is 2**256 / twos.
                      // If twos is zero, then it becomes one
                      assembly {
                          twos := add(div(sub(0, twos), twos), 1)
                      }
                      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
                      // correct for four bits. That is, denominator * inv = 1 mod 2**4
                      uint256 inv = (3 * denominator) ^ 2;
                      // Now use 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.
                      inv *= 2 - denominator * inv; // inverse mod 2**8
                      inv *= 2 - denominator * inv; // inverse mod 2**16
                      inv *= 2 - denominator * inv; // inverse mod 2**32
                      inv *= 2 - denominator * inv; // inverse mod 2**64
                      inv *= 2 - denominator * inv; // inverse mod 2**128
                      inv *= 2 - denominator * inv; // 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 precoditions 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 * inv;
                      return result;
                  }
                  /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
                  /// @param a The multiplicand
                  /// @param b The multiplier
                  /// @param denominator The divisor
                  /// @return result The 256-bit result
                  function mulDivRoundingUp(
                      uint256 a,
                      uint256 b,
                      uint256 denominator
                  ) internal pure returns (uint256 result) {
                      result = mulDiv(a, b, denominator);
                      if (mulmod(a, b, denominator) > 0) {
                          require(result < type(uint256).max);
                          result++;
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.4.0;
              /// @title FixedPoint128
              /// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
              library FixedPoint128 {
                  uint256 internal constant Q128 = 0x100000000000000000000000000000000;
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.6.0;
              import '../interfaces/IERC20Minimal.sol';
              /// @title TransferHelper
              /// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false
              library TransferHelper {
                  /// @notice Transfers tokens from msg.sender to a recipient
                  /// @dev Calls transfer on token contract, errors with TF if transfer fails
                  /// @param token The contract address of the token which will be transferred
                  /// @param to The recipient of the transfer
                  /// @param value The value of the transfer
                  function safeTransfer(
                      address token,
                      address to,
                      uint256 value
                  ) internal {
                      (bool success, bytes memory data) =
                          token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF');
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Math library for computing sqrt prices from ticks and vice versa
              /// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
              /// prices between 2**-128 and 2**128
              library TickMath {
                  /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
                  int24 internal constant MIN_TICK = -887272;
                  /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
                  int24 internal constant MAX_TICK = -MIN_TICK;
                  /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
                  uint160 internal constant MIN_SQRT_RATIO = 4295128739;
                  /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
                  uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
                  /// @notice Calculates sqrt(1.0001^tick) * 2^96
                  /// @dev Throws if |tick| > max tick
                  /// @param tick The input tick for the above formula
                  /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
                  /// at the given tick
                  function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
                      uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
                      require(absTick <= uint256(MAX_TICK), 'T');
                      uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
                      if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
                      if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
                      if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
                      if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
                      if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
                      if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
                      if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
                      if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
                      if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
                      if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
                      if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
                      if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
                      if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
                      if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
                      if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
                      if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
                      if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
                      if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
                      if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
                      if (tick > 0) ratio = type(uint256).max / ratio;
                      // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
                      // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
                      // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
                      sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
                  }
                  /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
                  /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
                  /// ever return.
                  /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
                  /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
                  function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
                      // second inequality must be < because the price can never reach the price at the max tick
                      require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R');
                      uint256 ratio = uint256(sqrtPriceX96) << 32;
                      uint256 r = ratio;
                      uint256 msb = 0;
                      assembly {
                          let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
                          msb := or(msb, f)
                          r := shr(f, r)
                      }
                      assembly {
                          let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
                          msb := or(msb, f)
                          r := shr(f, r)
                      }
                      assembly {
                          let f := shl(5, gt(r, 0xFFFFFFFF))
                          msb := or(msb, f)
                          r := shr(f, r)
                      }
                      assembly {
                          let f := shl(4, gt(r, 0xFFFF))
                          msb := or(msb, f)
                          r := shr(f, r)
                      }
                      assembly {
                          let f := shl(3, gt(r, 0xFF))
                          msb := or(msb, f)
                          r := shr(f, r)
                      }
                      assembly {
                          let f := shl(2, gt(r, 0xF))
                          msb := or(msb, f)
                          r := shr(f, r)
                      }
                      assembly {
                          let f := shl(1, gt(r, 0x3))
                          msb := or(msb, f)
                          r := shr(f, r)
                      }
                      assembly {
                          let f := gt(r, 0x1)
                          msb := or(msb, f)
                      }
                      if (msb >= 128) r = ratio >> (msb - 127);
                      else r = ratio << (127 - msb);
                      int256 log_2 = (int256(msb) - 128) << 64;
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(63, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(62, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(61, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(60, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(59, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(58, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(57, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(56, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(55, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(54, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(53, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(52, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(51, f))
                          r := shr(f, r)
                      }
                      assembly {
                          r := shr(127, mul(r, r))
                          let f := shr(128, r)
                          log_2 := or(log_2, shl(50, f))
                      }
                      int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number
                      int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
                      int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);
                      tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Math library for liquidity
              library LiquidityMath {
                  /// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows
                  /// @param x The liquidity before change
                  /// @param y The delta by which liquidity should be changed
                  /// @return z The liquidity delta
                  function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) {
                      if (y < 0) {
                          require((z = x - uint128(-y)) < x, 'LS');
                      } else {
                          require((z = x + uint128(y)) >= x, 'LA');
                      }
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity >=0.5.0;
              import './LowGasSafeMath.sol';
              import './SafeCast.sol';
              import './FullMath.sol';
              import './UnsafeMath.sol';
              import './FixedPoint96.sol';
              /// @title Functions based on Q64.96 sqrt price and liquidity
              /// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
              library SqrtPriceMath {
                  using LowGasSafeMath for uint256;
                  using SafeCast for uint256;
                  /// @notice Gets the next sqrt price given a delta of token0
                  /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least
                  /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the
                  /// price less in order to not send too much output.
                  /// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),
                  /// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).
                  /// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta
                  /// @param liquidity The amount of usable liquidity
                  /// @param amount How much of token0 to add or remove from virtual reserves
                  /// @param add Whether to add or remove the amount of token0
                  /// @return The price after adding or removing amount, depending on add
                  function getNextSqrtPriceFromAmount0RoundingUp(
                      uint160 sqrtPX96,
                      uint128 liquidity,
                      uint256 amount,
                      bool add
                  ) internal pure returns (uint160) {
                      // we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price
                      if (amount == 0) return sqrtPX96;
                      uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
                      if (add) {
                          uint256 product;
                          if ((product = amount * sqrtPX96) / amount == sqrtPX96) {
                              uint256 denominator = numerator1 + product;
                              if (denominator >= numerator1)
                                  // always fits in 160 bits
                                  return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));
                          }
                          return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount)));
                      } else {
                          uint256 product;
                          // if the product overflows, we know the denominator underflows
                          // in addition, we must check that the denominator does not underflow
                          require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);
                          uint256 denominator = numerator1 - product;
                          return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
                      }
                  }
                  /// @notice Gets the next sqrt price given a delta of token1
                  /// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least
                  /// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the
                  /// price less in order to not send too much output.
                  /// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity
                  /// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta
                  /// @param liquidity The amount of usable liquidity
                  /// @param amount How much of token1 to add, or remove, from virtual reserves
                  /// @param add Whether to add, or remove, the amount of token1
                  /// @return The price after adding or removing `amount`
                  function getNextSqrtPriceFromAmount1RoundingDown(
                      uint160 sqrtPX96,
                      uint128 liquidity,
                      uint256 amount,
                      bool add
                  ) internal pure returns (uint160) {
                      // if we're adding (subtracting), rounding down requires rounding the quotient down (up)
                      // in both cases, avoid a mulDiv for most inputs
                      if (add) {
                          uint256 quotient =
                              (
                                  amount <= type(uint160).max
                                      ? (amount << FixedPoint96.RESOLUTION) / liquidity
                                      : FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)
                              );
                          return uint256(sqrtPX96).add(quotient).toUint160();
                      } else {
                          uint256 quotient =
                              (
                                  amount <= type(uint160).max
                                      ? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)
                                      : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)
                              );
                          require(sqrtPX96 > quotient);
                          // always fits 160 bits
                          return uint160(sqrtPX96 - quotient);
                      }
                  }
                  /// @notice Gets the next sqrt price given an input amount of token0 or token1
                  /// @dev Throws if price or liquidity are 0, or if the next price is out of bounds
                  /// @param sqrtPX96 The starting price, i.e., before accounting for the input amount
                  /// @param liquidity The amount of usable liquidity
                  /// @param amountIn How much of token0, or token1, is being swapped in
                  /// @param zeroForOne Whether the amount in is token0 or token1
                  /// @return sqrtQX96 The price after adding the input amount to token0 or token1
                  function getNextSqrtPriceFromInput(
                      uint160 sqrtPX96,
                      uint128 liquidity,
                      uint256 amountIn,
                      bool zeroForOne
                  ) internal pure returns (uint160 sqrtQX96) {
                      require(sqrtPX96 > 0);
                      require(liquidity > 0);
                      // round to make sure that we don't pass the target price
                      return
                          zeroForOne
                              ? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)
                              : getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);
                  }
                  /// @notice Gets the next sqrt price given an output amount of token0 or token1
                  /// @dev Throws if price or liquidity are 0 or the next price is out of bounds
                  /// @param sqrtPX96 The starting price before accounting for the output amount
                  /// @param liquidity The amount of usable liquidity
                  /// @param amountOut How much of token0, or token1, is being swapped out
                  /// @param zeroForOne Whether the amount out is token0 or token1
                  /// @return sqrtQX96 The price after removing the output amount of token0 or token1
                  function getNextSqrtPriceFromOutput(
                      uint160 sqrtPX96,
                      uint128 liquidity,
                      uint256 amountOut,
                      bool zeroForOne
                  ) internal pure returns (uint160 sqrtQX96) {
                      require(sqrtPX96 > 0);
                      require(liquidity > 0);
                      // round to make sure that we pass the target price
                      return
                          zeroForOne
                              ? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)
                              : getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);
                  }
                  /// @notice Gets the amount0 delta between two prices
                  /// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
                  /// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))
                  /// @param sqrtRatioAX96 A sqrt price
                  /// @param sqrtRatioBX96 Another sqrt price
                  /// @param liquidity The amount of usable liquidity
                  /// @param roundUp Whether to round the amount up or down
                  /// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices
                  function getAmount0Delta(
                      uint160 sqrtRatioAX96,
                      uint160 sqrtRatioBX96,
                      uint128 liquidity,
                      bool roundUp
                  ) internal pure returns (uint256 amount0) {
                      if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
                      uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
                      uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96;
                      require(sqrtRatioAX96 > 0);
                      return
                          roundUp
                              ? UnsafeMath.divRoundingUp(
                                  FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96),
                                  sqrtRatioAX96
                              )
                              : FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96;
                  }
                  /// @notice Gets the amount1 delta between two prices
                  /// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
                  /// @param sqrtRatioAX96 A sqrt price
                  /// @param sqrtRatioBX96 Another sqrt price
                  /// @param liquidity The amount of usable liquidity
                  /// @param roundUp Whether to round the amount up, or down
                  /// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices
                  function getAmount1Delta(
                      uint160 sqrtRatioAX96,
                      uint160 sqrtRatioBX96,
                      uint128 liquidity,
                      bool roundUp
                  ) internal pure returns (uint256 amount1) {
                      if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
                      return
                          roundUp
                              ? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96)
                              : FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
                  }
                  /// @notice Helper that gets signed token0 delta
                  /// @param sqrtRatioAX96 A sqrt price
                  /// @param sqrtRatioBX96 Another sqrt price
                  /// @param liquidity The change in liquidity for which to compute the amount0 delta
                  /// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices
                  function getAmount0Delta(
                      uint160 sqrtRatioAX96,
                      uint160 sqrtRatioBX96,
                      int128 liquidity
                  ) internal pure returns (int256 amount0) {
                      return
                          liquidity < 0
                              ? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
                              : getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
                  }
                  /// @notice Helper that gets signed token1 delta
                  /// @param sqrtRatioAX96 A sqrt price
                  /// @param sqrtRatioBX96 Another sqrt price
                  /// @param liquidity The change in liquidity for which to compute the amount1 delta
                  /// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices
                  function getAmount1Delta(
                      uint160 sqrtRatioAX96,
                      uint160 sqrtRatioBX96,
                      int128 liquidity
                  ) internal pure returns (int256 amount1) {
                      return
                          liquidity < 0
                              ? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
                              : getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity >=0.5.0;
              import './FullMath.sol';
              import './SqrtPriceMath.sol';
              /// @title Computes the result of a swap within ticks
              /// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick.
              library SwapMath {
                  /// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap
                  /// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive
                  /// @param sqrtRatioCurrentX96 The current sqrt price of the pool
                  /// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred
                  /// @param liquidity The usable liquidity
                  /// @param amountRemaining How much input or output amount is remaining to be swapped in/out
                  /// @param feePips The fee taken from the input amount, expressed in hundredths of a bip
                  /// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target
                  /// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap
                  /// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap
                  /// @return feeAmount The amount of input that will be taken as a fee
                  function computeSwapStep(
                      uint160 sqrtRatioCurrentX96,
                      uint160 sqrtRatioTargetX96,
                      uint128 liquidity,
                      int256 amountRemaining,
                      uint24 feePips
                  )
                      internal
                      pure
                      returns (
                          uint160 sqrtRatioNextX96,
                          uint256 amountIn,
                          uint256 amountOut,
                          uint256 feeAmount
                      )
                  {
                      bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96;
                      bool exactIn = amountRemaining >= 0;
                      if (exactIn) {
                          uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6);
                          amountIn = zeroForOne
                              ? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true)
                              : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true);
                          if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96;
                          else
                              sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput(
                                  sqrtRatioCurrentX96,
                                  liquidity,
                                  amountRemainingLessFee,
                                  zeroForOne
                              );
                      } else {
                          amountOut = zeroForOne
                              ? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false)
                              : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false);
                          if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96;
                          else
                              sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput(
                                  sqrtRatioCurrentX96,
                                  liquidity,
                                  uint256(-amountRemaining),
                                  zeroForOne
                              );
                      }
                      bool max = sqrtRatioTargetX96 == sqrtRatioNextX96;
                      // get the input/output amounts
                      if (zeroForOne) {
                          amountIn = max && exactIn
                              ? amountIn
                              : SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true);
                          amountOut = max && !exactIn
                              ? amountOut
                              : SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false);
                      } else {
                          amountIn = max && exactIn
                              ? amountIn
                              : SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true);
                          amountOut = max && !exactIn
                              ? amountOut
                              : SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false);
                      }
                      // cap the output amount to not exceed the remaining output amount
                      if (!exactIn && amountOut > uint256(-amountRemaining)) {
                          amountOut = uint256(-amountRemaining);
                      }
                      if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) {
                          // we didn't reach the target, so take the remainder of the maximum input as fee
                          feeAmount = uint256(amountRemaining) - amountIn;
                      } else {
                          feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips);
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title An interface for a contract that is capable of deploying Uniswap V3 Pools
              /// @notice A contract that constructs a pool must implement this to pass arguments to the pool
              /// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash
              /// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain
              interface IUniswapV3PoolDeployer {
                  /// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation.
                  /// @dev Called by the pool constructor to fetch the parameters of the pool
                  /// Returns factory The factory address
                  /// Returns token0 The first token of the pool by address sort order
                  /// Returns token1 The second token of the pool by address sort order
                  /// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
                  /// Returns tickSpacing The minimum number of ticks between initialized ticks
                  function parameters()
                      external
                      view
                      returns (
                          address factory,
                          address token0,
                          address token1,
                          uint24 fee,
                          int24 tickSpacing
                      );
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title The interface for the Uniswap V3 Factory
              /// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
              interface IUniswapV3Factory {
                  /// @notice Emitted when the owner of the factory is changed
                  /// @param oldOwner The owner before the owner was changed
                  /// @param newOwner The owner after the owner was changed
                  event OwnerChanged(address indexed oldOwner, address indexed newOwner);
                  /// @notice Emitted when a pool is created
                  /// @param token0 The first token of the pool by address sort order
                  /// @param token1 The second token of the pool by address sort order
                  /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
                  /// @param tickSpacing The minimum number of ticks between initialized ticks
                  /// @param pool The address of the created pool
                  event PoolCreated(
                      address indexed token0,
                      address indexed token1,
                      uint24 indexed fee,
                      int24 tickSpacing,
                      address pool
                  );
                  /// @notice Emitted when a new fee amount is enabled for pool creation via the factory
                  /// @param fee The enabled fee, denominated in hundredths of a bip
                  /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
                  event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);
                  /// @notice Returns the current owner of the factory
                  /// @dev Can be changed by the current owner via setOwner
                  /// @return The address of the factory owner
                  function owner() external view returns (address);
                  /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
                  /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
                  /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
                  /// @return The tick spacing
                  function feeAmountTickSpacing(uint24 fee) external view returns (int24);
                  /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
                  /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
                  /// @param tokenA The contract address of either token0 or token1
                  /// @param tokenB The contract address of the other token
                  /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
                  /// @return pool The pool address
                  function getPool(
                      address tokenA,
                      address tokenB,
                      uint24 fee
                  ) external view returns (address pool);
                  /// @notice Creates a pool for the given two tokens and fee
                  /// @param tokenA One of the two tokens in the desired pool
                  /// @param tokenB The other of the two tokens in the desired pool
                  /// @param fee The desired fee for the pool
                  /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
                  /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
                  /// are invalid.
                  /// @return pool The address of the newly created pool
                  function createPool(
                      address tokenA,
                      address tokenB,
                      uint24 fee
                  ) external returns (address pool);
                  /// @notice Updates the owner of the factory
                  /// @dev Must be called by the current owner
                  /// @param _owner The new owner of the factory
                  function setOwner(address _owner) external;
                  /// @notice Enables a fee amount with the given tickSpacing
                  /// @dev Fee amounts may never be removed once enabled
                  /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
                  /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
                  function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Minimal ERC20 interface for Uniswap
              /// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3
              interface IERC20Minimal {
                  /// @notice Returns the balance of a token
                  /// @param account The account for which to look up the number of tokens it has, i.e. its balance
                  /// @return The number of tokens held by the account
                  function balanceOf(address account) external view returns (uint256);
                  /// @notice Transfers the amount of token from the `msg.sender` to the recipient
                  /// @param recipient The account that will receive the amount transferred
                  /// @param amount The number of tokens to send from the sender to the recipient
                  /// @return Returns true for a successful transfer, false for an unsuccessful transfer
                  function transfer(address recipient, uint256 amount) external returns (bool);
                  /// @notice Returns the current allowance given to a spender by an owner
                  /// @param owner The account of the token owner
                  /// @param spender The account of the token spender
                  /// @return The current allowance granted by `owner` to `spender`
                  function allowance(address owner, address spender) external view returns (uint256);
                  /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount`
                  /// @param spender The account which will be allowed to spend a given amount of the owners tokens
                  /// @param amount The amount of tokens allowed to be used by `spender`
                  /// @return Returns true for a successful approval, false for unsuccessful
                  function approve(address spender, uint256 amount) external returns (bool);
                  /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender`
                  /// @param sender The account from which the transfer will be initiated
                  /// @param recipient The recipient of the transfer
                  /// @param amount The amount of the transfer
                  /// @return Returns true for a successful transfer, false for unsuccessful
                  function transferFrom(
                      address sender,
                      address recipient,
                      uint256 amount
                  ) external returns (bool);
                  /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`.
                  /// @param from The account from which the tokens were sent, i.e. the balance decreased
                  /// @param to The account to which the tokens were sent, i.e. the balance increased
                  /// @param value The amount of tokens that were transferred
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes.
                  /// @param owner The account that approved spending of its tokens
                  /// @param spender The account for which the spending allowance was modified
                  /// @param value The new allowance from the owner to the spender
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Callback for IUniswapV3PoolActions#mint
              /// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface
              interface IUniswapV3MintCallback {
                  /// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint.
                  /// @dev In the implementation you must pay the pool tokens owed for the minted liquidity.
                  /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
                  /// @param amount0Owed The amount of token0 due to the pool for the minted liquidity
                  /// @param amount1Owed The amount of token1 due to the pool for the minted liquidity
                  /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call
                  function uniswapV3MintCallback(
                      uint256 amount0Owed,
                      uint256 amount1Owed,
                      bytes calldata data
                  ) external;
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Callback for IUniswapV3PoolActions#swap
              /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
              interface IUniswapV3SwapCallback {
                  /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
                  /// @dev In the implementation you must pay the pool tokens owed for the swap.
                  /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
                  /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
                  /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
                  /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
                  /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
                  /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
                  /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
                  function uniswapV3SwapCallback(
                      int256 amount0Delta,
                      int256 amount1Delta,
                      bytes calldata data
                  ) external;
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Callback for IUniswapV3PoolActions#flash
              /// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface
              interface IUniswapV3FlashCallback {
                  /// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash.
                  /// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts.
                  /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
                  /// @param fee0 The fee amount in token0 due to the pool by the end of the flash
                  /// @param fee1 The fee amount in token1 due to the pool by the end of the flash
                  /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call
                  function uniswapV3FlashCallback(
                      uint256 fee0,
                      uint256 fee1,
                      bytes calldata data
                  ) external;
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Pool state that never changes
              /// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
              interface IUniswapV3PoolImmutables {
                  /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
                  /// @return The contract address
                  function factory() external view returns (address);
                  /// @notice The first of the two tokens of the pool, sorted by address
                  /// @return The token contract address
                  function token0() external view returns (address);
                  /// @notice The second of the two tokens of the pool, sorted by address
                  /// @return The token contract address
                  function token1() external view returns (address);
                  /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
                  /// @return The fee
                  function fee() external view returns (uint24);
                  /// @notice The pool tick spacing
                  /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
                  /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
                  /// This value is an int24 to avoid casting even though it is always positive.
                  /// @return The tick spacing
                  function tickSpacing() external view returns (int24);
                  /// @notice The maximum amount of position liquidity that can use any tick in the range
                  /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
                  /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
                  /// @return The max amount of liquidity per tick
                  function maxLiquidityPerTick() external view returns (uint128);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Pool state that can change
              /// @notice These methods compose the pool's state, and can change with any frequency including multiple times
              /// per transaction
              interface IUniswapV3PoolState {
                  /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
                  /// when accessed externally.
                  /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
                  /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
                  /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
                  /// boundary.
                  /// observationIndex The index of the last oracle observation that was written,
                  /// observationCardinality The current maximum number of observations stored in the pool,
                  /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
                  /// feeProtocol The protocol fee for both tokens of the pool.
                  /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
                  /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
                  /// unlocked Whether the pool is currently locked to reentrancy
                  function slot0()
                      external
                      view
                      returns (
                          uint160 sqrtPriceX96,
                          int24 tick,
                          uint16 observationIndex,
                          uint16 observationCardinality,
                          uint16 observationCardinalityNext,
                          uint8 feeProtocol,
                          bool unlocked
                      );
                  /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
                  /// @dev This value can overflow the uint256
                  function feeGrowthGlobal0X128() external view returns (uint256);
                  /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
                  /// @dev This value can overflow the uint256
                  function feeGrowthGlobal1X128() external view returns (uint256);
                  /// @notice The amounts of token0 and token1 that are owed to the protocol
                  /// @dev Protocol fees will never exceed uint128 max in either token
                  function protocolFees() external view returns (uint128 token0, uint128 token1);
                  /// @notice The currently in range liquidity available to the pool
                  /// @dev This value has no relationship to the total liquidity across all ticks
                  function liquidity() external view returns (uint128);
                  /// @notice Look up information about a specific tick in the pool
                  /// @param tick The tick to look up
                  /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
                  /// tick upper,
                  /// liquidityNet how much liquidity changes when the pool price crosses the tick,
                  /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
                  /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
                  /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
                  /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
                  /// secondsOutside the seconds spent on the other side of the tick from the current tick,
                  /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
                  /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
                  /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
                  /// a specific position.
                  function ticks(int24 tick)
                      external
                      view
                      returns (
                          uint128 liquidityGross,
                          int128 liquidityNet,
                          uint256 feeGrowthOutside0X128,
                          uint256 feeGrowthOutside1X128,
                          int56 tickCumulativeOutside,
                          uint160 secondsPerLiquidityOutsideX128,
                          uint32 secondsOutside,
                          bool initialized
                      );
                  /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
                  function tickBitmap(int16 wordPosition) external view returns (uint256);
                  /// @notice Returns the information about a position by the position's key
                  /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
                  /// @return _liquidity The amount of liquidity in the position,
                  /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
                  /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
                  /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
                  /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
                  function positions(bytes32 key)
                      external
                      view
                      returns (
                          uint128 _liquidity,
                          uint256 feeGrowthInside0LastX128,
                          uint256 feeGrowthInside1LastX128,
                          uint128 tokensOwed0,
                          uint128 tokensOwed1
                      );
                  /// @notice Returns data about a specific observation index
                  /// @param index The element of the observations array to fetch
                  /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
                  /// ago, rather than at a specific index in the array.
                  /// @return blockTimestamp The timestamp of the observation,
                  /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
                  /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
                  /// Returns initialized whether the observation has been initialized and the values are safe to use
                  function observations(uint256 index)
                      external
                      view
                      returns (
                          uint32 blockTimestamp,
                          int56 tickCumulative,
                          uint160 secondsPerLiquidityCumulativeX128,
                          bool initialized
                      );
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Pool state that is not stored
              /// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
              /// blockchain. The functions here may have variable gas costs.
              interface IUniswapV3PoolDerivedState {
                  /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
                  /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
                  /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
                  /// you must call it with secondsAgos = [3600, 0].
                  /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
                  /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
                  /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
                  /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
                  /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
                  /// timestamp
                  function observe(uint32[] calldata secondsAgos)
                      external
                      view
                      returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
                  /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
                  /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
                  /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
                  /// snapshot is taken and the second snapshot is taken.
                  /// @param tickLower The lower tick of the range
                  /// @param tickUpper The upper tick of the range
                  /// @return tickCumulativeInside The snapshot of the tick accumulator for the range
                  /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
                  /// @return secondsInside The snapshot of seconds per liquidity for the range
                  function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
                      external
                      view
                      returns (
                          int56 tickCumulativeInside,
                          uint160 secondsPerLiquidityInsideX128,
                          uint32 secondsInside
                      );
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Permissionless pool actions
              /// @notice Contains pool methods that can be called by anyone
              interface IUniswapV3PoolActions {
                  /// @notice Sets the initial price for the pool
                  /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
                  /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
                  function initialize(uint160 sqrtPriceX96) external;
                  /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
                  /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
                  /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
                  /// on tickLower, tickUpper, the amount of liquidity, and the current price.
                  /// @param recipient The address for which the liquidity will be created
                  /// @param tickLower The lower tick of the position in which to add liquidity
                  /// @param tickUpper The upper tick of the position in which to add liquidity
                  /// @param amount The amount of liquidity to mint
                  /// @param data Any data that should be passed through to the callback
                  /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
                  /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
                  function mint(
                      address recipient,
                      int24 tickLower,
                      int24 tickUpper,
                      uint128 amount,
                      bytes calldata data
                  ) external returns (uint256 amount0, uint256 amount1);
                  /// @notice Collects tokens owed to a position
                  /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
                  /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
                  /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
                  /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
                  /// @param recipient The address which should receive the fees collected
                  /// @param tickLower The lower tick of the position for which to collect fees
                  /// @param tickUpper The upper tick of the position for which to collect fees
                  /// @param amount0Requested How much token0 should be withdrawn from the fees owed
                  /// @param amount1Requested How much token1 should be withdrawn from the fees owed
                  /// @return amount0 The amount of fees collected in token0
                  /// @return amount1 The amount of fees collected in token1
                  function collect(
                      address recipient,
                      int24 tickLower,
                      int24 tickUpper,
                      uint128 amount0Requested,
                      uint128 amount1Requested
                  ) external returns (uint128 amount0, uint128 amount1);
                  /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
                  /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
                  /// @dev Fees must be collected separately via a call to #collect
                  /// @param tickLower The lower tick of the position for which to burn liquidity
                  /// @param tickUpper The upper tick of the position for which to burn liquidity
                  /// @param amount How much liquidity to burn
                  /// @return amount0 The amount of token0 sent to the recipient
                  /// @return amount1 The amount of token1 sent to the recipient
                  function burn(
                      int24 tickLower,
                      int24 tickUpper,
                      uint128 amount
                  ) external returns (uint256 amount0, uint256 amount1);
                  /// @notice Swap token0 for token1, or token1 for token0
                  /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
                  /// @param recipient The address to receive the output of the swap
                  /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
                  /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
                  /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
                  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
                  /// @param data Any data to be passed through to the callback
                  /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
                  /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
                  function swap(
                      address recipient,
                      bool zeroForOne,
                      int256 amountSpecified,
                      uint160 sqrtPriceLimitX96,
                      bytes calldata data
                  ) external returns (int256 amount0, int256 amount1);
                  /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
                  /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
                  /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
                  /// with 0 amount{0,1} and sending the donation amount(s) from the callback
                  /// @param recipient The address which will receive the token0 and token1 amounts
                  /// @param amount0 The amount of token0 to send
                  /// @param amount1 The amount of token1 to send
                  /// @param data Any data to be passed through to the callback
                  function flash(
                      address recipient,
                      uint256 amount0,
                      uint256 amount1,
                      bytes calldata data
                  ) external;
                  /// @notice Increase the maximum number of price and liquidity observations that this pool will store
                  /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
                  /// the input observationCardinalityNext.
                  /// @param observationCardinalityNext The desired minimum number of observations for the pool to store
                  function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Permissioned pool actions
              /// @notice Contains pool methods that may only be called by the factory owner
              interface IUniswapV3PoolOwnerActions {
                  /// @notice Set the denominator of the protocol's % share of the fees
                  /// @param feeProtocol0 new protocol fee for token0 of the pool
                  /// @param feeProtocol1 new protocol fee for token1 of the pool
                  function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;
                  /// @notice Collect the protocol fee accrued to the pool
                  /// @param recipient The address to which collected protocol fees should be sent
                  /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
                  /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
                  /// @return amount0 The protocol fee collected in token0
                  /// @return amount1 The protocol fee collected in token1
                  function collectProtocol(
                      address recipient,
                      uint128 amount0Requested,
                      uint128 amount1Requested
                  ) external returns (uint128 amount0, uint128 amount1);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Events emitted by a pool
              /// @notice Contains all events emitted by the pool
              interface IUniswapV3PoolEvents {
                  /// @notice Emitted exactly once by a pool when #initialize is first called on the pool
                  /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
                  /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
                  /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
                  event Initialize(uint160 sqrtPriceX96, int24 tick);
                  /// @notice Emitted when liquidity is minted for a given position
                  /// @param sender The address that minted the liquidity
                  /// @param owner The owner of the position and recipient of any minted liquidity
                  /// @param tickLower The lower tick of the position
                  /// @param tickUpper The upper tick of the position
                  /// @param amount The amount of liquidity minted to the position range
                  /// @param amount0 How much token0 was required for the minted liquidity
                  /// @param amount1 How much token1 was required for the minted liquidity
                  event Mint(
                      address sender,
                      address indexed owner,
                      int24 indexed tickLower,
                      int24 indexed tickUpper,
                      uint128 amount,
                      uint256 amount0,
                      uint256 amount1
                  );
                  /// @notice Emitted when fees are collected by the owner of a position
                  /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
                  /// @param owner The owner of the position for which fees are collected
                  /// @param tickLower The lower tick of the position
                  /// @param tickUpper The upper tick of the position
                  /// @param amount0 The amount of token0 fees collected
                  /// @param amount1 The amount of token1 fees collected
                  event Collect(
                      address indexed owner,
                      address recipient,
                      int24 indexed tickLower,
                      int24 indexed tickUpper,
                      uint128 amount0,
                      uint128 amount1
                  );
                  /// @notice Emitted when a position's liquidity is removed
                  /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
                  /// @param owner The owner of the position for which liquidity is removed
                  /// @param tickLower The lower tick of the position
                  /// @param tickUpper The upper tick of the position
                  /// @param amount The amount of liquidity to remove
                  /// @param amount0 The amount of token0 withdrawn
                  /// @param amount1 The amount of token1 withdrawn
                  event Burn(
                      address indexed owner,
                      int24 indexed tickLower,
                      int24 indexed tickUpper,
                      uint128 amount,
                      uint256 amount0,
                      uint256 amount1
                  );
                  /// @notice Emitted by the pool for any swaps between token0 and token1
                  /// @param sender The address that initiated the swap call, and that received the callback
                  /// @param recipient The address that received the output of the swap
                  /// @param amount0 The delta of the token0 balance of the pool
                  /// @param amount1 The delta of the token1 balance of the pool
                  /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
                  /// @param liquidity The liquidity of the pool after the swap
                  /// @param tick The log base 1.0001 of price of the pool after the swap
                  event Swap(
                      address indexed sender,
                      address indexed recipient,
                      int256 amount0,
                      int256 amount1,
                      uint160 sqrtPriceX96,
                      uint128 liquidity,
                      int24 tick
                  );
                  /// @notice Emitted by the pool for any flashes of token0/token1
                  /// @param sender The address that initiated the swap call, and that received the callback
                  /// @param recipient The address that received the tokens from flash
                  /// @param amount0 The amount of token0 that was flashed
                  /// @param amount1 The amount of token1 that was flashed
                  /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
                  /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
                  event Flash(
                      address indexed sender,
                      address indexed recipient,
                      uint256 amount0,
                      uint256 amount1,
                      uint256 paid0,
                      uint256 paid1
                  );
                  /// @notice Emitted by the pool for increases to the number of observations that can be stored
                  /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
                  /// just before a mint/swap/burn.
                  /// @param observationCardinalityNextOld The previous value of the next observation cardinality
                  /// @param observationCardinalityNextNew The updated value of the next observation cardinality
                  event IncreaseObservationCardinalityNext(
                      uint16 observationCardinalityNextOld,
                      uint16 observationCardinalityNextNew
                  );
                  /// @notice Emitted when the protocol fee is changed by the pool
                  /// @param feeProtocol0Old The previous value of the token0 protocol fee
                  /// @param feeProtocol1Old The previous value of the token1 protocol fee
                  /// @param feeProtocol0New The updated value of the token0 protocol fee
                  /// @param feeProtocol1New The updated value of the token1 protocol fee
                  event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);
                  /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner
                  /// @param sender The address that collects the protocol fees
                  /// @param recipient The address that receives the collected protocol fees
                  /// @param amount0 The amount of token0 protocol fees that is withdrawn
                  /// @param amount0 The amount of token1 protocol fees that is withdrawn
                  event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title BitMath
              /// @dev This library provides functionality for computing bit properties of an unsigned integer
              library BitMath {
                  /// @notice Returns the index of the most significant bit of the number,
                  ///     where the least significant bit is at index 0 and the most significant bit is at index 255
                  /// @dev The function satisfies the property:
                  ///     x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1)
                  /// @param x the value for which to compute the most significant bit, must be greater than 0
                  /// @return r the index of the most significant bit
                  function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
                      require(x > 0);
                      if (x >= 0x100000000000000000000000000000000) {
                          x >>= 128;
                          r += 128;
                      }
                      if (x >= 0x10000000000000000) {
                          x >>= 64;
                          r += 64;
                      }
                      if (x >= 0x100000000) {
                          x >>= 32;
                          r += 32;
                      }
                      if (x >= 0x10000) {
                          x >>= 16;
                          r += 16;
                      }
                      if (x >= 0x100) {
                          x >>= 8;
                          r += 8;
                      }
                      if (x >= 0x10) {
                          x >>= 4;
                          r += 4;
                      }
                      if (x >= 0x4) {
                          x >>= 2;
                          r += 2;
                      }
                      if (x >= 0x2) r += 1;
                  }
                  /// @notice Returns the index of the least significant bit of the number,
                  ///     where the least significant bit is at index 0 and the most significant bit is at index 255
                  /// @dev The function satisfies the property:
                  ///     (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0)
                  /// @param x the value for which to compute the least significant bit, must be greater than 0
                  /// @return r the index of the least significant bit
                  function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
                      require(x > 0);
                      r = 255;
                      if (x & type(uint128).max > 0) {
                          r -= 128;
                      } else {
                          x >>= 128;
                      }
                      if (x & type(uint64).max > 0) {
                          r -= 64;
                      } else {
                          x >>= 64;
                      }
                      if (x & type(uint32).max > 0) {
                          r -= 32;
                      } else {
                          x >>= 32;
                      }
                      if (x & type(uint16).max > 0) {
                          r -= 16;
                      } else {
                          x >>= 16;
                      }
                      if (x & type(uint8).max > 0) {
                          r -= 8;
                      } else {
                          x >>= 8;
                      }
                      if (x & 0xf > 0) {
                          r -= 4;
                      } else {
                          x >>= 4;
                      }
                      if (x & 0x3 > 0) {
                          r -= 2;
                      } else {
                          x >>= 2;
                      }
                      if (x & 0x1 > 0) r -= 1;
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.5.0;
              /// @title Math functions that do not check inputs or outputs
              /// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks
              library UnsafeMath {
                  /// @notice Returns ceil(x / y)
                  /// @dev division by 0 has unspecified behavior, and must be checked externally
                  /// @param x The dividend
                  /// @param y The divisor
                  /// @return z The quotient, ceil(x / y)
                  function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
                      assembly {
                          z := add(div(x, y), gt(mod(x, y), 0))
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-2.0-or-later
              pragma solidity >=0.4.0;
              /// @title FixedPoint96
              /// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
              /// @dev Used in SqrtPriceMath.sol
              library FixedPoint96 {
                  uint8 internal constant RESOLUTION = 96;
                  uint256 internal constant Q96 = 0x1000000000000000000000000;
              }
              

              File 8 of 30: GnosisSafeProxy
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              
              /// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
              /// @author Richard Meissner - <richard@gnosis.io>
              interface IProxy {
                  function masterCopy() external view returns (address);
              }
              
              /// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
              /// @author Stefan George - <stefan@gnosis.io>
              /// @author Richard Meissner - <richard@gnosis.io>
              contract GnosisSafeProxy {
                  // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                  // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                  address internal singleton;
              
                  /// @dev Constructor function sets address of singleton contract.
                  /// @param _singleton Singleton address.
                  constructor(address _singleton) {
                      require(_singleton != address(0), "Invalid singleton address provided");
                      singleton = _singleton;
                  }
              
                  /// @dev Fallback function forwards all transactions and returns all received return data.
                  fallback() external payable {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                          // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                          if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                              mstore(0, _singleton)
                              return(0, 0x20)
                          }
                          calldatacopy(0, 0, calldatasize())
                          let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                          returndatacopy(0, 0, returndatasize())
                          if eq(success, 0) {
                              revert(0, returndatasize())
                          }
                          return(0, returndatasize())
                      }
                  }
              }
              
              /// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
              /// @author Stefan George - <stefan@gnosis.pm>
              contract GnosisSafeProxyFactory {
                  event ProxyCreation(GnosisSafeProxy proxy, address singleton);
              
                  /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                  /// @param singleton Address of singleton contract.
                  /// @param data Payload for message call sent to new proxy contract.
                  function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
                      proxy = new GnosisSafeProxy(singleton);
                      if (data.length > 0)
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                                  revert(0, 0)
                              }
                          }
                      emit ProxyCreation(proxy, singleton);
                  }
              
                  /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
                  function proxyRuntimeCode() public pure returns (bytes memory) {
                      return type(GnosisSafeProxy).runtimeCode;
                  }
              
                  /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
                  function proxyCreationCode() public pure returns (bytes memory) {
                      return type(GnosisSafeProxy).creationCode;
                  }
              
                  /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
                  ///      This method is only meant as an utility to be called from other methods
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function deployProxyWithNonce(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce
                  ) internal returns (GnosisSafeProxy proxy) {
                      // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
                      bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
                      bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
                      }
                      require(address(proxy) != address(0), "Create2 call failed");
                  }
              
                  /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function createProxyWithNonce(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce
                  ) public returns (GnosisSafeProxy proxy) {
                      proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                      if (initializer.length > 0)
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                                  revert(0, 0)
                              }
                          }
                      emit ProxyCreation(proxy, _singleton);
                  }
              
                  /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
                  function createProxyWithCallback(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce,
                      IProxyCreationCallback callback
                  ) public returns (GnosisSafeProxy proxy) {
                      uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
                      proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
                      if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
                  }
              
                  /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
                  ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
                  ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function calculateCreateProxyWithNonceAddress(
                      address _singleton,
                      bytes calldata initializer,
                      uint256 saltNonce
                  ) external returns (GnosisSafeProxy proxy) {
                      proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                      revert(string(abi.encodePacked(proxy)));
                  }
              }
              
              interface IProxyCreationCallback {
                  function proxyCreated(
                      GnosisSafeProxy proxy,
                      address _singleton,
                      bytes calldata initializer,
                      uint256 saltNonce
                  ) external;
              }

              File 9 of 30: BeefyFeeBatchV4
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
              pragma solidity ^0.8.0;
              import "../utils/ContextUpgradeable.sol";
              import "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
                  address private _owner;
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the deployer as the initial owner.
                   */
                  function __Ownable_init() internal onlyInitializing {
                      __Ownable_init_unchained();
                  }
                  function __Ownable_init_unchained() internal onlyInitializing {
                      _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);
                  }
                  /**
                   * @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[49] 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) (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 IERC20PermitUpgradeable {
                  /**
                   * @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) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20Upgradeable {
                  /**
                   * @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.3) (token/ERC20/utils/SafeERC20.sol)
              pragma solidity ^0.8.0;
              import "../IERC20Upgradeable.sol";
              import "../extensions/IERC20PermitUpgradeable.sol";
              import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
                  using AddressUpgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(
                      IERC20PermitUpgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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))) && AddressUpgradeable.isContract(address(token));
                  }
              }
              // 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 v4.4.1 (utils/Context.sol)
              pragma solidity ^0.8.0;
              import "../proxy/utils/Initializable.sol";
              /**
               * @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 ContextUpgradeable is Initializable {
                  function __Context_init() internal onlyInitializing {
                  }
                  function __Context_init_unchained() internal onlyInitializing {
                  }
                  function _msgSender() internal view virtual returns (address) {
                      return msg.sender;
                  }
                  function _msgData() internal view virtual returns (bytes calldata) {
                      return msg.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
              pragma solidity 0.8.19;
              import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
              import { SafeERC20Upgradeable, IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
              import { IBeefySwapper } from "../interfaces/beefy/IBeefySwapper.sol";
              import { IBeefyRewardPool } from "../interfaces/beefy/IBeefyRewardPool.sol";
              /// @title Beefy fee batch
              /// @author kexley, Beefy
              /// @notice All Beefy fees will flow through to the treasury and the reward pool
              /// @dev Wrapped ETH will build up on this contract and will be swapped via the Beefy Swapper to
              /// the pre-specified tokens and distributed to the treasury and reward pool
              contract BeefyFeeBatchV4 is OwnableUpgradeable {
                  using SafeERC20Upgradeable for IERC20Upgradeable;
                  /// @dev Bundled token information
                  /// @param tokens Token addresses to swap to
                  /// @param index Location of a token in the tokens array
                  /// @param allocPoint Allocation points for this token
                  /// @param totalAllocPoint Total amount of allocation points assigned to tokens in the array
                  struct TokenInfo {
                      address[] tokens;
                      mapping(address => uint256) index;
                      mapping(address => uint256) allocPoint;
                      uint256 totalAllocPoint;
                  }
                  /// @notice Treasury address
                  address public treasury;
                  /// @notice Reward pool address
                  address public rewardPool;
                  /// @notice Swapper address to swap all tokens at
                  address public swapper;
                  /// @notice Treasury fee of the total native received on the contract (1 = 0.1%)
                  uint256 public treasuryFee;
                  /// @notice Duration of reward distributions
                  uint256 public duration;
                  /// @notice Tokens sent to this contract as fees
                  address[] public feeTokens;
                  /// @dev Tokens to be sent to the treasury
                  TokenInfo private _treasuryTokens;
                  /// @dev Tokens to be sent to the reward pool
                  TokenInfo private _rewardTokens;
                  /// @dev Denominator constant
                  uint256 constant private DIVISOR = 1000;
                  /// @notice Fees have been harvested
                  /// @param feeToken Address of the fee token
                  /// @param totalHarvested Total fee amount that has been processed
                  event Harvest(address feeToken, uint256 totalHarvested);
                  /// @notice Treasury fee that has been sent
                  /// @param token Token that has been sent
                  /// @param amount Amount of the token sent
                  event DistributeTreasuryFee(address indexed token, uint256 amount);
                  /// @notice Reward pool has been notified
                  /// @param token Token used as a reward
                  /// @param amount Amount of the token used
                  /// @param duration Duration of the distribution
                  event NotifyRewardPool(address indexed token, uint256 amount, uint256 duration);
                  /// @notice Set fee tokens
                  /// @param tokens Addresses of tokens to be received as fees
                  event SetFeeTokens(address[] tokens);
                  /// @notice Reward pool set
                  /// @param rewardPool New reward pool address
                  event SetRewardPool(address rewardPool);
                  /// @notice Treasury set
                  /// @param treasury New treasury address
                  event SetTreasury(address treasury);
                  /// @notice Swapper set
                  /// @param swapper New swapper address
                  event SetSwapper(address swapper);
                  /// @notice Treasury fee set
                  /// @param fee New fee split for the treasury
                  event SetTreasuryFee(uint256 fee);
                  /// @notice Reward pool duration set
                  /// @param duration New duration of the reward distribution
                  event SetDuration(uint256 duration);
                  /// @notice Rescue an unsupported token
                  /// @param token Address of the token
                  /// @param recipient Address to send the token to
                  event RescueTokens(address token, address recipient);
                  /// @notice Initialize the contract, callable only once
                  /// @param _rewardPool Reward pool address
                  /// @param _treasury Treasury address
                  /// @param _swapper Swapper address
                  /// @param _treasuryFee Treasury fee split
                  function initialize(
                      address _rewardPool,
                      address _treasury,
                      address _swapper,
                      uint256 _treasuryFee 
                  ) external initializer {
                      __Ownable_init();
                      treasury = _treasury;
                      rewardPool = _rewardPool;
                      treasuryFee = _treasuryFee;
                      swapper = _swapper;
                      duration = 7 days;
                  }
                  /// @notice Distribute the fees to the treasury and reward pool
                  function harvest() external {
                      uint256 feeTokenLength = feeTokens.length;
                      for (uint i; i < feeTokenLength;) {
                          address feeToken = feeTokens[i];
                          uint256 fees = IERC20Upgradeable(feeToken).balanceOf(address(this));
                          emit Harvest(feeToken, fees);
                          unchecked { ++i; }
                      }
                      _distribute(true);
                      _distribute(false);
                  }
                  /// @dev Distribute the fees to the treasury or the reward pool
                  /// @param _isTreasury Whether the fees are being distributed to the treasury or the reward pool
                  function _distribute(bool _isTreasury) private {
                      TokenInfo storage tokenInfo = _isTreasury ? _treasuryTokens : _rewardTokens;
                      uint256 tokenLength = tokenInfo.tokens.length;
                      for (uint i; i < tokenLength;) {
                          address token = tokenInfo.tokens[i];
                          uint256 amount;
                          uint256 feeTokenLength = feeTokens.length;
                          for (uint j; j < feeTokenLength;) {
                              address feeToken = feeTokens[j];
                              uint256 feeTokenTotal = IERC20Upgradeable(feeToken).balanceOf(address(this));
                              if (feeToken == token) feeTokenTotal -= amount;
                              uint256 feeAmount = feeTokenTotal
                                  * tokenInfo.allocPoint[token]
                                  / tokenInfo.totalAllocPoint;
                              if (_isTreasury) feeAmount = feeAmount * treasuryFee / DIVISOR;
                              if (feeAmount > 0) {
                                  amount += feeToken == token
                                      ? feeAmount
                                      : IBeefySwapper(swapper).swap(feeToken, token, feeAmount);
                              }
                              unchecked { ++j; }
                          }
                          if (amount > 0) {
                              if (_isTreasury) {
                                  IERC20Upgradeable(token).safeTransfer(treasury, amount);
                                  emit DistributeTreasuryFee(token, amount);
                              } else {
                                  IBeefyRewardPool(rewardPool).notifyRewardAmount(token, amount, duration);
                                  emit NotifyRewardPool(token, amount, duration);
                              }
                          }
                          unchecked { ++i; }
                      }
                  }
                  /// @notice Information for the tokens to be sent to the treasury
                  /// @param _id Index of the treasury token array
                  /// @return token Address of the token
                  /// @return allocPoint Allocation of the treasury fee to be sent in this token
                  function treasuryTokens(uint256 _id) external view returns (address token, uint256 allocPoint) {
                      token = _treasuryTokens.tokens[_id];
                      allocPoint = _treasuryTokens.allocPoint[token];
                  }
                  /// @notice Total allocation points for the treasury tokens
                  /// @return totalAllocPoint Total allocation points
                  function treasuryTotalAllocPoint() external view returns (uint256 totalAllocPoint) {
                      totalAllocPoint = _treasuryTokens.totalAllocPoint;
                  }
                  /// @notice Information for the tokens to be sent to the reward pool
                  /// @param _id Index of the reward token array
                  /// @return token Address of the token
                  /// @return allocPoint Allocation of the rewards to be sent in this token
                  function rewardTokens(uint256 _id) external view returns (address token, uint256 allocPoint) {
                      token = _rewardTokens.tokens[_id];
                      allocPoint = _rewardTokens.allocPoint[token];
                  }
                  /// @notice Total allocation points for the reward tokens
                  /// @return totalAllocPoint Total allocation points
                  function rewardTotalAllocPoint() external view returns (uint256 totalAllocPoint) {
                      totalAllocPoint = _rewardTokens.totalAllocPoint;
                  }
                  /* ----------------------------------- VARIABLE SETTERS ----------------------------------- */
                  /// @notice Set which tokens are sent to this contract as fees
                  /// @param _feeTokens Array of fee token addresses
                  function setFeeTokens(address[] calldata _feeTokens) external onlyOwner {
                      for (uint i; i < feeTokens.length; ++i) {
                          IERC20Upgradeable(feeTokens[i]).forceApprove(swapper, 0);
                      }
                      feeTokens = _feeTokens;
                      for (uint i; i < _feeTokens.length; ++i) {
                          IERC20Upgradeable(_feeTokens[i]).forceApprove(swapper, type(uint).max);
                      }
                      emit SetFeeTokens(_feeTokens);
                  }
                  /// @notice Adjust which tokens and how much the harvest should swap the treasury fee to
                  /// @param _token Address of the token to send to the treasury
                  /// @param _allocPoint How much to swap into the particular token from the treasury fee
                  function setTreasuryAllocPoint(address _token, uint256 _allocPoint) external onlyOwner {
                      _setAllocPoint(_treasuryTokens, _token, _allocPoint);
                  }
                  /// @notice Adjust which tokens and how much the harvest should swap the reward pool fee to
                  /// @param _token Address of the token to send to the reward pool
                  /// @param _allocPoint How much to swap into the particular token from the reward pool fee 
                  function setRewardAllocPoint(address _token, uint256 _allocPoint) external onlyOwner {
                      _setAllocPoint(_rewardTokens, _token, _allocPoint);
                      if (_allocPoint > 0) {
                          IERC20Upgradeable(_token).forceApprove(rewardPool, type(uint).max);
                      } else {
                          IERC20Upgradeable(_token).forceApprove(rewardPool, 0);
                      }
                  }
                  /// @dev Adjust the allocation for treasury or rewards
                  /// @param _tokenInfo Token basket to make changes to
                  /// @param _token Token to change allocation for
                  /// @param _allocPoint New allocation amount
                  function _setAllocPoint(
                      TokenInfo storage _tokenInfo,
                      address _token,
                      uint256 _allocPoint
                  ) internal {
                      if (_tokenInfo.allocPoint[_token] > 0 && _allocPoint == 0) {
                          address endToken = _tokenInfo.tokens[_tokenInfo.tokens.length - 1];
                          _tokenInfo.index[endToken] = _tokenInfo.index[_token];
                          _tokenInfo.tokens[_tokenInfo.index[endToken]] = endToken;
                          _tokenInfo.tokens.pop();
                      } else if (_tokenInfo.allocPoint[_token] == 0 && _allocPoint > 0) {
                          _tokenInfo.index[_token] = _tokenInfo.tokens.length;
                          _tokenInfo.tokens.push(_token);
                      }
                      _tokenInfo.totalAllocPoint -= _tokenInfo.allocPoint[_token];
                      _tokenInfo.totalAllocPoint += _allocPoint;
                      _tokenInfo.allocPoint[_token] = _allocPoint;
                  }
                  /// @notice Set the reward pool
                  /// @param _rewardPool New reward pool address
                  function setRewardPool(address _rewardPool) external onlyOwner {
                      address oldRewardPool = rewardPool;
                      rewardPool = _rewardPool;
                      for (uint i; i < _rewardTokens.tokens.length; ++i) {
                          address token = _rewardTokens.tokens[i];
                          IERC20Upgradeable(token).forceApprove(oldRewardPool, 0);
                          IERC20Upgradeable(token).forceApprove(_rewardPool, type(uint).max);
                      }
                      emit SetRewardPool(_rewardPool);
                  }
                  /// @notice Set the treasury
                  /// @param _treasury New treasury address
                  function setTreasury(address _treasury) external onlyOwner {
                      treasury = _treasury;
                      emit SetTreasury(_treasury);
                  }
                  /// @notice Set the swapper
                  /// @param _swapper New swapper address
                  function setSwapper(address _swapper) external onlyOwner {
                      address oldSwapper = swapper;
                      for (uint i; i < feeTokens.length; ++i) {
                          address feeToken = feeTokens[i];
                          IERC20Upgradeable(feeToken).forceApprove(oldSwapper, 0);
                          IERC20Upgradeable(feeToken).forceApprove(_swapper, type(uint).max);
                      }
                      swapper = _swapper;
                      emit SetSwapper(_swapper);
                  }
                  /// @notice Set the treasury fee
                  /// @param _treasuryFee New treasury fee split
                  function setTreasuryFee(uint256 _treasuryFee) external onlyOwner {
                      if (_treasuryFee > DIVISOR) _treasuryFee = DIVISOR;
                      treasuryFee = _treasuryFee;
                      emit SetTreasuryFee(_treasuryFee);
                  }
                  /// @notice Set the duration of the reward distribution
                  /// @param _duration New duration of the reward distribution
                  function setDuration(uint256 _duration) external onlyOwner {
                      duration = _duration;
                      emit SetDuration(_duration);
                  }
                  /* ------------------------------------- SWEEP TOKENS ------------------------------------- */
                  /// @notice Rescue an unsupported token
                  /// @param _token Address of the token
                  /// @param _recipient Address to send the token to
                  function rescueTokens(address _token, address _recipient) external onlyOwner {
                      uint256 amount = IERC20Upgradeable(_token).balanceOf(address(this));
                      IERC20Upgradeable(_token).safeTransfer(_recipient, amount);
                      emit RescueTokens(_token, _recipient);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.19;
              interface IBeefyRewardPool {
                  function stake(uint256 amount) external;
                  function withdraw(uint256 amount) external;
                  function getReward() external;
                  function earned(address user, address reward) external view returns (uint256);
                  function notifyRewardAmount(address reward, uint256 amount, uint256 duration) external;
                  function removeReward(address reward, address recipient) external;
                  function rescueTokens(address token, address recipient) external;
                  function setWhitelist(address manager, bool whitelisted) external;
                  function transferOwnership(address owner) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.19;
              interface IBeefySwapper {
                  function swap(
                      address fromToken,
                      address toToken,
                      uint256 amountIn
                  ) external returns (uint256 amountOut);
                  function swap(
                      address fromToken,
                      address toToken,
                      uint256 amountIn,
                      uint256 minAmountOut
                  ) external returns (uint256 amountOut);
                  function getAmountOut(
                      address _fromToken,
                      address _toToken,
                      uint256 _amountIn
                  ) external view returns (uint256 amountOut);
              }
              

              File 10 of 30: CurveRouter v1.0
              # @version 0.3.7
              
              """
              @title CurveRouter v1.0
              @author Curve.Fi
              @license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
              @notice Performs up to 5 swaps in a single transaction, can do estimations with get_dy and get_dx
              """
              
              from vyper.interfaces import ERC20
              
              interface StablePool:
                  def exchange(i: int128, j: int128, dx: uint256, min_dy: uint256): payable
                  def exchange_underlying(i: int128, j: int128, dx: uint256, min_dy: uint256): payable
                  def get_dy(i: int128, j: int128, amount: uint256) -> uint256: view
                  def get_dy_underlying(i: int128, j: int128, amount: uint256) -> uint256: view
                  def coins(i: uint256) -> address: view
                  def calc_withdraw_one_coin(token_amount: uint256, i: int128) -> uint256: view
                  def remove_liquidity_one_coin(token_amount: uint256, i: int128, min_amount: uint256): nonpayable
              
              interface CryptoPool:
                  def exchange(i: uint256, j: uint256, dx: uint256, min_dy: uint256): payable
                  def exchange_underlying(i: uint256, j: uint256, dx: uint256, min_dy: uint256): payable
                  def get_dy(i: uint256, j: uint256, amount: uint256) -> uint256: view
                  def get_dy_underlying(i: uint256, j: uint256, amount: uint256) -> uint256: view
                  def calc_withdraw_one_coin(token_amount: uint256, i: uint256) -> uint256: view
                  def remove_liquidity_one_coin(token_amount: uint256, i: uint256, min_amount: uint256): nonpayable
              
              interface CryptoPoolETH:
                  def exchange(i: uint256, j: uint256, dx: uint256, min_dy: uint256, use_eth: bool): payable
              
              interface LendingBasePoolMetaZap:
                  def exchange_underlying(pool: address, i: int128, j: int128, dx: uint256, min_dy: uint256): nonpayable
              
              interface CryptoMetaZap:
                  def get_dy(pool: address, i: uint256, j: uint256, dx: uint256) -> uint256: view
                  def exchange(pool: address, i: uint256, j: uint256, dx: uint256, min_dy: uint256, use_eth: bool): payable
              
              interface StablePool2Coins:
                  def add_liquidity(amounts: uint256[2], min_mint_amount: uint256): payable
                  def calc_token_amount(amounts: uint256[2], is_deposit: bool) -> uint256: view
              
              interface CryptoPool2Coins:
                  def calc_token_amount(amounts: uint256[2]) -> uint256: view
              
              interface StablePool3Coins:
                  def add_liquidity(amounts: uint256[3], min_mint_amount: uint256): payable
                  def calc_token_amount(amounts: uint256[3], is_deposit: bool) -> uint256: view
              
              interface CryptoPool3Coins:
                  def calc_token_amount(amounts: uint256[3]) -> uint256: view
              
              interface StablePool4Coins:
                  def add_liquidity(amounts: uint256[4], min_mint_amount: uint256): payable
                  def calc_token_amount(amounts: uint256[4], is_deposit: bool) -> uint256: view
              
              interface CryptoPool4Coins:
                  def calc_token_amount(amounts: uint256[4]) -> uint256: view
              
              interface StablePool5Coins:
                  def add_liquidity(amounts: uint256[5], min_mint_amount: uint256): payable
                  def calc_token_amount(amounts: uint256[5], is_deposit: bool) -> uint256: view
              
              interface CryptoPool5Coins:
                  def calc_token_amount(amounts: uint256[5]) -> uint256: view
              
              interface LendingStablePool3Coins:
                  def add_liquidity(amounts: uint256[3], min_mint_amount: uint256, use_underlying: bool): payable
                  def remove_liquidity_one_coin(token_amount: uint256, i: int128, min_amount: uint256, use_underlying: bool) -> uint256: nonpayable
              
              interface Llamma:
                  def get_dx(i: uint256, j: uint256, out_amount: uint256) -> uint256: view
              
              interface WETH:
                  def deposit(): payable
                  def withdraw(_amount: uint256): nonpayable
              
              interface stETH:
                  def submit(_refferer: address): payable
              
              interface frxETHMinter:
                  def submit(): payable
              
              interface wstETH:
                  def getWstETHByStETH(_stETHAmount: uint256) -> uint256: view
                  def getStETHByWstETH(_wstETHAmount: uint256) -> uint256: view
                  def wrap(_stETHAmount: uint256) -> uint256: nonpayable
                  def unwrap(_wstETHAmount: uint256) -> uint256: nonpayable
              
              interface sfrxETH:
                  def convertToShares(assets: uint256) -> uint256: view
                  def convertToAssets(shares: uint256) -> uint256: view
                  def deposit(assets: uint256, receiver: address) -> uint256: nonpayable
                  def redeem(shares: uint256, receiver: address, owner: address) -> uint256: nonpayable
              
              interface wBETH:
                  def deposit(referral: address): payable
                  def exchangeRate() -> uint256: view
              
              # SNX
              interface SnxCoin:
                  def currencyKey() -> bytes32: nonpayable
              
              interface Synthetix:
                  def exchangeAtomically(sourceCurrencyKey: bytes32, sourceAmount: uint256, destinationCurrencyKey: bytes32, trackingCode: bytes32, minAmount: uint256) -> uint256: nonpayable
              
              interface SynthetixExchanger:
                  def getAmountsForAtomicExchange(sourceAmount: uint256, sourceCurrencyKey: bytes32, destinationCurrencyKey: bytes32) -> AtomicAmountAndFee: view
              
              interface SynthetixAddressResolver:
                  def getAddress(name: bytes32) -> address: view
              
              # Calc zaps
              interface StableCalc:
                  def calc_token_amount(pool: address, token: address, amounts: uint256[10], n_coins: uint256, deposit: bool, use_underlying: bool) -> uint256: view
                  def get_dx(pool: address, i: int128, j: int128, dy: uint256, n_coins: uint256) -> uint256: view
                  def get_dx_underlying(pool: address, i: int128, j: int128, dy: uint256, n_coins: uint256) -> uint256: view
                  def get_dx_meta(pool: address, i: int128, j: int128, dy: uint256, n_coins: uint256, base_pool: address) -> uint256: view
                  def get_dx_meta_underlying(pool: address, i: int128, j: int128, dy: uint256, n_coins: uint256, base_pool: address, base_token: address) -> uint256: view
              
              interface CryptoCalc:
                  def get_dx(pool: address, i: uint256, j: uint256, dy: uint256, n_coins: uint256) -> uint256: view
                  def get_dx_meta_underlying(pool: address, i: uint256, j: uint256, dy: uint256, n_coins: uint256, base_pool: address, base_token: address) -> uint256: view
              
              
              struct AtomicAmountAndFee:
                  amountReceived: uint256
                  fee: uint256
                  exchangeFeeRate: uint256
              
              
              event Exchange:
                  sender: indexed(address)
                  receiver: indexed(address)
                  route: address[11]
                  swap_params: uint256[5][5]
                  pools: address[5]
                  in_amount: uint256
                  out_amount: uint256
              
              
              ETH_ADDRESS: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
              STETH_ADDRESS: constant(address) = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84
              WSTETH_ADDRESS: constant(address) = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0
              FRXETH_ADDRESS: constant(address) = 0x5E8422345238F34275888049021821E8E08CAa1f
              SFRXETH_ADDRESS: constant(address) = 0xac3E018457B222d93114458476f3E3416Abbe38F
              WBETH_ADDRESS: constant(address) = 0xa2E3356610840701BDf5611a53974510Ae27E2e1
              WETH_ADDRESS: immutable(address)
              
              
              # SNX
              # https://github.com/Synthetixio/synthetix-docs/blob/master/content/addresses.md
              SNX_ADDRESS_RESOLVER: constant(address) = 0x823bE81bbF96BEc0e25CA13170F5AaCb5B79ba83
              SNX_TRACKING_CODE: constant(bytes32) = 0x4355525645000000000000000000000000000000000000000000000000000000  # CURVE
              SNX_EXCHANGER_NAME: constant(bytes32) = 0x45786368616E6765720000000000000000000000000000000000000000000000  # Exchanger
              snx_currency_keys: HashMap[address, bytes32]
              
              # Calc zaps
              STABLE_CALC: immutable(StableCalc)
              CRYPTO_CALC: immutable(CryptoCalc)
              
              is_approved: HashMap[address, HashMap[address, bool]]
              
              
              @external
              @payable
              def __default__():
                  pass
              
              
              @external
              def __init__( _weth: address, _stable_calc: address, _crypto_calc: address, _snx_coins: address[4]):
                  self.is_approved[WSTETH_ADDRESS][WSTETH_ADDRESS] = True
                  self.is_approved[SFRXETH_ADDRESS][SFRXETH_ADDRESS] = True
              
                  WETH_ADDRESS = _weth
                  STABLE_CALC = StableCalc(_stable_calc)
                  CRYPTO_CALC = CryptoCalc(_crypto_calc)
              
                  for _snx_coin in _snx_coins:
                      self.is_approved[_snx_coin][0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F] = True
                      self.snx_currency_keys[_snx_coin] = SnxCoin(_snx_coin).currencyKey()
              
              
              @external
              @payable
              @nonreentrant('lock')
              def exchange(
                  _route: address[11],
                  _swap_params: uint256[5][5],
                  _amount: uint256,
                  _expected: uint256,
                  _pools: address[5]=empty(address[5]),
                  _receiver: address=msg.sender
              ) -> uint256:
                  """
                  @notice Performs up to 5 swaps in a single transaction.
                  @dev Routing and swap params must be determined off-chain. This
                       functionality is designed for gas efficiency over ease-of-use.
                  @param _route Array of [initial token, pool or zap, token, pool or zap, token, ...]
                                The array is iterated until a pool address of 0x00, then the last
                                given token is transferred to `_receiver`
                  @param _swap_params Multidimensional array of [i, j, swap type, pool_type, n_coins] where
                                      i is the index of input token
                                      j is the index of output token
              
                                      The swap_type should be:
                                      1. for `exchange`,
                                      2. for `exchange_underlying`,
                                      3. for underlying exchange via zap: factory stable metapools with lending base pool `exchange_underlying`
                                         and factory crypto-meta pools underlying exchange (`exchange` method in zap)
                                      4. for coin -> LP token "exchange" (actually `add_liquidity`),
                                      5. for lending pool underlying coin -> LP token "exchange" (actually `add_liquidity`),
                                      6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`)
                                      7. for LP token -> lending or fake pool underlying coin "exchange" (actually `remove_liquidity_one_coin`)
                                      8. for ETH <-> WETH, ETH -> stETH or ETH -> frxETH, stETH <-> wstETH, frxETH <-> sfrxETH, ETH -> wBETH
                                      9. for SNX swaps (sUSD, sEUR, sETH, sBTC)
              
                                      pool_type: 1 - stable, 2 - crypto, 3 - tricrypto, 4 - llamma
                                      n_coins is the number of coins in pool
                  @param _amount The amount of input token (`_route[0]`) to be sent.
                  @param _expected The minimum amount received after the final swap.
                  @param _pools Array of pools for swaps via zap contracts. This parameter is only needed for swap_type = 3.
                  @param _receiver Address to transfer the final output token to.
                  @return Received amount of the final output token.
                  """
                  input_token: address = _route[0]
                  output_token: address = empty(address)
                  amount: uint256 = _amount
              
                  # validate / transfer initial token
                  if input_token == ETH_ADDRESS:
                      assert msg.value == amount
                  else:
                      assert msg.value == 0
                      assert ERC20(input_token).transferFrom(msg.sender, self, amount, default_return_value=True)
              
                  for i in range(1, 6):
                      # 5 rounds of iteration to perform up to 5 swaps
                      swap: address = _route[i*2-1]
                      pool: address = _pools[i-1] # Only for Polygon meta-factories underlying swap (swap_type == 6)
                      output_token = _route[i*2]
                      params: uint256[5] = _swap_params[i-1]  # i, j, swap_type, pool_type, n_coins
              
                      if not self.is_approved[input_token][swap]:
                          assert ERC20(input_token).approve(swap, max_value(uint256), default_return_value=True, skip_contract_check=True)
                          self.is_approved[input_token][swap] = True
              
                      eth_amount: uint256 = 0
                      if input_token == ETH_ADDRESS:
                          eth_amount = amount
                      # perform the swap according to the swap type
                      if params[2] == 1:
                          if params[3] == 1:  # stable
                              StablePool(swap).exchange(convert(params[0], int128), convert(params[1], int128), amount, 0, value=eth_amount)
                          else:  # crypto, tricrypto or llamma
                              if input_token == ETH_ADDRESS or output_token == ETH_ADDRESS:
                                  CryptoPoolETH(swap).exchange(params[0], params[1], amount, 0, True, value=eth_amount)
                              else:
                                  CryptoPool(swap).exchange(params[0], params[1], amount, 0)
                      elif params[2] == 2:
                          if params[3] == 1:  # stable
                              StablePool(swap).exchange_underlying(convert(params[0], int128), convert(params[1], int128), amount, 0, value=eth_amount)
                          else:  # crypto or tricrypto
                              CryptoPool(swap).exchange_underlying(params[0], params[1], amount, 0, value=eth_amount)
                      elif params[2] == 3:  # SWAP IS ZAP HERE !!!
                          if params[3] == 1:  # stable
                              LendingBasePoolMetaZap(swap).exchange_underlying(pool, convert(params[0], int128), convert(params[1], int128), amount, 0)
                          else:  # crypto or tricrypto
                              use_eth: bool = input_token == ETH_ADDRESS or output_token == ETH_ADDRESS
                              CryptoMetaZap(swap).exchange(pool, params[0], params[1], amount, 0, use_eth, value=eth_amount)
                      elif params[2] == 4:
                          if params[4] == 2:
                              amounts: uint256[2] = [0, 0]
                              amounts[params[0]] = amount
                              StablePool2Coins(swap).add_liquidity(amounts, 0, value=eth_amount)
                          elif params[4] == 3:
                              amounts: uint256[3] = [0, 0, 0]
                              amounts[params[0]] = amount
                              StablePool3Coins(swap).add_liquidity(amounts, 0, value=eth_amount)
                          elif params[4] == 4:
                              amounts: uint256[4] = [0, 0, 0, 0]
                              amounts[params[0]] = amount
                              StablePool4Coins(swap).add_liquidity(amounts, 0, value=eth_amount)
                          elif params[4] == 5:
                              amounts: uint256[5] = [0, 0, 0, 0, 0]
                              amounts[params[0]] = amount
                              StablePool5Coins(swap).add_liquidity(amounts, 0, value=eth_amount)
                      elif params[2] == 5:
                          amounts: uint256[3] = [0, 0, 0]
                          amounts[params[0]] = amount
                          LendingStablePool3Coins(swap).add_liquidity(amounts, 0, True, value=eth_amount) # example: aave on Polygon
                      elif params[2] == 6:
                          if params[3] == 1:  # stable
                              StablePool(swap).remove_liquidity_one_coin(amount, convert(params[1], int128), 0)
                          else:  # crypto or tricrypto
                              CryptoPool(swap).remove_liquidity_one_coin(amount, params[1], 0)  # example: atricrypto3 on Polygon
                      elif params[2] == 7:
                          LendingStablePool3Coins(swap).remove_liquidity_one_coin(amount, convert(params[1], int128), 0, True) # example: aave on Polygon
                      elif params[2] == 8:
                          if input_token == ETH_ADDRESS and output_token == WETH_ADDRESS:
                              WETH(swap).deposit(value=amount)
                          elif input_token == WETH_ADDRESS and output_token == ETH_ADDRESS:
                              WETH(swap).withdraw(amount)
                          elif input_token == ETH_ADDRESS and output_token == STETH_ADDRESS:
                              stETH(swap).submit(0x0000000000000000000000000000000000000000, value=amount)
                          elif input_token == ETH_ADDRESS and output_token == FRXETH_ADDRESS:
                              frxETHMinter(swap).submit(value=amount)
                          elif input_token == STETH_ADDRESS and output_token == WSTETH_ADDRESS:
                              wstETH(swap).wrap(amount)
                          elif input_token == WSTETH_ADDRESS and output_token == STETH_ADDRESS:
                              wstETH(swap).unwrap(amount)
                          elif input_token == FRXETH_ADDRESS and output_token == SFRXETH_ADDRESS:
                              sfrxETH(swap).deposit(amount, self)
                          elif input_token == SFRXETH_ADDRESS and output_token == FRXETH_ADDRESS:
                              sfrxETH(swap).redeem(amount, self, self)
                          elif input_token == ETH_ADDRESS and output_token == WBETH_ADDRESS:
                              wBETH(swap).deposit(0xeCb456EA5365865EbAb8a2661B0c503410e9B347, value=amount)
                          else:
                              raise "Swap type 8 is only for ETH <-> WETH, ETH -> stETH or ETH -> frxETH, stETH <-> wstETH, frxETH <-> sfrxETH, ETH -> wBETH"
                      elif params[2] == 9:
                          Synthetix(swap).exchangeAtomically(self.snx_currency_keys[input_token], amount, self.snx_currency_keys[output_token], SNX_TRACKING_CODE, 0)
                      else:
                          raise "Bad swap type"
              
                      # update the amount received
                      if output_token == ETH_ADDRESS:
                          amount = self.balance
                      else:
                          amount = ERC20(output_token).balanceOf(self)
              
                      # sanity check, if the routing data is incorrect we will have a 0 balance and that is bad
                      assert amount != 0, "Received nothing"
              
                      # check if this was the last swap
                      if i == 5 or _route[i*2+1] == empty(address):
                          break
                      # if there is another swap, the output token becomes the input for the next round
                      input_token = output_token
              
                  amount -= 1  # Change non-zero -> non-zero costs less gas than zero -> non-zero
                  assert amount >= _expected, "Slippage"
              
                  # transfer the final token to the receiver
                  if output_token == ETH_ADDRESS:
                      raw_call(_receiver, b"", value=amount)
                  else:
                      assert ERC20(output_token).transfer(_receiver, amount, default_return_value=True)
              
                  log Exchange(msg.sender, _receiver, _route, _swap_params, _pools, _amount, amount)
              
                  return amount
              
              
              @view
              @external
              def get_dy(
                  _route: address[11],
                  _swap_params: uint256[5][5],
                  _amount: uint256,
                  _pools: address[5]=empty(address[5])
              ) -> uint256:
                  """
                  @notice Get amount of the final output token received in an exchange
                  @dev Routing and swap params must be determined off-chain. This
                       functionality is designed for gas efficiency over ease-of-use.
                  @param _route Array of [initial token, pool or zap, token, pool or zap, token, ...]
                                The array is iterated until a pool address of 0x00, then the last
                                given token is transferred to `_receiver`
                  @param _swap_params Multidimensional array of [i, j, swap type, pool_type, n_coins] where
                                      i is the index of input token
                                      j is the index of output token
              
                                      The swap_type should be:
                                      1. for `exchange`,
                                      2. for `exchange_underlying`,
                                      3. for underlying exchange via zap: factory stable metapools with lending base pool `exchange_underlying`
                                         and factory crypto-meta pools underlying exchange (`exchange` method in zap)
                                      4. for coin -> LP token "exchange" (actually `add_liquidity`),
                                      5. for lending pool underlying coin -> LP token "exchange" (actually `add_liquidity`),
                                      6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`)
                                      7. for LP token -> lending or fake pool underlying coin "exchange" (actually `remove_liquidity_one_coin`)
                                      8. for ETH <-> WETH, ETH -> stETH or ETH -> frxETH, stETH <-> wstETH, frxETH <-> sfrxETH, ETH -> wBETH
                                      9. for SNX swaps (sUSD, sEUR, sETH, sBTC)
              
                                      pool_type: 1 - stable, 2 - crypto, 3 - tricrypto, 4 - llamma
                                      n_coins is the number of coins in pool
                  @param _amount The amount of input token (`_route[0]`) to be sent.
                  @param _pools Array of pools for swaps via zap contracts. This parameter is needed only for swap_type = 3.
                  @return Expected amount of the final output token.
                  """
                  input_token: address = _route[0]
                  output_token: address = empty(address)
                  amount: uint256 = _amount
              
                  for i in range(1, 6):
                      # 5 rounds of iteration to perform up to 5 swaps
                      swap: address = _route[i*2-1]
                      pool: address = _pools[i-1] # Only for Polygon meta-factories underlying swap (swap_type == 4)
                      output_token = _route[i * 2]
                      params: uint256[5] = _swap_params[i-1]  # i, j, swap_type, pool_type, n_coins
              
                      # Calc output amount according to the swap type
                      if params[2] == 1:
                          if params[3] == 1:  # stable
                              amount = StablePool(swap).get_dy(convert(params[0], int128), convert(params[1], int128), amount)
                          else:  # crypto or llamma
                              amount = CryptoPool(swap).get_dy(params[0], params[1], amount)
                      elif params[2] == 2:
                          if params[3] == 1:  # stable
                              amount = StablePool(swap).get_dy_underlying(convert(params[0], int128), convert(params[1], int128), amount)
                          else:  # crypto
                              amount = CryptoPool(swap).get_dy_underlying(params[0], params[1], amount)
                      elif params[2] == 3:  # SWAP IS ZAP HERE !!!
                          if params[3] == 1:  # stable
                              amount = StablePool(pool).get_dy_underlying(convert(params[0], int128), convert(params[1], int128), amount)
                          else:  # crypto
                              amount = CryptoMetaZap(swap).get_dy(pool, params[0], params[1], amount)
                      elif params[2] in [4, 5]:
                          if params[3] == 1: # stable
                              amounts: uint256[10] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                              amounts[params[0]] = amount
                              amount = STABLE_CALC.calc_token_amount(swap, output_token, amounts, params[4], True, True)
                          else:
                              # Tricrypto pools have stablepool interface for calc_token_amount
                              if params[4] == 2:
                                  amounts: uint256[2] = [0, 0]
                                  amounts[params[0]] = amount
                                  if params[3] == 2:  # crypto
                                      amount = CryptoPool2Coins(swap).calc_token_amount(amounts)
                                  else:  # tricrypto
                                      amount = StablePool2Coins(swap).calc_token_amount(amounts, True)
                              elif params[4] == 3:
                                  amounts: uint256[3] = [0, 0, 0]
                                  amounts[params[0]] = amount
                                  if params[3] == 2:  # crypto
                                      amount = CryptoPool3Coins(swap).calc_token_amount(amounts)
                                  else:  # tricrypto
                                      amount = StablePool3Coins(swap).calc_token_amount(amounts, True)
                              elif params[4] == 4:
                                  amounts: uint256[4] = [0, 0, 0, 0]
                                  amounts[params[0]] = amount
                                  if params[3] == 2:  # crypto
                                      amount = CryptoPool4Coins(swap).calc_token_amount(amounts)
                                  else:  # tricrypto
                                      amount = StablePool4Coins(swap).calc_token_amount(amounts, True)
                              elif params[4] == 5:
                                  amounts: uint256[5] = [0, 0, 0, 0, 0]
                                  amounts[params[0]] = amount
                                  if params[3] == 2:  # crypto
                                      amount = CryptoPool5Coins(swap).calc_token_amount(amounts)
                                  else:  # tricrypto
                                      amount = StablePool5Coins(swap).calc_token_amount(amounts, True)
                      elif params[2] in [6, 7]:
                          if params[3] == 1:  # stable
                              amount = StablePool(swap).calc_withdraw_one_coin(amount, convert(params[1], int128))
                          else:  # crypto
                              amount = CryptoPool(swap).calc_withdraw_one_coin(amount, params[1])
                      elif params[2] == 8:
                          if input_token == WETH_ADDRESS or output_token == WETH_ADDRESS or \
                                  (input_token == ETH_ADDRESS and output_token == STETH_ADDRESS) or \
                                  (input_token == ETH_ADDRESS and output_token == FRXETH_ADDRESS):
                              # ETH <--> WETH rate is 1:1
                              # ETH ---> stETH rate is 1:1
                              # ETH ---> frxETH rate is 1:1
                              pass
                          elif input_token == WSTETH_ADDRESS:
                              amount = wstETH(swap).getStETHByWstETH(amount)
                          elif output_token == WSTETH_ADDRESS:
                              amount = wstETH(swap).getWstETHByStETH(amount)
                          elif input_token == SFRXETH_ADDRESS:
                              amount = sfrxETH(swap).convertToAssets(amount)
                          elif output_token == SFRXETH_ADDRESS:
                              amount = sfrxETH(swap).convertToShares(amount)
                          elif output_token == WBETH_ADDRESS:
                              amount = amount * 10**18 / wBETH(swap).exchangeRate()
                          else:
                              raise "Swap type 8 is only for ETH <-> WETH, ETH -> stETH or ETH -> frxETH, stETH <-> wstETH, frxETH <-> sfrxETH, ETH -> wBETH"
                      elif params[2] == 9:
                          snx_exchanger: address = SynthetixAddressResolver(SNX_ADDRESS_RESOLVER).getAddress(SNX_EXCHANGER_NAME)
                          atomic_amount_and_fee: AtomicAmountAndFee = SynthetixExchanger(snx_exchanger).getAmountsForAtomicExchange(
                              amount, self.snx_currency_keys[input_token], self.snx_currency_keys[output_token]
                          )
                          amount = atomic_amount_and_fee.amountReceived
                      else:
                          raise "Bad swap type"
              
                      # check if this was the last swap
                      if i == 5 or _route[i*2+1] == empty(address):
                          break
                      # if there is another swap, the output token becomes the input for the next round
                      input_token = output_token
              
                  return amount - 1
              
              
              @view
              @external
              def get_dx(
                  _route: address[11],
                  _swap_params: uint256[5][5],
                  _out_amount: uint256,
                  _pools: address[5],
                  _base_pools: address[5]=empty(address[5]),
                  _base_tokens: address[5]=empty(address[5]),
              ) -> uint256:
                  """
                  @notice Calculate the input amount required to receive the desired output amount
                  @dev Routing and swap params must be determined off-chain. This
                       functionality is designed for gas efficiency over ease-of-use.
                  @param _route Array of [initial token, pool or zap, token, pool or zap, token, ...]
                                The array is iterated until a pool address of 0x00, then the last
                                given token is transferred to `_receiver`
                  @param _swap_params Multidimensional array of [i, j, swap type, pool_type, n_coins] where
                                      i is the index of input token
                                      j is the index of output token
              
                                      The swap_type should be:
                                      1. for `exchange`,
                                      2. for `exchange_underlying`,
                                      3. for underlying exchange via zap: factory stable metapools with lending base pool `exchange_underlying`
                                         and factory crypto-meta pools underlying exchange (`exchange` method in zap)
                                      4. for coin -> LP token "exchange" (actually `add_liquidity`),
                                      5. for lending pool underlying coin -> LP token "exchange" (actually `add_liquidity`),
                                      6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`)
                                      7. for LP token -> lending or fake pool underlying coin "exchange" (actually `remove_liquidity_one_coin`)
                                      8. for ETH <-> WETH, ETH -> stETH or ETH -> frxETH, stETH <-> wstETH, frxETH <-> sfrxETH, ETH -> wBETH
                                      9. for SNX swaps (sUSD, sEUR, sETH, sBTC)
              
                                      pool_type: 1 - stable, 2 - crypto, 3 - tricrypto, 4 - llamma
                                      n_coins is the number of coins in pool
                  @param _out_amount The desired amount of output coin to receive.
                  @param _pools Array of pools.
                  @param _base_pools Array of base pools (for meta pools).
                  @param _base_tokens Array of base lp tokens (for meta pools). Should be a zap address for double meta pools.
                  @return Required amount of input token to send.
                  """
                  amount: uint256 = _out_amount
              
                  for _i in range(1, 6):
                      # 5 rounds of iteration to perform up to 5 swaps
                      i: uint256 = 6 - _i
                      swap: address = _route[i*2-1]
                      if swap == empty(address):
                          continue
                      input_token: address = _route[(i - 1) * 2]
                      output_token: address = _route[i * 2]
                      pool: address = _pools[i-1]
                      base_pool: address = _base_pools[i-1]
                      base_token: address = _base_tokens[i-1]
                      params: uint256[5] = _swap_params[i-1]  # i, j, swap_type, pool_type, n_coins
                      n_coins: uint256 = params[4]
              
              
                      # Calc a required input amount according to the swap type
                      if params[2] == 1:
                          if params[3] == 1:  # stable
                              if base_pool == empty(address):  # non-meta
                                  amount = STABLE_CALC.get_dx(pool, convert(params[0], int128), convert(params[1], int128), amount, n_coins)
                              else:
                                  amount = STABLE_CALC.get_dx_meta(pool, convert(params[0], int128), convert(params[1], int128), amount, n_coins, base_pool)
                          elif params[3] in [2, 3]:  # crypto or tricrypto
                              amount = CRYPTO_CALC.get_dx(pool, params[0], params[1], amount, n_coins)
                          else:  # llamma
                              amount = Llamma(pool).get_dx(params[0], params[1], amount)
                      elif params[2] in [2, 3]:
                          if params[3] == 1:  # stable
                              if base_pool == empty(address):  # non-meta
                                  amount = STABLE_CALC.get_dx_underlying(pool, convert(params[0], int128), convert(params[1], int128), amount, n_coins)
                              else:
                                  amount = STABLE_CALC.get_dx_meta_underlying(pool, convert(params[0], int128), convert(params[1], int128), amount, n_coins, base_pool, base_token)
                          else:  # crypto
                              amount = CRYPTO_CALC.get_dx_meta_underlying(pool, params[0], params[1], amount, n_coins, base_pool, base_token)
                      elif params[2] in [4, 5]:
                          # This is not correct. Should be something like calc_add_one_coin. But tests say that it's precise enough.
                          if params[3] == 1:  # stable
                              amount = StablePool(swap).calc_withdraw_one_coin(amount, convert(params[0], int128))
                          else:  # crypto
                              amount = CryptoPool(swap).calc_withdraw_one_coin(amount, params[0])
                      elif params[2] in [6, 7]:
                          if params[3] == 1: # stable
                              amounts: uint256[10] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                              amounts[params[1]] = amount
                              amount = STABLE_CALC.calc_token_amount(swap, input_token, amounts, n_coins, False, True)
                          else:
                              # Tricrypto pools have stablepool interface for calc_token_amount
                              if n_coins == 2:
                                  amounts: uint256[2] = [0, 0]
                                  amounts[params[1]] = amount
                                  if params[3] == 2:  # crypto
                                      amount = CryptoPool2Coins(swap).calc_token_amount(amounts)  # This is not correct
                                  else:  # tricrypto
                                      amount = StablePool2Coins(swap).calc_token_amount(amounts, False)
                              elif n_coins == 3:
                                  amounts: uint256[3] = [0, 0, 0]
                                  amounts[params[1]] = amount
                                  if params[3] == 2:  # crypto
                                      amount = CryptoPool3Coins(swap).calc_token_amount(amounts)  # This is not correct
                                  else:  # tricrypto
                                      amount = StablePool3Coins(swap).calc_token_amount(amounts, False)
                              elif n_coins == 4:
                                  amounts: uint256[4] = [0, 0, 0, 0]
                                  amounts[params[1]] = amount
                                  if params[3] == 2:  # crypto
                                      amount = CryptoPool4Coins(swap).calc_token_amount(amounts)  # This is not correct
                                  else:  # tricrypto
                                      amount = StablePool4Coins(swap).calc_token_amount(amounts, False)
                              elif n_coins == 5:
                                  amounts: uint256[5] = [0, 0, 0, 0, 0]
                                  amounts[params[1]] = amount
                                  if params[3] == 2:  # crypto
                                      amount = CryptoPool5Coins(swap).calc_token_amount(amounts)  # This is not correct
                                  else:  # tricrypto
                                      amount = StablePool5Coins(swap).calc_token_amount(amounts, False)
                      elif params[2] == 8:
                          if input_token == WETH_ADDRESS or output_token == WETH_ADDRESS or \
                                  (input_token == ETH_ADDRESS and output_token == STETH_ADDRESS) or \
                                  (input_token == ETH_ADDRESS and output_token == FRXETH_ADDRESS):
                              # ETH <--> WETH rate is 1:1
                              # ETH ---> stETH rate is 1:1
                              # ETH ---> frxETH rate is 1:1
                              pass
                          elif input_token == WSTETH_ADDRESS:
                              amount = wstETH(swap).getWstETHByStETH(amount)
                          elif output_token == WSTETH_ADDRESS:
                              amount = wstETH(swap).getStETHByWstETH(amount)
                          elif input_token == SFRXETH_ADDRESS:
                              amount = sfrxETH(swap).convertToShares(amount)
                          elif output_token == SFRXETH_ADDRESS:
                              amount = sfrxETH(swap).convertToAssets(amount)
                          elif output_token == WBETH_ADDRESS:
                              amount = amount * wBETH(swap).exchangeRate() / 10**18
                          else:
                              raise "Swap type 8 is only for ETH <-> WETH, ETH -> stETH or ETH -> frxETH, stETH <-> wstETH, frxETH <-> sfrxETH, ETH -> wBETH"
                      elif params[2] == 9:
                          snx_exchanger: address = SynthetixAddressResolver(SNX_ADDRESS_RESOLVER).getAddress(SNX_EXCHANGER_NAME)
                          atomic_amount_and_fee: AtomicAmountAndFee = SynthetixExchanger(snx_exchanger).getAmountsForAtomicExchange(
                              10**18, self.snx_currency_keys[input_token], self.snx_currency_keys[output_token]
                          )
                          amount = amount * 10**18 / atomic_amount_and_fee.amountReceived
                      else:
                          raise "Bad swap type"
              
                  return amount

              File 11 of 30: CurveTricryptoOptimizedWETH
              # @version 0.3.9
              
              """
              @title CurveTricryptoOptimizedWETH
              @author Curve.Fi
              @license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
              @notice A Curve AMM pool for 3 unpegged assets (e.g. ETH, BTC, 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 geometric_mean(_x: uint256[N_COINS]) -> uint256: view
                  def wad_exp(_power: int256) -> uint256: view
                  def cbrt(x: uint256) -> uint256: view
                  def reduction_coefficient(
                      x: uint256[N_COINS], fee_gamma: uint256
                  ) -> 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[N_COINS-1]: view
              
              interface WETH:
                  def deposit(): payable
                  def withdraw(_amount: uint256): nonpayable
              
              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 CommitNewParameters:
                  deadline: indexed(uint256)
                  mid_fee: uint256
                  out_fee: uint256
                  fee_gamma: uint256
                  allowed_extra_profit: uint256
                  adjustment_step: uint256
                  ma_time: 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
              
              
              # ----------------------- Storage/State Variables ----------------------------
              
              WETH20: public(immutable(address))
              
              N_COINS: constant(uint256) = 3
              PRECISION: constant(uint256) = 10**18  # <------- The precision to convert to.
              A_MULTIPLIER: constant(uint256) = 10000
              packed_precisions: uint256
              
              MATH: public(immutable(Math))
              coins: public(immutable(address[N_COINS]))
              factory: public(address)
              
              price_scale_packed: uint256  # <------------------------ Internal price scale.
              price_oracle_packed: uint256  # <------- Price target given by moving average.
              
              last_prices_packed: uint256
              last_prices_timestamp: public(uint256)
              
              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.
              
              future_packed_rebalancing_params: uint256
              
              # ---------------- Fee params that determine dynamic fees --------------------
              
              packed_fee_params: public(uint256)  # <---- Packs mid_fee, out_fee, fee_gamma.
              future_packed_fee_params: uint256
              
              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 ---------------------------------------
              
              admin_actions_deadline: public(uint256)
              
              ADMIN_ACTIONS_DELAY: constant(uint256) = 3 * 86400
              MIN_RAMP_TIME: constant(uint256) = 86400
              
              MIN_A: constant(uint256) = N_COINS**N_COINS * A_MULTIPLIER / 100
              MAX_A: constant(uint256) = 1000 * A_MULTIPLIER * N_COINS**N_COINS
              MAX_A_CHANGE: constant(uint256) = 10
              MIN_GAMMA: constant(uint256) = 10**10
              MAX_GAMMA: constant(uint256) = 5 * 10**16
              
              PRICE_SIZE: constant(uint128) = 256 / (N_COINS - 1)
              PRICE_MASK: constant(uint256) = 2**PRICE_SIZE - 1
              
              # ----------------------- ERC20 Specific vars --------------------------------
              
              name: public(immutable(String[64]))
              symbol: public(immutable(String[32]))
              decimals: public(constant(uint8)) = 18
              version: public(constant(String[8])) = "v2.0.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,
                  _weth: address,
                  _salt: bytes32,
                  packed_precisions: uint256,
                  packed_A_gamma: uint256,
                  packed_fee_params: uint256,
                  packed_rebalancing_params: uint256,
                  packed_prices: uint256,
              ):
              
                  WETH20 = _weth
                  MATH = Math(_math)
              
                  self.factory = msg.sender
              
                  name = _name
                  symbol = _symbol
                  coins = _coins
              
                  self.packed_precisions = packed_precisions  # <------- Precisions of coins
                  #                            are calculated as 10**(18 - coin.decimals()).
              
                  self.initial_A_gamma = packed_A_gamma  # <------------------- A and gamma.
                  self.future_A_gamma = packed_A_gamma
              
                  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.price_scale_packed = packed_prices
                  self.price_oracle_packed = packed_prices
                  self.last_prices_packed = packed_prices
                  self.last_prices_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 ------------------
              
              
              @payable
              @external
              def __default__():
                  if msg.value > 0:
                      assert WETH20 in coins
              
              
              @internal
              def _transfer_in(
                  _coin: address,
                  dx: uint256,
                  dy: uint256,
                  mvalue: uint256,
                  callbacker: address,
                  callback_sig: bytes32,
                  sender: address,
                  receiver: address,
                  use_eth: bool
              ):
                  """
                  @notice Transfers `_coin` from `sender` to `self` and calls `callback_sig`
                          if it is not empty.
                  @dev The callback sig must have the following args:
                       sender: address
                       receiver: address
                       coin: address
                       dx: uint256
                       dy: uint256
                  @params _coin address of the coin to transfer in.
                  @params dx amount of `_coin` to transfer into the pool.
                  @params dy amount of `_coin` to transfer out of the pool.
                  @params mvalue msg.value if the transfer is ETH, 0 otherwise.
                  @params callbacker address to call `callback_sig` on.
                  @params callback_sig signature of the callback function.
                  @params sender address to transfer `_coin` from.
                  @params receiver address to transfer `_coin` to.
                  @params use_eth True if the transfer is ETH, False otherwise.
                  """
              
                  if use_eth and _coin == WETH20:
                      assert mvalue == dx  # dev: incorrect eth amount
                  else:
                      assert mvalue == 0  # dev: nonzero eth amount
              
                      if callback_sig == empty(bytes32):
              
                          assert ERC20(_coin).transferFrom(
                              sender, self, dx, default_return_value=True
                          )
              
                      else:
              
                          # --------- This part of the _transfer_in logic is only accessible
                          #                                                    by _exchange.
              
                          #                 First call callback logic and then check if pool
                          #                  gets dx amounts of _coins[i], revert otherwise.
                          b: uint256 = ERC20(_coin).balanceOf(self)
                          raw_call(
                              callbacker,
                              concat(
                                  slice(callback_sig, 0, 4),
                                  _abi_encode(sender, receiver, _coin, dx, dy)
                              )
                          )
                          assert ERC20(_coin).balanceOf(self) - b == dx  # dev: callback didn't give us coins
                          #                                          ^------ note: dx cannot
                          #                   be 0, so the contract MUST receive some _coin.
              
                      if _coin == WETH20:
                          WETH(WETH20).withdraw(dx)  # <--------- if WETH was transferred in
                          #           previous step and `not use_eth`, withdraw WETH to ETH.
              
              
              @internal
              def _transfer_out(
                  _coin: address, _amount: uint256, use_eth: bool, receiver: address
              ):
                  """
                  @notice Transfer a single token from the pool to receiver.
                  @dev This function is called by `remove_liquidity` and
                       `remove_liquidity_one` and `_exchange` methods.
                  @params _coin Address of the token to transfer out
                  @params _amount Amount of token to transfer out
                  @params use_eth Whether to transfer ETH or not
                  @params receiver Address to send the tokens to
                  """
              
                  if use_eth and _coin == WETH20:
                      raw_call(receiver, b"", value=_amount)
                  else:
                      if _coin == WETH20:
                          WETH(WETH20).deposit(value=_amount)
              
                      assert ERC20(_coin).transfer(
                          receiver, _amount, default_return_value=True
                      )
              
              
              # -------------------------- AMM Main Functions ------------------------------
              
              
              @payable
              @external
              @nonreentrant("lock")
              def exchange(
                  i: uint256,
                  j: uint256,
                  dx: uint256,
                  min_dy: uint256,
                  use_eth: bool = False,
                  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 use_eth True if the input coin is native token, False otherwise
                  @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
                  """
                  return self._exchange(
                      msg.sender,
                      msg.value,
                      i,
                      j,
                      dx,
                      min_dy,
                      use_eth,
                      receiver,
                      empty(address),
                      empty(bytes32)
                  )
              
              
              @payable
              @external
              @nonreentrant('lock')
              def exchange_underlying(
                  i: uint256,
                  j: uint256,
                  dx: uint256,
                  min_dy: uint256,
                  receiver: address = msg.sender
              ) -> uint256:
                  """
                  @notice Exchange using native token transfers.
                  @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
                  """
                  return self._exchange(
                      msg.sender,
                      msg.value,
                      i,
                      j,
                      dx,
                      min_dy,
                      True,
                      receiver,
                      empty(address),
                      empty(bytes32)
                  )
              
              
              @external
              @nonreentrant('lock')
              def exchange_extended(
                  i: uint256,
                  j: uint256,
                  dx: uint256,
                  min_dy: uint256,
                  use_eth: bool,
                  sender: address,
                  receiver: address,
                  cb: bytes32
              ) -> uint256:
                  """
                  @notice Exchange with callback method.
                  @dev This method does not allow swapping in native token, but does allow
                       swaps that transfer out native token from the pool.
                  @dev Does not allow flashloans
                  @dev One use-case is to reduce the number of redundant ERC20 token
                       transfers in zaps.
                  @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 use_eth True if output is native token, False otherwise
                  @param sender Address to transfer input coin from
                  @param receiver Address to send the output coin to
                  @param cb Callback signature
                  @return uint256 Amount of tokens at index j received by the `receiver`
                  """
              
                  assert cb != empty(bytes32)  # dev: No callback specified
                  return self._exchange(
                      sender, 0, i, j, dx, min_dy, use_eth, receiver, msg.sender, cb
                  )  # callbacker should never be self ------------------^
              
              
              @payable
              @external
              @nonreentrant("lock")
              def add_liquidity(
                  amounts: uint256[N_COINS],
                  min_mint_amount: uint256,
                  use_eth: bool = False,
                  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 use_eth True if native token is being added to the pool.
                  @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])
                  xx: 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] + amounts[2] > 0  # dev: no coins to add
              
                  # --------------------- Get prices, balances -----------------------------
              
                  precisions: uint256[N_COINS] = self._unpack(self.packed_precisions)
                  packed_price_scale: uint256 = self.price_scale_packed
                  price_scale: uint256[N_COINS-1] = self._unpack_prices(packed_price_scale)
              
                  # -------------------------------------- Update balances and calculate xp.
                  xp_old: uint256[N_COINS] = xp
                  for i in range(N_COINS):
                      bal: uint256 = xp[i] + amounts[i]
                      xp[i] = bal
                      self.balances[i] = bal
                  xx = xp
              
                  xp[0] *= precisions[0]
                  xp_old[0] *= precisions[0]
                  for i in range(1, N_COINS):
                      xp[i] = unsafe_div(xp[i] * price_scale[i-1] * precisions[i], PRECISION)
                      xp_old[i] = unsafe_div(
                          xp_old[i] * unsafe_mul(price_scale[i-1], precisions[i]),
                          PRECISION
                      )
              
                  # ---------------- transferFrom token into the pool ----------------------
              
                  for i in range(N_COINS):
              
                      if amounts[i] > 0:
              
                          if coins[i] == WETH20:
              
                              self._transfer_in(
                                  coins[i],
                                  amounts[i],
                                  0,  # <-----------------------------------
                                  msg.value,  #                             | No callbacks
                                  empty(address),  # <----------------------| for
                                  empty(bytes32),  # <----------------------| add_liquidity.
                                  msg.sender,  #                            |
                                  empty(address),  # <-----------------------
                                  use_eth
                              )
              
                          else:
              
                              self._transfer_in(
                                  coins[i],
                                  amounts[i],
                                  0,
                                  0,  # <----------------- mvalue = 0 if coin is not WETH20.
                                  empty(address),
                                  empty(bytes32),
                                  msg.sender,
                                  empty(address),
                                  False  # <-------- use_eth is False if coin is not WETH20.
                              )
              
                          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)  # <------------------------- 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)
              
                      packed_price_scale = self.tweak_price(A_gamma, xp, D, 0)
              
                  else:
              
                      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 AddLiquidity(
                      receiver, amounts, d_token_fee, token_supply, packed_price_scale
                  )
              
                  self._claim_admin_fees()  # <--------------------------- Claim admin fees.
              
                  return d_token
              
              
              @external
              @nonreentrant("lock")
              def remove_liquidity(
                  _amount: uint256,
                  min_amounts: uint256[N_COINS],
                  use_eth: bool = False,
                  receiver: address = msg.sender,
                  claim_admin_fees: bool = True,
              ) -> 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 use_eth Whether to withdraw ETH or not
                  @param receiver Address to send the withdrawn tokens to
                  @param claim_admin_fees If True, call self._claim_admin_fees(). Default is True.
                  @return uint256[3] Amount of pool tokens received by the `receiver`
                  """
                  amount: uint256 = _amount
                  balances: uint256[N_COINS] = self.balances
                  d_balances: uint256[N_COINS] = empty(uint256[N_COINS])
              
                  if claim_admin_fees:
                      self._claim_admin_fees()  # <------ We claim fees so that the DAO gets
                      #         paid before withdrawal. In emergency cases, set it to False.
              
                  # -------------------------------------------------------- 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):
              
                          d_balances[i] = balances[i]
                          self.balances[i] = 0  # <------------------------- Empty the pool.
              
                  else:  # <-------------------------------------------------------- Case 1.
              
                      amount -= 1  # <---- To prevent rounding errors, favor LPs a tiny bit.
              
                      for i in range(N_COINS):
                          d_balances[i] = balances[i] * amount / total_supply
                          assert d_balances[i] >= min_amounts[i]
                          self.balances[i] = balances[i] - d_balances[i]
                          balances[i] = d_balances[i]  # <-- Now it's the amounts going out.
              
                  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):
                      self._transfer_out(coins[i], d_balances[i], use_eth, receiver)
              
                  log RemoveLiquidity(msg.sender, balances, total_supply - _amount)
              
                  return d_balances
              
              
              @external
              @nonreentrant("lock")
              def remove_liquidity_one_coin(
                  token_amount: uint256,
                  i: uint256,
                  min_amount: uint256,
                  use_eth: bool = False,
                  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 use_eth Whether to withdraw ETH or not
                  @param receiver Address to send the withdrawn tokens to
                  @return Amount of tokens at index i received by the `receiver`
                  """
              
                  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
              
                  # ---------------------------- Claim admin fees before removing liquidity.
                  self._claim_admin_fees()
              
                  # ------------------------------------------------------------------------
              
                  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"
              
                  # ------------------------- Transfers ------------------------------------
              
                  self.balances[i] -= dy
                  self.burnFrom(msg.sender, token_amount)
                  self._transfer_out(coins[i], dy, use_eth, receiver)
              
                  packed_price_scale: uint256 = self.tweak_price(A_gamma, xp, D, 0)
                  #        Safe to use D from _calc_withdraw_one_coin here ---^
              
                  log RemoveLiquidityOne(
                      msg.sender, token_amount, i, dy, approx_fee, packed_price_scale
                  )
              
                  return dy
              
              
              @external
              @nonreentrant("lock")
              def claim_admin_fees():
                  """
                  @notice Claim admin fees. Callable by anyone.
                  """
                  self._claim_admin_fees()
              
              
              # -------------------------- Packing functions -------------------------------
              
              
              @internal
              @view
              def _pack(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
              @view
              def _unpack(_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,
                  ]
              
              
              @internal
              @view
              def _pack_prices(prices_to_pack: uint256[N_COINS-1]) -> uint256:
                  """
                  @notice Packs N_COINS-1 prices into a uint256.
                  @param prices_to_pack The prices to pack
                  @return uint256 An integer that packs prices
                  """
                  packed_prices: uint256 = 0
                  p: uint256 = 0
                  for k in range(N_COINS - 1):
                      packed_prices = packed_prices << PRICE_SIZE
                      p = prices_to_pack[N_COINS - 2 - k]
                      assert p < PRICE_MASK
                      packed_prices = p | packed_prices
                  return packed_prices
              
              
              @internal
              @view
              def _unpack_prices(_packed_prices: uint256) -> uint256[2]:
                  """
                  @notice Unpacks N_COINS-1 prices from a uint256.
                  @param _packed_prices The packed prices
                  @return uint256[2] Unpacked prices
                  """
                  unpacked_prices: uint256[N_COINS-1] = empty(uint256[N_COINS-1])
                  packed_prices: uint256 = _packed_prices
                  for k in range(N_COINS - 1):
                      unpacked_prices[k] = packed_prices & PRICE_MASK
                      packed_prices = packed_prices >> PRICE_SIZE
              
                  return unpacked_prices
              
              
              # ---------------------- AMM Internal Functions -------------------------------
              
              
              @internal
              def _exchange(
                  sender: address,
                  mvalue: uint256,
                  i: uint256,
                  j: uint256,
                  dx: uint256,
                  min_dy: uint256,
                  use_eth: bool,
                  receiver: address,
                  callbacker: address,
                  callback_sig: bytes32
              ) -> uint256:
              
                  assert i != j  # dev: coin index out of range
                  assert dx > 0  # dev: do not exchange 0 coins
              
                  A_gamma: uint256[2] = self._A_gamma()
                  xp: uint256[N_COINS] = self.balances
                  precisions: uint256[N_COINS] = self._unpack(self.packed_precisions)
                  dy: uint256 = 0
              
                  y: uint256 = xp[j]  # <----------------- if j > N_COINS, this will revert.
                  x0: uint256 = xp[i]  # <--------------- if i > N_COINS, this will  revert.
                  xp[i] = x0 + dx
                  self.balances[i] = xp[i]
              
                  packed_price_scale: uint256 = self.price_scale_packed
                  price_scale: uint256[N_COINS - 1] = self._unpack_prices(
                      packed_price_scale
                  )
              
                  xp[0] *= precisions[0]
                  for k in range(1, N_COINS):
                      xp[k] = unsafe_div(
                          xp[k] * price_scale[k - 1] * precisions[k],
                          PRECISION
                      )  # <-------- Safu to do unsafe_div here since PRECISION is not zero.
              
                  prec_i: uint256 = precisions[i]
              
                  # ----------- Update invariant if A, gamma are undergoing ramps ---------
              
                  t: uint256 = self.future_A_gamma_time
                  if t > block.timestamp:
              
                      x0 *= prec_i
              
                      if i > 0:
                          x0 = unsafe_div(x0 * price_scale[i - 1], 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
                  prec_j: uint256 = precisions[j]
                  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[j - 1]
                  dy /= prec_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
                  self.balances[j] = y  # <----------- Update pool balance of outgoing coin.
              
                  y *= prec_j
                  if j > 0:
                      y = unsafe_div(y * price_scale[j - 1], PRECISION)
                  xp[j] = y  # <------------------------------------------------- Update xp.
              
                  # ---------------------- Do Transfers in and out -------------------------
              
                  ########################## TRANSFER IN <-------
                  self._transfer_in(
                      coins[i], dx, dy, mvalue,
                      callbacker, callback_sig,  # <-------- Callback method is called here.
                      sender, receiver, use_eth,
                  )
              
                  ########################## -------> TRANSFER OUT
                  self._transfer_out(coins[j], dy, use_eth, receiver)
              
                  # ------ Tweak price_scale with good initial guess for newton_D ----------
              
                  packed_price_scale = self.tweak_price(A_gamma, xp, 0, y_out[1])
              
                  log TokenExchange(sender, i, dx, j, dy, fee, packed_price_scale)
              
                  return dy
              
              
              @internal
              def tweak_price(
                  A_gamma: uint256[2],
                  _xp: uint256[N_COINS],
                  new_D: uint256,
                  K0_prev: uint256 = 0,
              ) -> uint256:
                  """
                  @notice Tweaks 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 ------------------------------
              
                  rebalancing_params: uint256[3] = self._unpack(
                      self.packed_rebalancing_params
                  )  # <---------- Contains: allowed_extra_profit, adjustment_step, ma_time.
                  price_oracle: uint256[N_COINS - 1] = self._unpack_prices(
                      self.price_oracle_packed
                  )
                  last_prices: uint256[N_COINS - 1] = self._unpack_prices(
                      self.last_prices_packed
                  )
                  packed_price_scale: uint256 = self.price_scale_packed
                  price_scale: uint256[N_COINS - 1] = self._unpack_prices(
                      packed_price_scale
                  )
              
                  total_supply: uint256 = self.totalSupply
                  old_xcp_profit: uint256 = self.xcp_profit
                  old_virtual_price: uint256 = self.virtual_price
                  last_prices_timestamp: uint256 = self.last_prices_timestamp
              
                  # ----------------------- Update MA if needed ----------------------------
              
                  if last_prices_timestamp < block.timestamp:
              
                      #   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: uint256 = MATH.wad_exp(
                          -convert(
                              unsafe_div(
                                  (block.timestamp - last_prices_timestamp) * 10**18,
                                  rebalancing_params[2]  # <----------------------- ma_time.
                              ),
                              int256,
                          )
                      )
              
                      for k in range(N_COINS - 1):
              
                          # ----------------- We cap state price that goes into the EMA with
                          #                                                 2 x price_scale.
                          price_oracle[k] = unsafe_div(
                              min(last_prices[k], 2 * price_scale[k]) * (10**18 - alpha) +
                              price_oracle[k] * alpha,  # ^-------- Cap spot price into EMA.
                              10**18
                          )
              
                      self.price_oracle_packed = self._pack_prices(price_oracle)
                      self.last_prices_timestamp = block.timestamp  # <---- Store 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 --------------------------
              
                  last_prices = MATH.get_p(_xp, D_unadjusted, A_gamma)
                  for k in range(N_COINS - 1):
                      last_prices[k] = unsafe_div(last_prices[k] * price_scale[k], 10**18)
                  self.last_prices_packed = self._pack_prices(last_prices)
              
                  # ---------- Update profit numbers without price adjustment first --------
              
                  xp: uint256[N_COINS] = empty(uint256[N_COINS])
                  xp[0] = unsafe_div(D_unadjusted, N_COINS)
                  for k in range(N_COINS - 1):
                      xp[k + 1] = D_unadjusted * 10**18 / (N_COINS * price_scale[k])
              
                  # ------------------------- Update xcp_profit ----------------------------
              
                  xcp_profit: uint256 = 10**18
                  virtual_price: uint256 = 10**18
              
                  if old_virtual_price > 0:
              
                      xcp: uint256 = MATH.geometric_mean(xp)
                      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:
                          assert virtual_price > old_virtual_price, "Loss"
              
                  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 = 0
                      ratio: uint256 = 0
                      for k in range(N_COINS - 1):
              
                          ratio = unsafe_div(price_oracle[k] * 10**18, price_scale[k])
                          # unsafe_div because we did safediv before ----^
              
                          if ratio > 10**18:
                              ratio = unsafe_sub(ratio, 10**18)
                          else:
                              ratio = unsafe_sub(10**18, ratio)
                          norm = unsafe_add(norm, ratio**2)
              
                      norm = isqrt(norm)  # <-------------------- isqrt is not in base 1e18.
                      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[N_COINS - 1] = empty(uint256[N_COINS - 1])
                          for k in range(N_COINS - 1):
                              p_new[k] = unsafe_div(
                                  price_scale[k] * unsafe_sub(norm, adjustment_step)
                                  + adjustment_step * price_oracle[k],
                                  norm
                              )  # <- norm is non-zero and gt adjustment_step; unsafe = safe
              
                          # ---------------- Update stale xp (using price_scale) with p_new.
                          xp = _xp
                          for k in range(N_COINS - 1):
                              xp[k + 1] = unsafe_div(_xp[k + 1] * p_new[k], price_scale[k])
                              # unsafe_div because we did safediv before ----^
              
                          # ------------------------------------------ Update D with new xp.
                          D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], xp, 0)
              
                          for k in range(N_COINS):
                              frac: uint256 = xp[k] * 10**18 / D  # <----- Check validity of
                              assert (frac > 10**16 - 1) and (frac < 10**20 + 1)  #   p_new.
              
                          xp[0] = D / N_COINS
                          for k in range(N_COINS - 1):
                              xp[k + 1] = D * 10**18 / (N_COINS * p_new[k])  # <---- Convert
                              #                                           xp to real prices.
              
                          # ---------- 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 * MATH.geometric_mean(xp), 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
                          ):
              
                              packed_price_scale = self._pack_prices(p_new)
              
                              self.D = D
                              self.virtual_price = old_virtual_price
                              self.price_scale_packed = packed_price_scale
              
                              return packed_price_scale
              
                  # --------- price_scale was not adjusted. Update the profit counter and D.
                  self.D = D_unadjusted
                  self.virtual_price = virtual_price
              
                  return packed_price_scale
              
              
              @internal
              def _claim_admin_fees():
                  """
                  @notice Claims admin fees and sends it to fee_receiver set in the factory.
                  """
                  A_gamma: uint256[2] = self._A_gamma()
              
                  xcp_profit: uint256 = self.xcp_profit  # <---------- Current pool profits.
                  xcp_profit_a: uint256 = self.xcp_profit_a  # <- Profits at previous claim.
                  total_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 total_supply < 10**18:
                      return
              
                  #      Claim tokens belonging to the admin here. This is done by 'gulping'
                  #       pool tokens that have accrued as fees, but not accounted in pool's
                  #         `self.balances` yet: pool balances only account for incoming and
                  #                  outgoing tokens excluding fees. Following 'gulps' fees:
              
                  for i in range(N_COINS):
                      if coins[i] == WETH20:
                          self.balances[i] = self.balance
                      else:
                          self.balances[i] = ERC20(coins[i]).balanceOf(self)
              
                  #            If the pool has made no profits, `xcp_profit == xcp_profit_a`
                  #                         and the pool gulps nothing in the previous step.
              
                  vprice: uint256 = self.virtual_price
              
                  #  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.
                  receiver: address = Factory(self.factory).fee_receiver()
                  if receiver != empty(address) and fees > 0:
              
                      frac: uint256 = vprice * 10**18 / (vprice - fees) - 10**18
                      claimed: uint256 = self.mint_relative(receiver, frac)
              
                      xcp_profit -= fees * 2
              
                      self.xcp_profit = xcp_profit
              
                      log ClaimAdminFee(receiver, claimed)
              
                  # ------------------------------------------- Recalculate D b/c we gulped.
                  D: uint256 = MATH.newton_D(A_gamma[0], A_gamma[1], self.xp(), 0)
                  self.D = D
              
                  # ------------------- Recalculate virtual_price following admin fee claim.
                  #     In this instance we do not check if current virtual price is greater
                  #               than old virtual price, since the claim process can result
                  #                                     in a small decrease in pool's value.
              
                  self.virtual_price = 10**18 * self.get_xcp(D) / self.totalSupply
                  self.xcp_profit_a = xcp_profit  # <------------ Cache last claimed profit.
              
              
              @internal
              @view
              def xp() -> uint256[N_COINS]:
              
                  result: uint256[N_COINS] = self.balances
                  packed_prices: uint256 = self.price_scale_packed
                  precisions: uint256[N_COINS] = self._unpack(self.packed_precisions)
              
                  result[0] *= precisions[0]
                  for i in range(1, N_COINS):
                      p: uint256 = (packed_prices & PRICE_MASK) * precisions[i]
                      result[i] = result[i] * p / PRECISION
                      packed_prices = packed_prices >> PRICE_SIZE
              
                  return result
              
              
              @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(self.packed_fee_params)
                  f: uint256 = MATH.reduction_coefficient(xp, fee_params[2])
                  return unsafe_div(
                      fee_params[0] * f + fee_params[1] * (10**18 - f),
                      10**18
                  )
              
              
              @internal
              @view
              def get_xcp(D: uint256) -> uint256:
              
                  x: uint256[N_COINS] = empty(uint256[N_COINS])
                  x[0] = D / N_COINS
                  packed_prices: uint256 = self.price_scale_packed  # <-- No precisions here
                  #                                 because we don't switch to "real" units.
              
                  for i in range(1, N_COINS):
                      x[i] = D * 10**18 / (N_COINS * (packed_prices & PRICE_MASK))
                      packed_prices = packed_prices >> PRICE_SIZE
              
                  return MATH.geometric_mean(x)
              
              
              @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
                  precisions: uint256[N_COINS] = self._unpack(self.packed_precisions)
                  xp: uint256[N_COINS] = precisions
                  D0: uint256 = 0
              
                  # -------------------------- Calculate D0 and xp -------------------------
              
                  price_scale_i: uint256 = PRECISION * precisions[0]
                  packed_prices: uint256 = self.price_scale_packed
                  xp[0] *= xx[0]
                  for k in range(1, N_COINS):
                      p: uint256 = (packed_prices & PRICE_MASK)
                      if i == k:
                          price_scale_i = p * xp[i]
                      xp[k] = unsafe_div(xp[k] * xx[k] * p, PRECISION)
                      packed_prices = packed_prices >> PRICE_SIZE
              
                  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(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
              
                  # ------------------------------------------------------------------------
                  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.
                  @dev Non-zero to non-zero approvals are allowed, but should
                       be used cautiously. The methods increaseAllowance + decreaseAllowance
                       are available to prevent any front-running that may occur.
                  @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 increaseAllowance(_spender: address, _add_value: uint256) -> bool:
                  """
                  @notice Increase the allowance granted to `_spender`.
                  @dev This function will never overflow, and instead will bound
                       allowance to max_value(uint256). This has the potential to grant an
                       infinite approval.
                  @param _spender The account to increase the allowance of.
                  @param _add_value The amount to increase the allowance by.
                  @return bool Success
                  """
                  cached_allowance: uint256 = self.allowance[msg.sender][_spender]
                  allowance: uint256 = unsafe_add(cached_allowance, _add_value)
              
                  if allowance < cached_allowance:  # <-------------- Check for an overflow.
                      allowance = max_value(uint256)
              
                  if allowance != cached_allowance:
                      self._approve(msg.sender, _spender, allowance)
              
                  return True
              
              
              @external
              def decreaseAllowance(_spender: address, _sub_value: uint256) -> bool:
                  """
                  @notice Decrease the allowance granted to `_spender`.
                  @dev This function will never underflow, and instead will bound
                      allowance to 0.
                  @param _spender The account to decrease the allowance of.
                  @param _sub_value The amount to decrease the allowance by.
                  @return bool Success.
                  """
                  cached_allowance: uint256 = self.allowance[msg.sender][_spender]
                  allowance: uint256 = unsafe_sub(cached_allowance, _sub_value)
              
                  if cached_allowance < allowance:  # <------------- Check for an underflow.
                      allowance = 0
              
                  if allowance != cached_allowance:
                      self._approve(msg.sender, _spender, allowance)
              
                  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 mint_relative(_to: address, frac: uint256) -> uint256:
                  """
                  @dev Increases supply by factor of (1 + frac/1e18) and mints it for _to
                  @param _to The account that will receive the created tokens.
                  @param frac The fraction of the current supply to mint.
                  @return uint256 Amount of tokens minted.
                  """
                  supply: uint256 = self.totalSupply
                  d_supply: uint256 = supply * frac / 10**18
                  if d_supply > 0:
                      self.totalSupply = supply + d_supply
                      self.balanceOf[_to] += d_supply
                      log Transfer(empty(address), _to, d_supply)
              
                  return d_supply
              
              
              @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 -------------------------------
              
              
              @external
              @view
              def fee_receiver() -> address:
                  """
                  @notice Returns the address of the admin fee receiver.
                  @return address Fee receiver.
                  """
                  return Factory(self.factory).fee_receiver()
              
              
              @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(self.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(self.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(self.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.
                  """
              
                  price_oracle: uint256[N_COINS-1] = self._unpack_prices(
                      self.price_oracle_packed
                  )
                  return (
                      3 * self.virtual_price * MATH.cbrt(price_oracle[0] * price_oracle[1])
                  ) / 10**24
              
              
              @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.totalSupply
              
              
              @external
              @view
              @nonreentrant("lock")
              def price_oracle(k: uint256) -> 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._unpack_prices(self.price_oracle_packed)[k]
                  price_scale: uint256 = self._unpack_prices(self.price_scale_packed)[k]
                  last_prices_timestamp: uint256 = self.last_prices_timestamp
              
                  if last_prices_timestamp < block.timestamp:  # <------------ Update moving
                      #                                                   average if needed.
              
                      last_prices: uint256 = self._unpack_prices(self.last_prices_packed)[k]
                      ma_time: uint256 = self._unpack(self.packed_rebalancing_params)[2]
                      alpha: uint256 = MATH.wad_exp(
                          -convert(
                              (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 last_prices(k: uint256) -> uint256:
                  """
                  @notice Returns last price of the coin at index `k` w.r.t the coin
                          at index 0.
                  @dev last_prices returns the quote by the AMM for an infinitesimally small swap
                       after the last trade. It is not equivalent to the last traded price, and
                       is computed by taking the partial differential of `x` w.r.t `y`. The
                       derivative is calculated in `get_p` and then multiplied with price_scale
                       to give last_prices.
                  @param k The index of the coin.
                  @return uint256 Last logged price of coin.
                  """
                  return self._unpack_prices(self.last_prices_packed)[k]
              
              
              @external
              @view
              def price_scale(k: uint256) -> 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.
                  @param k The index of the coin.
                  @return uint256 Price scale of coin.
                  """
                  return self._unpack_prices(self.price_scale_packed)[k]
              
              
              @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())
              
              
              @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(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(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(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(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(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(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 self._unpack(self.packed_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(self.factory).admin()  # dev: only owner
                  assert block.timestamp > self.initial_A_gamma_time + (MIN_RAMP_TIME - 1)  # 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
                  assert ratio > 10**18 / MAX_A_CHANGE - 1
              
                  ratio = 10**18 * future_gamma / A_gamma[1]
                  assert ratio < 10**18 * MAX_A_CHANGE + 1
                  assert ratio > 10**18 / MAX_A_CHANGE - 1
              
                  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(self.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
              def commit_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(self.factory).admin()  # dev: only owner
                  assert self.admin_actions_deadline == 0  # dev: active action
              
                  _deadline: uint256 = block.timestamp + ADMIN_ACTIONS_DELAY
                  self.admin_actions_deadline = _deadline
              
                  # ----------------------------- 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(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.future_packed_fee_params = self._pack(
                      [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(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.future_packed_rebalancing_params = self._pack(
                      [new_allowed_extra_profit, new_adjustment_step, new_ma_time]
                  )
              
                  # ---------------------------------- LOG ---------------------------------
              
                  log CommitNewParameters(
                      _deadline,
                      new_mid_fee,
                      new_out_fee,
                      new_fee_gamma,
                      new_allowed_extra_profit,
                      new_adjustment_step,
                      new_ma_time,
                  )
              
              
              @external
              @nonreentrant("lock")
              def apply_new_parameters():
                  """
                  @notice Apply committed parameters.
                  @dev Only callable after admin_actions_deadline.
                  """
                  assert block.timestamp >= self.admin_actions_deadline  # dev: insufficient time
                  assert self.admin_actions_deadline != 0  # dev: no active action
              
                  self.admin_actions_deadline = 0
              
                  packed_fee_params: uint256 = self.future_packed_fee_params
                  self.packed_fee_params = packed_fee_params
              
                  packed_rebalancing_params: uint256 = self.future_packed_rebalancing_params
                  self.packed_rebalancing_params = packed_rebalancing_params
              
                  rebalancing_params: uint256[3] = self._unpack(packed_rebalancing_params)
                  fee_params: uint256[3] = self._unpack(packed_fee_params)
              
                  log NewParameters(
                      fee_params[0],
                      fee_params[1],
                      fee_params[2],
                      rebalancing_params[0],
                      rebalancing_params[1],
                      rebalancing_params[2],
                  )
              
              
              @external
              def revert_new_parameters():
                  """
                  @notice Revert committed parameters
                  @dev Only accessible by factory admin. Setting admin_actions_deadline to 0
                       ensures a revert in apply_new_parameters.
                  """
                  assert msg.sender == Factory(self.factory).admin()  # dev: only owner
                  self.admin_actions_deadline = 0

              File 12 of 30: 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 13 of 30: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.20;
              
              // contracts/interfaces/IERC1967.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
              
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               */
              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);
              }
              
              // contracts/proxy/Proxy.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
              
              /**
               * @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 {
                      _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();
                  }
              }
              
              // contracts/proxy/beacon/IBeacon.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
              
              /**
               * @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.
                   *
                   * {UpgradeableBeacon} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              
              // contracts/utils/Address.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
              
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev The ETH balance of the account is not enough to perform the operation.
                   */
                  error AddressInsufficientBalance(address account);
              
                  /**
                   * @dev There's no code at `target` (it is not a contract).
                   */
                  error AddressEmptyCode(address target);
              
                  /**
                   * @dev A call to an address target failed. The target may have reverted.
                   */
                  error FailedInnerCall();
              
                  /**
                   * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      if (address(this).balance < amount) {
                          revert AddressInsufficientBalance(address(this));
                      }
              
                      (bool success, ) = recipient.call{value: amount}("");
                      if (!success) {
                          revert FailedInnerCall();
                      }
                  }
              
                  /**
                   * @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 or custom error, it is bubbled
                   * up by this function (like regular Solidity function calls). However, if
                   * the call reverted with no returned reason, this function reverts with a
                   * {FailedInnerCall} error.
                   *
                   * 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.
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0);
                  }
              
                  /**
                   * @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`.
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      if (address(this).balance < value) {
                          revert AddressInsufficientBalance(address(this));
                      }
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
              
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
              
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                   * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
                   * unsuccessful call.
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata
                  ) internal view returns (bytes memory) {
                      if (!success) {
                          _revert(returndata);
                      } else {
                          // only check if target is a contract if the call was successful and the return data is empty
                          // otherwise we already know that it was a contract
                          if (returndata.length == 0 && target.code.length == 0) {
                              revert AddressEmptyCode(target);
                          }
                          return returndata;
                      }
                  }
              
                  /**
                   * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                   * revert reason or with a default {FailedInnerCall} error.
                   */
                  function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                      if (!success) {
                          _revert(returndata);
                      } else {
                          return returndata;
                      }
                  }
              
                  /**
                   * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
                   */
                  function _revert(bytes memory returndata) 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 FailedInnerCall();
                      }
                  }
              }
              
              // contracts/utils/Context.sol
              
              // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
              
              /**
               * @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;
                  }
              }
              
              // contracts/utils/StorageSlot.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              
              /**
               * @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(newImplementation.code.length > 0);
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               */
              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
                      }
                  }
              }
              
              // contracts/access/Ownable.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.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.
               *
               * The initial owner is set to the address provided by the deployer. 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;
              
                  /**
                   * @dev The caller account is not authorized to perform an operation.
                   */
                  error OwnableUnauthorizedAccount(address account);
              
                  /**
                   * @dev The owner is not a valid owner account. (eg. `address(0)`)
                   */
                  error OwnableInvalidOwner(address owner);
              
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
              
                  /**
                   * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
                   */
                  constructor(address initialOwner) {
                      if (initialOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _transferOwnership(initialOwner);
                  }
              
                  /**
                   * @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 {
                      if (owner() != _msgSender()) {
                          revert OwnableUnauthorizedAccount(_msgSender());
                      }
                  }
              
                  /**
                   * @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 {
                      if (newOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _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);
                  }
              }
              
              // contracts/proxy/ERC1967/ERC1967Utils.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
              
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               */
              library ERC1967Utils {
                  // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
                  // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
                  /**
                   * @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);
              
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
              
                  /**
                   * @dev The `implementation` of the proxy is invalid.
                   */
                  error ERC1967InvalidImplementation(address implementation);
              
                  /**
                   * @dev The `admin` of the proxy is invalid.
                   */
                  error ERC1967InvalidAdmin(address admin);
              
                  /**
                   * @dev The `beacon` of the proxy is invalid.
                   */
                  error ERC1967InvalidBeacon(address beacon);
              
                  /**
                   * @dev An upgrade function sees `msg.value > 0` that may be lost.
                   */
                  error ERC1967NonPayable();
              
                  /**
                   * @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 {
                      if (newImplementation.code.length == 0) {
                          revert ERC1967InvalidImplementation(newImplementation);
                      }
                      StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
                  }
              
                  /**
                   * @dev Performs implementation upgrade with additional setup call if data is nonempty.
                   * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                   * to avoid stuck value in the contract.
                   *
                   * Emits an {IERC1967-Upgraded} event.
                   */
                  function upgradeToAndCall(address newImplementation, bytes memory data) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
              
                      if (data.length > 0) {
                          Address.functionDelegateCall(newImplementation, data);
                      } else {
                          _checkNonPayable();
                      }
                  }
              
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
              
                  /**
                   * @dev Returns the current admin.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
                   * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function 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 {
                      if (newAdmin == address(0)) {
                          revert ERC1967InvalidAdmin(address(0));
                      }
                      StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
                  }
              
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {IERC1967-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 the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  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 {
                      if (newBeacon.code.length == 0) {
                          revert ERC1967InvalidBeacon(newBeacon);
                      }
              
                      StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
              
                      address beaconImplementation = IBeacon(newBeacon).implementation();
                      if (beaconImplementation.code.length == 0) {
                          revert ERC1967InvalidImplementation(beaconImplementation);
                      }
                  }
              
                  /**
                   * @dev Change the beacon and trigger a setup call if data is nonempty.
                   * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                   * to avoid stuck value in the contract.
                   *
                   * Emits an {IERC1967-BeaconUpgraded} event.
                   *
                   * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
                   * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
                   * efficiency.
                   */
                  function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
              
                      if (data.length > 0) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      } else {
                          _checkNonPayable();
                      }
                  }
              
                  /**
                   * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
                   * if an upgrade doesn't perform an initialization call.
                   */
                  function _checkNonPayable() private {
                      if (msg.value > 0) {
                          revert ERC1967NonPayable();
                      }
                  }
              }
              
              // contracts/proxy/ERC1967/ERC1967Proxy.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.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 {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
                   * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
                   *
                   * Requirements:
                   *
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  constructor(address implementation, bytes memory _data) payable {
                      ERC1967Utils.upgradeToAndCall(implementation, _data);
                  }
              
                  /**
                   * @dev Returns the current implementation address.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
                   * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function _implementation() internal view virtual override returns (address) {
                      return ERC1967Utils.getImplementation();
                  }
              }
              
              // contracts/proxy/transparent/ProxyAdmin.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/ProxyAdmin.sol)
              
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
                  /**
                   * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address)`
                   * and `upgradeAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
                   * while `upgradeAndCall` will invoke the `receive` function if the second argument is the empty byte string.
                   * If the getter returns `"5.0.0"`, only `upgradeAndCall(address,bytes)` is present, and the second argument must
                   * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
                   * during an upgrade.
                   */
                  string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
              
                  /**
                   * @dev Sets the initial owner who can perform upgrades.
                   */
                  constructor(address initialOwner) Ownable(initialOwner) {}
              
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
                   * See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  function upgradeAndCall(
                      ITransparentUpgradeableProxy proxy,
                      address implementation,
                      bytes memory data
                  ) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              
              // contracts/proxy/transparent/TransparentUpgradeableProxy.sol
              
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
              
              /**
               * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
               * does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch
               * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
               * include them in the ABI so this interface must be used to interact with it.
               */
              interface ITransparentUpgradeableProxy is IERC1967 {
                  function upgradeToAndCall(address, bytes calldata) external payable;
              }
              
              /**
               * @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to
               * the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating
               * the proxy admin cannot fallback to the target implementation.
               *
               * These properties mean that the admin account can only be used for upgrading the proxy, so it's best if it's a
               * dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to
               * call a function from the proxy implementation. For this reason, the proxy deploys an instance of {ProxyAdmin} and
               * allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative
               * interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership.
               *
               * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
               * inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch
               * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
               * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
               * implementation.
               *
               * NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a
               * meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract.
               *
               * IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an
               * immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be
               * overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an
               * undesirable state where the admin slot is different from the actual admin.
               *
               * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the
               * compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new
               * function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This
               * could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  // An immutable address for the admin to avoid unnecessary SLOADs before each call
                  // at the expense of removing the ability to change the admin once it's set.
                  // This is acceptable if the admin is always a ProxyAdmin instance or similar contract
                  // with its own ability to transfer the permissions to another account.
                  address private immutable _admin;
              
                  /**
                   * @dev The proxy caller is the current admin, and can't fallback to the proxy target.
                   */
                  error ProxyDeniedAdminAccess();
              
                  /**
                   * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
                   * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
                   * {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      _admin = address(new ProxyAdmin(initialOwner));
                      // Set the storage value and emit an event for ERC-1967 compatibility
                      ERC1967Utils.changeAdmin(_proxyAdmin());
                  }
              
                  /**
                   * @dev Returns the admin of this proxy.
                   */
                  function _proxyAdmin() internal virtual returns (address) {
                      return _admin;
                  }
              
                  /**
                   * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior.
                   */
                  function _fallback() internal virtual override {
                      if (msg.sender == _proxyAdmin()) {
                          if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
                              revert ProxyDeniedAdminAccess();
                          } else {
                              _dispatchUpgradeToAndCall();
                          }
                      } else {
                          super._fallback();
                      }
                  }
              
                  /**
                   * @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  function _dispatchUpgradeToAndCall() private {
                      (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
                      ERC1967Utils.upgradeToAndCall(newImplementation, data);
                  }
              }

              File 14 of 30: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
              pragma solidity ^0.8.20;
              import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
              import {ERC1967Proxy} from "../ERC1967/ERC1967Proxy.sol";
              import {IERC1967} from "../../interfaces/IERC1967.sol";
              import {ProxyAdmin} from "./ProxyAdmin.sol";
              /**
               * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
               * does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch
               * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
               * include them in the ABI so this interface must be used to interact with it.
               */
              interface ITransparentUpgradeableProxy is IERC1967 {
                  function upgradeToAndCall(address, bytes calldata) external payable;
              }
              /**
               * @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to
               * the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating
               * the proxy admin cannot fallback to the target implementation.
               *
               * These properties mean that the admin account can only be used for upgrading the proxy, so it's best if it's a
               * dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to
               * call a function from the proxy implementation. For this reason, the proxy deploys an instance of {ProxyAdmin} and
               * allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative
               * interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership.
               *
               * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
               * inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch
               * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
               * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
               * implementation.
               *
               * NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a
               * meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract.
               *
               * IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an
               * immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be
               * overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an
               * undesirable state where the admin slot is different from the actual admin.
               *
               * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the
               * compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new
               * function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This
               * could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  // An immutable address for the admin to avoid unnecessary SLOADs before each call
                  // at the expense of removing the ability to change the admin once it's set.
                  // This is acceptable if the admin is always a ProxyAdmin instance or similar contract
                  // with its own ability to transfer the permissions to another account.
                  address private immutable _admin;
                  /**
                   * @dev The proxy caller is the current admin, and can't fallback to the proxy target.
                   */
                  error ProxyDeniedAdminAccess();
                  /**
                   * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
                   * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
                   * {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      _admin = address(new ProxyAdmin(initialOwner));
                      // Set the storage value and emit an event for ERC-1967 compatibility
                      ERC1967Utils.changeAdmin(_proxyAdmin());
                  }
                  /**
                   * @dev Returns the admin of this proxy.
                   */
                  function _proxyAdmin() internal virtual returns (address) {
                      return _admin;
                  }
                  /**
                   * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior.
                   */
                  function _fallback() internal virtual override {
                      if (msg.sender == _proxyAdmin()) {
                          if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
                              revert ProxyDeniedAdminAccess();
                          } else {
                              _dispatchUpgradeToAndCall();
                          }
                      } else {
                          super._fallback();
                      }
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  function _dispatchUpgradeToAndCall() private {
                      (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
                      ERC1967Utils.upgradeToAndCall(newImplementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
              pragma solidity ^0.8.20;
              import {IBeacon} from "../beacon/IBeacon.sol";
              import {Address} from "../../utils/Address.sol";
              import {StorageSlot} from "../../utils/StorageSlot.sol";
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               */
              library ERC1967Utils {
                  // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
                  // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
                  /**
                   * @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);
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev The `implementation` of the proxy is invalid.
                   */
                  error ERC1967InvalidImplementation(address implementation);
                  /**
                   * @dev The `admin` of the proxy is invalid.
                   */
                  error ERC1967InvalidAdmin(address admin);
                  /**
                   * @dev The `beacon` of the proxy is invalid.
                   */
                  error ERC1967InvalidBeacon(address beacon);
                  /**
                   * @dev An upgrade function sees `msg.value > 0` that may be lost.
                   */
                  error ERC1967NonPayable();
                  /**
                   * @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 {
                      if (newImplementation.code.length == 0) {
                          revert ERC1967InvalidImplementation(newImplementation);
                      }
                      StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Performs implementation upgrade with additional setup call if data is nonempty.
                   * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                   * to avoid stuck value in the contract.
                   *
                   * Emits an {IERC1967-Upgraded} event.
                   */
                  function upgradeToAndCall(address newImplementation, bytes memory data) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                      if (data.length > 0) {
                          Address.functionDelegateCall(newImplementation, data);
                      } else {
                          _checkNonPayable();
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Returns the current admin.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
                   * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function 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 {
                      if (newAdmin == address(0)) {
                          revert ERC1967InvalidAdmin(address(0));
                      }
                      StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {IERC1967-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 the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
                   */
                  // solhint-disable-next-line private-vars-leading-underscore
                  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 {
                      if (newBeacon.code.length == 0) {
                          revert ERC1967InvalidBeacon(newBeacon);
                      }
                      StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
                      address beaconImplementation = IBeacon(newBeacon).implementation();
                      if (beaconImplementation.code.length == 0) {
                          revert ERC1967InvalidImplementation(beaconImplementation);
                      }
                  }
                  /**
                   * @dev Change the beacon and trigger a setup call if data is nonempty.
                   * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
                   * to avoid stuck value in the contract.
                   *
                   * Emits an {IERC1967-BeaconUpgraded} event.
                   *
                   * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
                   * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
                   * efficiency.
                   */
                  function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      } else {
                          _checkNonPayable();
                      }
                  }
                  /**
                   * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
                   * if an upgrade doesn't perform an initialization call.
                   */
                  function _checkNonPayable() private {
                      if (msg.value > 0) {
                          revert ERC1967NonPayable();
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.20;
              import {Proxy} from "../Proxy.sol";
              import {ERC1967Utils} from "./ERC1967Utils.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 {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
                   * encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
                   *
                   * Requirements:
                   *
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  constructor(address implementation, bytes memory _data) payable {
                      ERC1967Utils.upgradeToAndCall(implementation, _data);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
                   * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function _implementation() internal view virtual override returns (address) {
                      return ERC1967Utils.getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
               */
              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 v5.0.0) (proxy/transparent/ProxyAdmin.sol)
              pragma solidity ^0.8.20;
              import {ITransparentUpgradeableProxy} from "./TransparentUpgradeableProxy.sol";
              import {Ownable} from "../../access/Ownable.sol";
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
                  /**
                   * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address)`
                   * and `upgradeAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
                   * while `upgradeAndCall` will invoke the `receive` function if the second argument is the empty byte string.
                   * If the getter returns `"5.0.0"`, only `upgradeAndCall(address,bytes)` is present, and the second argument must
                   * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
                   * during an upgrade.
                   */
                  string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
                  /**
                   * @dev Sets the initial owner who can perform upgrades.
                   */
                  constructor(address initialOwner) Ownable(initialOwner) {}
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
                   * See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   * - If `data` is empty, `msg.value` must be zero.
                   */
                  function upgradeAndCall(
                      ITransparentUpgradeableProxy proxy,
                      address implementation,
                      bytes memory data
                  ) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.20;
              /**
               * @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.
                   *
                   * {UpgradeableBeacon} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
              pragma solidity ^0.8.20;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev The ETH balance of the account is not enough to perform the operation.
                   */
                  error AddressInsufficientBalance(address account);
                  /**
                   * @dev There's no code at `target` (it is not a contract).
                   */
                  error AddressEmptyCode(address target);
                  /**
                   * @dev A call to an address target failed. The target may have reverted.
                   */
                  error FailedInnerCall();
                  /**
                   * @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      if (address(this).balance < amount) {
                          revert AddressInsufficientBalance(address(this));
                      }
                      (bool success, ) = recipient.call{value: amount}("");
                      if (!success) {
                          revert FailedInnerCall();
                      }
                  }
                  /**
                   * @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 or custom error, it is bubbled
                   * up by this function (like regular Solidity function calls). However, if
                   * the call reverted with no returned reason, this function reverts with a
                   * {FailedInnerCall} error.
                   *
                   * 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.
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0);
                  }
                  /**
                   * @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`.
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      if (address(this).balance < value) {
                          revert AddressInsufficientBalance(address(this));
                      }
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResultFromTarget(target, success, returndata);
                  }
                  /**
                   * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
                   * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
                   * unsuccessful call.
                   */
                  function verifyCallResultFromTarget(
                      address target,
                      bool success,
                      bytes memory returndata
                  ) internal view returns (bytes memory) {
                      if (!success) {
                          _revert(returndata);
                      } else {
                          // only check if target is a contract if the call was successful and the return data is empty
                          // otherwise we already know that it was a contract
                          if (returndata.length == 0 && target.code.length == 0) {
                              revert AddressEmptyCode(target);
                          }
                          return returndata;
                      }
                  }
                  /**
                   * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
                   * revert reason or with a default {FailedInnerCall} error.
                   */
                  function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
                      if (!success) {
                          _revert(returndata);
                      } else {
                          return returndata;
                      }
                  }
                  /**
                   * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
                   */
                  function _revert(bytes memory returndata) 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 FailedInnerCall();
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
              // This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
              pragma solidity ^0.8.20;
              /**
               * @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(newImplementation.code.length > 0);
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               */
              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 (last updated v5.0.0) (proxy/Proxy.sol)
              pragma solidity ^0.8.20;
              /**
               * @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 {
                      _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();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
              pragma solidity ^0.8.20;
              import {Context} from "../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.
               *
               * The initial owner is set to the address provided by the deployer. 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;
                  /**
                   * @dev The caller account is not authorized to perform an operation.
                   */
                  error OwnableUnauthorizedAccount(address account);
                  /**
                   * @dev The owner is not a valid owner account. (eg. `address(0)`)
                   */
                  error OwnableInvalidOwner(address owner);
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  /**
                   * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
                   */
                  constructor(address initialOwner) {
                      if (initialOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _transferOwnership(initialOwner);
                  }
                  /**
                   * @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 {
                      if (owner() != _msgSender()) {
                          revert OwnableUnauthorizedAccount(_msgSender());
                      }
                  }
                  /**
                   * @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 {
                      if (newOwner == address(0)) {
                          revert OwnableInvalidOwner(address(0));
                      }
                      _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 v5.0.1) (utils/Context.sol)
              pragma solidity ^0.8.20;
              /**
               * @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;
                  }
              }
              

              File 15 of 30: PendleMarketV3
              // 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.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) (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.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) (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: GPL-3.0-or-later
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "@openzeppelin/contracts/utils/Context.sol";
              /**
               * @dev Pendle's ERC20 implementation, modified from @openzeppelin implementation
               * Changes are:
               * - comes with built-in reentrancy protection, storage-packed with totalSupply variable
               * - delete increaseAllowance / decreaseAllowance
               * - add nonReentrancy protection to transfer / transferFrom functions
               * - allow decimals to be passed in
               * - block self-transfer by default
               */
              // solhint-disable
              contract PendleERC20 is Context, IERC20, IERC20Metadata {
                  uint8 private constant _NOT_ENTERED = 1;
                  uint8 private constant _ENTERED = 2;
                  mapping(address => uint256) private _balances;
                  mapping(address => mapping(address => uint256)) private _allowances;
                  uint248 private _totalSupply;
                  uint8 private _status;
                  string private _name;
                  string private _symbol;
                  uint8 public immutable decimals;
                  /**
                   * @dev Prevents a contract from calling itself, directly or indirectly.
                   * Calling a `nonReentrant` function from another `nonReentrant`
                   * function is not supported. It is possible to prevent this from happening
                   * by making the `nonReentrant` function external, and making it call a
                   * `private` function that does the actual work.
                   */
                  modifier nonReentrant() {
                      // On the first call to nonReentrant, _notEntered will be true
                      require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                      // Any calls to nonReentrant after this point will fail
                      _status = _ENTERED;
                      _;
                      // By storing the original value once again, a refund is triggered (see
                      // https://eips.ethereum.org/EIPS/eip-2200)
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Sets the values for {name}, {symbol} and {decimals}.
                   *
                   * All three of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_, uint8 decimals_) {
                      _name = name_;
                      _symbol = symbol_;
                      decimals = decimals_;
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual override returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address to, uint256 amount) external virtual override nonReentrant returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) external virtual override returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Emits an {Approval} event indicating the updated allowance. This is not
                   * required by the EIP. See the note at the beginning of {ERC20}.
                   *
                   * NOTE: Does not update the allowance if the current allowance
                   * is the maximum `uint256`.
                   *
                   * Requirements:
                   *
                   * - `from` and `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``from``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) external virtual override nonReentrant returns (bool) {
                      address spender = _msgSender();
                      _spendAllowance(from, spender, amount);
                      _transfer(from, to, amount);
                      return true;
                  }
                  /**
                   * @dev Moves `amount` of tokens from `sender` to `recipient`.
                   *
                   * This internal function is equivalent to {transfer}, and can be used to
                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                   *
                   * Emits a {Transfer} event.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   */
                  function _transfer(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), "ERC20: transfer from the zero address");
                      require(to != address(0), "ERC20: transfer to the zero address");
                      require(from != to, "ERC20: transfer to self");
                      _beforeTokenTransfer(from, to, amount);
                      uint256 fromBalance = _balances[from];
                      require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                      unchecked {
                          _balances[from] = fromBalance - amount;
                      }
                      _balances[to] += amount;
                      emit Transfer(from, to, amount);
                      _afterTokenTransfer(from, to, amount);
                  }
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
                      _beforeTokenTransfer(address(0), account, amount);
                      _totalSupply += toUint248(amount);
                      _balances[account] += amount;
                      emit Transfer(address(0), account, amount);
                      _afterTokenTransfer(address(0), account, amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
                      _beforeTokenTransfer(account, address(0), amount);
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                      unchecked {
                          _balances[account] = accountBalance - amount;
                      }
                      _totalSupply -= toUint248(amount);
                      emit Transfer(account, address(0), amount);
                      _afterTokenTransfer(account, address(0), amount);
                  }
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                   *
                   * This internal function is equivalent to `approve`, and can be used to
                   * e.g. set automatic allowances for certain subsystems, etc.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `owner` cannot be the zero address.
                   * - `spender` cannot be the zero address.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      require(owner != address(0), "ERC20: approve from the zero address");
                      require(spender != address(0), "ERC20: approve to the zero address");
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                  }
                  /**
                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                   *
                   * Does not update the allowance amount in case of infinite allowance.
                   * Revert if not enough allowance is available.
                   *
                   * Might emit an {Approval} event.
                   */
                  function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                      uint256 currentAllowance = allowance(owner, spender);
                      if (currentAllowance != type(uint256).max) {
                          require(currentAllowance >= amount, "ERC20: insufficient allowance");
                          unchecked {
                              _approve(owner, spender, currentAllowance - amount);
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  /**
                   * @dev Hook that is called after any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * has been transferred to `to`.
                   * - when `from` is zero, `amount` tokens have been minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  function toUint248(uint256 x) internal virtual returns (uint248) {
                      require(x <= type(uint248).max); // signed, lim = bit-1
                      return uint248(x);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library ArrayLib {
                  function sum(uint256[] memory input) internal pure returns (uint256) {
                      uint256 value = 0;
                      for (uint256 i = 0; i < input.length; ) {
                          value += input[i];
                          unchecked {
                              i++;
                          }
                      }
                      return value;
                  }
                  /// @notice return index of the element if found, else return uint256.max
                  function find(address[] memory array, address element) internal pure returns (uint256 index) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return i;
                          unchecked {
                              i++;
                          }
                      }
                      return type(uint256).max;
                  }
                  function append(address[] memory inp, address element) internal pure returns (address[] memory out) {
                      uint256 length = inp.length;
                      out = new address[](length + 1);
                      for (uint256 i = 0; i < length; ) {
                          out[i] = inp[i];
                          unchecked {
                              i++;
                          }
                      }
                      out[length] = element;
                  }
                  function appendHead(address[] memory inp, address element) internal pure returns (address[] memory out) {
                      uint256 length = inp.length;
                      out = new address[](length + 1);
                      out[0] = element;
                      for (uint256 i = 1; i <= length; ) {
                          out[i] = inp[i - 1];
                          unchecked {
                              i++;
                          }
                      }
                  }
                  /**
                   * @dev This function assumes a and b each contains unidentical elements
                   * @param a array of addresses a
                   * @param b array of addresses b
                   * @return out Concatenation of a and b containing unidentical elements
                   */
                  function merge(address[] memory a, address[] memory b) internal pure returns (address[] memory out) {
                      unchecked {
                          uint256 countUnidenticalB = 0;
                          bool[] memory isUnidentical = new bool[](b.length);
                          for (uint256 i = 0; i < b.length; ++i) {
                              if (!contains(a, b[i])) {
                                  countUnidenticalB++;
                                  isUnidentical[i] = true;
                              }
                          }
                          out = new address[](a.length + countUnidenticalB);
                          for (uint256 i = 0; i < a.length; ++i) {
                              out[i] = a[i];
                          }
                          uint256 id = a.length;
                          for (uint256 i = 0; i < b.length; ++i) {
                              if (isUnidentical[i]) {
                                  out[id++] = b[i];
                              }
                          }
                      }
                  }
                  // various version of contains
                  function contains(address[] memory array, address element) internal pure returns (bool) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return true;
                          unchecked {
                              i++;
                          }
                      }
                      return false;
                  }
                  function contains(bytes4[] memory array, bytes4 element) internal pure returns (bool) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return true;
                          unchecked {
                              i++;
                          }
                      }
                      return false;
                  }
                  function create(address a) internal pure returns (address[] memory res) {
                      res = new address[](1);
                      res[0] = a;
                  }
                  function create(address a, address b) internal pure returns (address[] memory res) {
                      res = new address[](2);
                      res[0] = a;
                      res[1] = b;
                  }
                  function create(address a, address b, address c) internal pure returns (address[] memory res) {
                      res = new address[](3);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                  }
                  function create(address a, address b, address c, address d) internal pure returns (address[] memory res) {
                      res = new address[](4);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                      res[3] = d;
                  }
                  function create(
                      address a,
                      address b,
                      address c,
                      address d,
                      address e
                  ) internal pure returns (address[] memory res) {
                      res = new address[](5);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                      res[3] = d;
                      res[4] = e;
                  }
                  function create(uint256 a) internal pure returns (uint256[] memory res) {
                      res = new uint256[](1);
                      res[0] = a;
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library Errors {
                  // BulkSeller
                  error BulkInsufficientSyForTrade(uint256 currentAmount, uint256 requiredAmount);
                  error BulkInsufficientTokenForTrade(uint256 currentAmount, uint256 requiredAmount);
                  error BulkInSufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
                  error BulkInSufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  error BulkInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
                  error BulkNotMaintainer();
                  error BulkNotAdmin();
                  error BulkSellerAlreadyExisted(address token, address SY, address bulk);
                  error BulkSellerInvalidToken(address token, address SY);
                  error BulkBadRateTokenToSy(uint256 actualRate, uint256 currentRate, uint256 eps);
                  error BulkBadRateSyToToken(uint256 actualRate, uint256 currentRate, uint256 eps);
                  // APPROX
                  error ApproxFail();
                  error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps);
                  error ApproxBinarySearchInputInvalid(
                      uint256 approxGuessMin,
                      uint256 approxGuessMax,
                      uint256 minGuessMin,
                      uint256 maxGuessMax
                  );
                  // MARKET + MARKET MATH CORE
                  error MarketExpired();
                  error MarketZeroAmountsInput();
                  error MarketZeroAmountsOutput();
                  error MarketZeroLnImpliedRate();
                  error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount);
                  error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance);
                  error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
                  error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset);
                  error MarketExchangeRateBelowOne(int256 exchangeRate);
                  error MarketProportionMustNotEqualOne();
                  error MarketRateScalarBelowZero(int256 rateScalar);
                  error MarketScalarRootBelowZero(int256 scalarRoot);
                  error MarketProportionTooHigh(int256 proportion, int256 maxProportion);
                  error OracleUninitialized();
                  error OracleTargetTooOld(uint32 target, uint32 oldest);
                  error OracleZeroCardinality();
                  error MarketFactoryExpiredPt();
                  error MarketFactoryInvalidPt();
                  error MarketFactoryMarketExists();
                  error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot);
                  error MarketFactoryOverriddenFeeTooHigh(uint80 overriddenFee, uint256 marketLnFeeRateRoot);
                  error MarketFactoryReserveFeePercentTooHigh(uint8 reserveFeePercent, uint8 maxReserveFeePercent);
                  error MarketFactoryZeroTreasury();
                  error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor);
                  error MFNotPendleMarket(address addr);
                  // ROUTER
                  error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut);
                  error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
                  error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut);
                  error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut);
                  error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut);
                  error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay);
                  error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay);
                  error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed);
                  error RouterTimeRangeZero();
                  error RouterCallbackNotPendleMarket(address caller);
                  error RouterInvalidAction(bytes4 selector);
                  error RouterInvalidFacet(address facet);
                  error RouterKyberSwapDataZero();
                  error SimulationResults(bool success, bytes res);
                  // YIELD CONTRACT
                  error YCExpired();
                  error YCNotExpired();
                  error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy);
                  error YCNothingToRedeem();
                  error YCPostExpiryDataNotSet();
                  error YCNoFloatingSy();
                  // YieldFactory
                  error YCFactoryInvalidExpiry();
                  error YCFactoryYieldContractExisted();
                  error YCFactoryZeroExpiryDivisor();
                  error YCFactoryZeroTreasury();
                  error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate);
                  error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate);
                  // SY
                  error SYInvalidTokenIn(address token);
                  error SYInvalidTokenOut(address token);
                  error SYZeroDeposit();
                  error SYZeroRedeem();
                  error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut);
                  error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  // SY-specific
                  error SYQiTokenMintFailed(uint256 errCode);
                  error SYQiTokenRedeemFailed(uint256 errCode);
                  error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1);
                  error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax);
                  error SYCurveInvalidPid();
                  error SYCurve3crvPoolNotFound();
                  error SYApeDepositAmountTooSmall(uint256 amountDeposited);
                  error SYBalancerInvalidPid();
                  error SYInvalidRewardToken(address token);
                  error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable);
                  error SYBalancerReentrancy();
                  error NotFromTrustedRemote(uint16 srcChainId, bytes path);
                  error ApxETHNotEnoughBuffer();
                  // Liquidity Mining
                  error VCInactivePool(address pool);
                  error VCPoolAlreadyActive(address pool);
                  error VCZeroVePendle(address user);
                  error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight);
                  error VCEpochNotFinalized(uint256 wTime);
                  error VCPoolAlreadyAddAndRemoved(address pool);
                  error VEInvalidNewExpiry(uint256 newExpiry);
                  error VEExceededMaxLockTime();
                  error VEInsufficientLockTime();
                  error VENotAllowedReduceExpiry();
                  error VEZeroAmountLocked();
                  error VEPositionNotExpired();
                  error VEZeroPosition();
                  error VEZeroSlope(uint128 bias, uint128 slope);
                  error VEReceiveOldSupply(uint256 msgTime);
                  error GCNotPendleMarket(address caller);
                  error GCNotVotingController(address caller);
                  error InvalidWTime(uint256 wTime);
                  error ExpiryInThePast(uint256 expiry);
                  error ChainNotSupported(uint256 chainId);
                  error FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount);
                  error FDEpochLengthMismatch();
                  error FDInvalidPool(address pool);
                  error FDPoolAlreadyExists(address pool);
                  error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch);
                  error FDInvalidStartEpoch(uint256 startEpoch);
                  error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime);
                  error FDFutureFunding(uint256 lastFunded, uint256 currentWTime);
                  error BDInvalidEpoch(uint256 epoch, uint256 startTime);
                  // Cross-Chain
                  error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path);
                  error MsgNotFromReceiveEndpoint(address sender);
                  error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee);
                  error ApproxDstExecutionGasNotSet();
                  error InvalidRetryData();
                  // GENERIC MSG
                  error ArrayLengthMismatch();
                  error ArrayEmpty();
                  error ArrayOutOfBounds();
                  error ZeroAddress();
                  error FailedToSendEther();
                  error InvalidMerkleProof();
                  error OnlyLayerZeroEndpoint();
                  error OnlyYT();
                  error OnlyYCFactory();
                  error OnlyWhitelisted();
                  // Swap Aggregator
                  error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual);
                  error UnsupportedSelector(uint256 aggregatorType, bytes4 selector);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
              // documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
              // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
              // permit persons to whom the Software is furnished to do so, subject to the following conditions:
              // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
              // Software.
              // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
              // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
              // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
              // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
              pragma solidity ^0.8.0;
              /* solhint-disable */
              /**
               * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).
               *
               * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural
               * exponentiation and logarithm (where the base is Euler's number).
               *
               * @author Fernando Martinelli - @fernandomartinelli
               * @author Sergio Yuhjtman - @sergioyuhjtman
               * @author Daniel Fernandez - @dmf7z
               */
              library LogExpMath {
                  // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying
                  // two numbers, and multiply by ONE when dividing them.
                  // All arguments and return values are 18 decimal fixed point numbers.
                  int256 constant ONE_18 = 1e18;
                  // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the
                  // case of ln36, 36 decimals.
                  int256 constant ONE_20 = 1e20;
                  int256 constant ONE_36 = 1e36;
                  // The domain of natural exponentiation is bound by the word size and number of decimals used.
                  //
                  // Because internally the result will be stored using 20 decimals, the largest possible result is
                  // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.
                  // The smallest possible result is 10^(-18), which makes largest negative argument
                  // ln(10^(-18)) = -41.446531673892822312.
                  // We use 130.0 and -41.0 to have some safety margin.
                  int256 constant MAX_NATURAL_EXPONENT = 130e18;
                  int256 constant MIN_NATURAL_EXPONENT = -41e18;
                  // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point
                  // 256 bit integer.
                  int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;
                  int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;
                  uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20);
                  // 18 decimal constants
                  int256 constant x0 = 128000000000000000000; // 2ˆ7
                  int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals)
                  int256 constant x1 = 64000000000000000000; // 2ˆ6
                  int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals)
                  // 20 decimal constants
                  int256 constant x2 = 3200000000000000000000; // 2ˆ5
                  int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2)
                  int256 constant x3 = 1600000000000000000000; // 2ˆ4
                  int256 constant a3 = 888611052050787263676000000; // eˆ(x3)
                  int256 constant x4 = 800000000000000000000; // 2ˆ3
                  int256 constant a4 = 298095798704172827474000; // eˆ(x4)
                  int256 constant x5 = 400000000000000000000; // 2ˆ2
                  int256 constant a5 = 5459815003314423907810; // eˆ(x5)
                  int256 constant x6 = 200000000000000000000; // 2ˆ1
                  int256 constant a6 = 738905609893065022723; // eˆ(x6)
                  int256 constant x7 = 100000000000000000000; // 2ˆ0
                  int256 constant a7 = 271828182845904523536; // eˆ(x7)
                  int256 constant x8 = 50000000000000000000; // 2ˆ-1
                  int256 constant a8 = 164872127070012814685; // eˆ(x8)
                  int256 constant x9 = 25000000000000000000; // 2ˆ-2
                  int256 constant a9 = 128402541668774148407; // eˆ(x9)
                  int256 constant x10 = 12500000000000000000; // 2ˆ-3
                  int256 constant a10 = 113314845306682631683; // eˆ(x10)
                  int256 constant x11 = 6250000000000000000; // 2ˆ-4
                  int256 constant a11 = 106449445891785942956; // eˆ(x11)
                  /**
                   * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.
                   *
                   * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.
                   */
                  function exp(int256 x) internal pure returns (int256) {
                      unchecked {
                          require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, "Invalid exponent");
                          if (x < 0) {
                              // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it
                              // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT).
                              // Fixed point division requires multiplying by ONE_18.
                              return ((ONE_18 * ONE_18) / exp(-x));
                          }
                          // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,
                          // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7
                          // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the
                          // decomposition.
                          // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this
                          // decomposition, which will be lower than the smallest x_n.
                          // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.
                          // We mutate x by subtracting x_n, making it the remainder of the decomposition.
                          // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause
                          // intermediate overflows. Instead we store them as plain integers, with 0 decimals.
                          // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the
                          // decomposition.
                          // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct
                          // it and compute the accumulated product.
                          int256 firstAN;
                          if (x >= x0) {
                              x -= x0;
                              firstAN = a0;
                          } else if (x >= x1) {
                              x -= x1;
                              firstAN = a1;
                          } else {
                              firstAN = 1; // One with no decimal places
                          }
                          // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the
                          // smaller terms.
                          x *= 100;
                          // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point
                          // one. Recall that fixed point multiplication requires dividing by ONE_20.
                          int256 product = ONE_20;
                          if (x >= x2) {
                              x -= x2;
                              product = (product * a2) / ONE_20;
                          }
                          if (x >= x3) {
                              x -= x3;
                              product = (product * a3) / ONE_20;
                          }
                          if (x >= x4) {
                              x -= x4;
                              product = (product * a4) / ONE_20;
                          }
                          if (x >= x5) {
                              x -= x5;
                              product = (product * a5) / ONE_20;
                          }
                          if (x >= x6) {
                              x -= x6;
                              product = (product * a6) / ONE_20;
                          }
                          if (x >= x7) {
                              x -= x7;
                              product = (product * a7) / ONE_20;
                          }
                          if (x >= x8) {
                              x -= x8;
                              product = (product * a8) / ONE_20;
                          }
                          if (x >= x9) {
                              x -= x9;
                              product = (product * a9) / ONE_20;
                          }
                          // x10 and x11 are unnecessary here since we have high enough precision already.
                          // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series
                          // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).
                          int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.
                          int256 term; // Each term in the sum, where the nth term is (x^n / n!).
                          // The first term is simply x.
                          term = x;
                          seriesSum += term;
                          // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,
                          // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.
                          term = ((term * x) / ONE_20) / 2;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 3;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 4;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 5;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 6;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 7;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 8;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 9;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 10;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 11;
                          seriesSum += term;
                          term = ((term * x) / ONE_20) / 12;
                          seriesSum += term;
                          // 12 Taylor terms are sufficient for 18 decimal precision.
                          // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor
                          // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply
                          // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),
                          // and then drop two digits to return an 18 decimal value.
                          return (((product * seriesSum) / ONE_20) * firstAN) / 100;
                      }
                  }
                  /**
                   * @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
                   */
                  function ln(int256 a) internal pure returns (int256) {
                      unchecked {
                          // The real natural logarithm is not defined for negative numbers or zero.
                          require(a > 0, "out of bounds");
                          if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) {
                              return _ln_36(a) / ONE_18;
                          } else {
                              return _ln(a);
                          }
                      }
                  }
                  /**
                   * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.
                   *
                   * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.
                   */
                  function pow(uint256 x, uint256 y) internal pure returns (uint256) {
                      unchecked {
                          if (y == 0) {
                              // We solve the 0^0 indetermination by making it equal one.
                              return uint256(ONE_18);
                          }
                          if (x == 0) {
                              return 0;
                          }
                          // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to
                          // arrive at that r`esult. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means
                          // x^y = exp(y * ln(x)).
                          // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.
                          require(x < 2 ** 255, "x out of bounds");
                          int256 x_int256 = int256(x);
                          // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In
                          // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.
                          // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.
                          require(y < MILD_EXPONENT_BOUND, "y out of bounds");
                          int256 y_int256 = int256(y);
                          int256 logx_times_y;
                          if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {
                              int256 ln_36_x = _ln_36(x_int256);
                              // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just
                              // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal
                              // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the
                              // (downscaled) last 18 decimals.
                              logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18);
                          } else {
                              logx_times_y = _ln(x_int256) * y_int256;
                          }
                          logx_times_y /= ONE_18;
                          // Finally, we compute exp(y * ln(x)) to arrive at x^y
                          require(
                              MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT,
                              "product out of bounds"
                          );
                          return uint256(exp(logx_times_y));
                      }
                  }
                  /**
                   * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
                   */
                  function _ln(int256 a) private pure returns (int256) {
                      unchecked {
                          if (a < ONE_18) {
                              // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less
                              // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call.
                              // Fixed point division requires multiplying by ONE_18.
                              return (-_ln((ONE_18 * ONE_18) / a));
                          }
                          // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which
                          // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,
                          // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot
                          // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.
                          // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this
                          // decomposition, which will be lower than the smallest a_n.
                          // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.
                          // We mutate a by subtracting a_n, making it the remainder of the decomposition.
                          // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point
                          // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by
                          // ONE_18 to convert them to fixed point.
                          // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide
                          // by it and compute the accumulated sum.
                          int256 sum = 0;
                          if (a >= a0 * ONE_18) {
                              a /= a0; // Integer, not fixed point division
                              sum += x0;
                          }
                          if (a >= a1 * ONE_18) {
                              a /= a1; // Integer, not fixed point division
                              sum += x1;
                          }
                          // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.
                          sum *= 100;
                          a *= 100;
                          // Because further a_n are  20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.
                          if (a >= a2) {
                              a = (a * ONE_20) / a2;
                              sum += x2;
                          }
                          if (a >= a3) {
                              a = (a * ONE_20) / a3;
                              sum += x3;
                          }
                          if (a >= a4) {
                              a = (a * ONE_20) / a4;
                              sum += x4;
                          }
                          if (a >= a5) {
                              a = (a * ONE_20) / a5;
                              sum += x5;
                          }
                          if (a >= a6) {
                              a = (a * ONE_20) / a6;
                              sum += x6;
                          }
                          if (a >= a7) {
                              a = (a * ONE_20) / a7;
                              sum += x7;
                          }
                          if (a >= a8) {
                              a = (a * ONE_20) / a8;
                              sum += x8;
                          }
                          if (a >= a9) {
                              a = (a * ONE_20) / a9;
                              sum += x9;
                          }
                          if (a >= a10) {
                              a = (a * ONE_20) / a10;
                              sum += x10;
                          }
                          if (a >= a11) {
                              a = (a * ONE_20) / a11;
                              sum += x11;
                          }
                          // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series
                          // that converges rapidly for values of `a` close to one - the same one used in ln_36.
                          // Let z = (a - 1) / (a + 1).
                          // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))
                          // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires
                          // division by ONE_20.
                          int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);
                          int256 z_squared = (z * z) / ONE_20;
                          // num is the numerator of the series: the z^(2 * n + 1) term
                          int256 num = z;
                          // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
                          int256 seriesSum = num;
                          // In each step, the numerator is multiplied by z^2
                          num = (num * z_squared) / ONE_20;
                          seriesSum += num / 3;
                          num = (num * z_squared) / ONE_20;
                          seriesSum += num / 5;
                          num = (num * z_squared) / ONE_20;
                          seriesSum += num / 7;
                          num = (num * z_squared) / ONE_20;
                          seriesSum += num / 9;
                          num = (num * z_squared) / ONE_20;
                          seriesSum += num / 11;
                          // 6 Taylor terms are sufficient for 36 decimal precision.
                          // Finally, we multiply by 2 (non fixed point) to compute ln(remainder)
                          seriesSum *= 2;
                          // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both
                          // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal
                          // value.
                          return (sum + seriesSum) / 100;
                      }
                  }
                  /**
                   * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,
                   * for x close to one.
                   *
                   * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.
                   */
                  function _ln_36(int256 x) private pure returns (int256) {
                      unchecked {
                          // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits
                          // worthwhile.
                          // First, we transform x to a 36 digit fixed point value.
                          x *= ONE_18;
                          // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).
                          // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))
                          // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires
                          // division by ONE_36.
                          int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);
                          int256 z_squared = (z * z) / ONE_36;
                          // num is the numerator of the series: the z^(2 * n + 1) term
                          int256 num = z;
                          // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
                          int256 seriesSum = num;
                          // In each step, the numerator is multiplied by z^2
                          num = (num * z_squared) / ONE_36;
                          seriesSum += num / 3;
                          num = (num * z_squared) / ONE_36;
                          seriesSum += num / 5;
                          num = (num * z_squared) / ONE_36;
                          seriesSum += num / 7;
                          num = (num * z_squared) / ONE_36;
                          seriesSum += num / 9;
                          num = (num * z_squared) / ONE_36;
                          seriesSum += num / 11;
                          num = (num * z_squared) / ONE_36;
                          seriesSum += num / 13;
                          num = (num * z_squared) / ONE_36;
                          seriesSum += num / 15;
                          // 8 Taylor terms are sufficient for 36 decimal precision.
                          // All that remains is multiplying by 2 (non fixed point).
                          return seriesSum * 2;
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              // This program is free software: you can redistribute it and/or modify
              // it under the terms of the GNU General Public License as published by
              // the Free Software Foundation, either version 3 of the License, or
              // (at your option) any later version.
              // This program is distributed in the hope that it will be useful,
              // but WITHOUT ANY WARRANTY; without even the implied warranty of
              // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
              // GNU General Public License for more details.
              // You should have received a copy of the GNU General Public License
              // along with this program.  If not, see <http://www.gnu.org/licenses/>.
              pragma solidity ^0.8.0;
              /* solhint-disable private-vars-leading-underscore, reason-string */
              library PMath {
                  uint256 internal constant ONE = 1e18; // 18 decimal places
                  int256 internal constant IONE = 1e18; // 18 decimal places
                  function subMax0(uint256 a, uint256 b) internal pure returns (uint256) {
                      unchecked {
                          return (a >= b ? a - b : 0);
                      }
                  }
                  function subNoNeg(int256 a, int256 b) internal pure returns (int256) {
                      require(a >= b, "negative");
                      return a - b; // no unchecked since if b is very negative, a - b might overflow
                  }
                  function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 product = a * b;
                      unchecked {
                          return product / ONE;
                      }
                  }
                  function mulDown(int256 a, int256 b) internal pure returns (int256) {
                      int256 product = a * b;
                      unchecked {
                          return product / IONE;
                      }
                  }
                  function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 aInflated = a * ONE;
                      unchecked {
                          return aInflated / b;
                      }
                  }
                  function divDown(int256 a, int256 b) internal pure returns (int256) {
                      int256 aInflated = a * IONE;
                      unchecked {
                          return aInflated / b;
                      }
                  }
                  function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
                      return (a + b - 1) / b;
                  }
                  function rawDivUp(int256 a, int256 b) internal pure returns (int256) {
                      return (a + b - 1) / b;
                  }
                  function slipUp(uint256 a, uint256 factor) internal pure returns (uint256) {
                      return mulDown(a, ONE + factor);
                  }
                  function slipDown(uint256 a, uint256 factor) internal pure returns (uint256) {
                      return mulDown(a, ONE - factor);
                  }
                  // @author Uniswap
                  function sqrt(uint256 y) internal pure returns (uint256 z) {
                      if (y > 3) {
                          z = y;
                          uint256 x = y / 2 + 1;
                          while (x < z) {
                              z = x;
                              x = (y / x + x) / 2;
                          }
                      } else if (y != 0) {
                          z = 1;
                      }
                  }
                  function square(uint256 x) internal pure returns (uint256) {
                      return x * x;
                  }
                  function squareDown(uint256 x) internal pure returns (uint256) {
                      return mulDown(x, x);
                  }
                  function abs(int256 x) internal pure returns (uint256) {
                      return uint256(x > 0 ? x : -x);
                  }
                  function neg(int256 x) internal pure returns (int256) {
                      return x * (-1);
                  }
                  function neg(uint256 x) internal pure returns (int256) {
                      return Int(x) * (-1);
                  }
                  function max(uint256 x, uint256 y) internal pure returns (uint256) {
                      return (x > y ? x : y);
                  }
                  function max(int256 x, int256 y) internal pure returns (int256) {
                      return (x > y ? x : y);
                  }
                  function min(uint256 x, uint256 y) internal pure returns (uint256) {
                      return (x < y ? x : y);
                  }
                  function min(int256 x, int256 y) internal pure returns (int256) {
                      return (x < y ? x : y);
                  }
                  /*///////////////////////////////////////////////////////////////
                                             SIGNED CASTS
                  //////////////////////////////////////////////////////////////*/
                  function Int(uint256 x) internal pure returns (int256) {
                      require(x <= uint256(type(int256).max));
                      return int256(x);
                  }
                  function Int128(int256 x) internal pure returns (int128) {
                      require(type(int128).min <= x && x <= type(int128).max);
                      return int128(x);
                  }
                  function Int128(uint256 x) internal pure returns (int128) {
                      return Int128(Int(x));
                  }
                  /*///////////////////////////////////////////////////////////////
                                             UNSIGNED CASTS
                  //////////////////////////////////////////////////////////////*/
                  function Uint(int256 x) internal pure returns (uint256) {
                      require(x >= 0);
                      return uint256(x);
                  }
                  function Uint32(uint256 x) internal pure returns (uint32) {
                      require(x <= type(uint32).max);
                      return uint32(x);
                  }
                  function Uint64(uint256 x) internal pure returns (uint64) {
                      require(x <= type(uint64).max);
                      return uint64(x);
                  }
                  function Uint112(uint256 x) internal pure returns (uint112) {
                      require(x <= type(uint112).max);
                      return uint112(x);
                  }
                  function Uint96(uint256 x) internal pure returns (uint96) {
                      require(x <= type(uint96).max);
                      return uint96(x);
                  }
                  function Uint128(uint256 x) internal pure returns (uint128) {
                      require(x <= type(uint128).max);
                      return uint128(x);
                  }
                  function Uint192(uint256 x) internal pure returns (uint192) {
                      require(x <= type(uint192).max);
                      return uint192(x);
                  }
                  function isAApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps);
                  }
                  function isAGreaterApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return a >= b && a <= mulDown(b, ONE + eps);
                  }
                  function isASmallerApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return a <= b && a >= mulDown(b, ONE - eps);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library MiniHelpers {
                  function isCurrentlyExpired(uint256 expiry) internal view returns (bool) {
                      return (expiry <= block.timestamp);
                  }
                  function isExpired(uint256 expiry, uint256 blockTime) internal pure returns (bool) {
                      return (expiry <= blockTime);
                  }
                  function isTimeInThePast(uint256 timestamp) internal view returns (bool) {
                      return (timestamp <= block.timestamp); // same definition as isCurrentlyExpired
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
              import "../../interfaces/IWETH.sol";
              abstract contract TokenHelper {
                  using SafeERC20 for IERC20;
                  address internal constant NATIVE = address(0);
                  uint256 internal constant LOWER_BOUND_APPROVAL = type(uint96).max / 2; // some tokens use 96 bits for approval
                  function _transferIn(address token, address from, uint256 amount) internal {
                      if (token == NATIVE) require(msg.value == amount, "eth mismatch");
                      else if (amount != 0) IERC20(token).safeTransferFrom(from, address(this), amount);
                  }
                  function _transferFrom(IERC20 token, address from, address to, uint256 amount) internal {
                      if (amount != 0) token.safeTransferFrom(from, to, amount);
                  }
                  function _transferOut(address token, address to, uint256 amount) internal {
                      if (amount == 0) return;
                      if (token == NATIVE) {
                          (bool success, ) = to.call{value: amount}("");
                          require(success, "eth send failed");
                      } else {
                          IERC20(token).safeTransfer(to, amount);
                      }
                  }
                  function _transferOut(address[] memory tokens, address to, uint256[] memory amounts) internal {
                      uint256 numTokens = tokens.length;
                      require(numTokens == amounts.length, "length mismatch");
                      for (uint256 i = 0; i < numTokens; ) {
                          _transferOut(tokens[i], to, amounts[i]);
                          unchecked {
                              i++;
                          }
                      }
                  }
                  function _selfBalance(address token) internal view returns (uint256) {
                      return (token == NATIVE) ? address(this).balance : IERC20(token).balanceOf(address(this));
                  }
                  function _selfBalance(IERC20 token) internal view returns (uint256) {
                      return token.balanceOf(address(this));
                  }
                  /// @notice Approves the stipulated contract to spend the given allowance in the given token
                  /// @dev PLS PAY ATTENTION to tokens that requires the approval to be set to 0 before changing it
                  function _safeApprove(address token, address to, uint256 value) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), "Safe Approve");
                  }
                  function _safeApproveInf(address token, address to) internal {
                      if (token == NATIVE) return;
                      if (IERC20(token).allowance(address(this), to) < LOWER_BOUND_APPROVAL) {
                          _safeApprove(token, to, 0);
                          _safeApprove(token, to, type(uint256).max);
                      }
                  }
                  function _wrap_unwrap_ETH(address tokenIn, address tokenOut, uint256 netTokenIn) internal {
                      if (tokenIn == NATIVE) IWETH(tokenOut).deposit{value: netTokenIn}();
                      else IWETH(tokenIn).withdraw(netTokenIn);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "../libraries/math/PMath.sol";
              import "../libraries/math/LogExpMath.sol";
              import "../StandardizedYield/PYIndex.sol";
              import "../libraries/MiniHelpers.sol";
              import "../libraries/Errors.sol";
              struct MarketState {
                  int256 totalPt;
                  int256 totalSy;
                  int256 totalLp;
                  address treasury;
                  /// immutable variables ///
                  int256 scalarRoot;
                  uint256 expiry;
                  /// fee data ///
                  uint256 lnFeeRateRoot;
                  uint256 reserveFeePercent; // base 100
                  /// last trade data ///
                  uint256 lastLnImpliedRate;
              }
              // params that are expensive to compute, therefore we pre-compute them
              struct MarketPreCompute {
                  int256 rateScalar;
                  int256 totalAsset;
                  int256 rateAnchor;
                  int256 feeRate;
              }
              // solhint-disable ordering
              library MarketMathCore {
                  using PMath for uint256;
                  using PMath for int256;
                  using LogExpMath for int256;
                  using PYIndexLib for PYIndex;
                  int256 internal constant MINIMUM_LIQUIDITY = 10 ** 3;
                  int256 internal constant PERCENTAGE_DECIMALS = 100;
                  uint256 internal constant DAY = 86400;
                  uint256 internal constant IMPLIED_RATE_TIME = 365 * DAY;
                  int256 internal constant MAX_MARKET_PROPORTION = (1e18 * 96) / 100;
                  using PMath for uint256;
                  using PMath for int256;
                  /*///////////////////////////////////////////////////////////////
                              UINT FUNCTIONS TO PROXY TO CORE FUNCTIONS
                  //////////////////////////////////////////////////////////////*/
                  function addLiquidity(
                      MarketState memory market,
                      uint256 syDesired,
                      uint256 ptDesired,
                      uint256 blockTime
                  ) internal pure returns (uint256 lpToReserve, uint256 lpToAccount, uint256 syUsed, uint256 ptUsed) {
                      (int256 _lpToReserve, int256 _lpToAccount, int256 _syUsed, int256 _ptUsed) = addLiquidityCore(
                          market,
                          syDesired.Int(),
                          ptDesired.Int(),
                          blockTime
                      );
                      lpToReserve = _lpToReserve.Uint();
                      lpToAccount = _lpToAccount.Uint();
                      syUsed = _syUsed.Uint();
                      ptUsed = _ptUsed.Uint();
                  }
                  function removeLiquidity(
                      MarketState memory market,
                      uint256 lpToRemove
                  ) internal pure returns (uint256 netSyToAccount, uint256 netPtToAccount) {
                      (int256 _syToAccount, int256 _ptToAccount) = removeLiquidityCore(market, lpToRemove.Int());
                      netSyToAccount = _syToAccount.Uint();
                      netPtToAccount = _ptToAccount.Uint();
                  }
                  function swapExactPtForSy(
                      MarketState memory market,
                      PYIndex index,
                      uint256 exactPtToMarket,
                      uint256 blockTime
                  ) internal pure returns (uint256 netSyToAccount, uint256 netSyFee, uint256 netSyToReserve) {
                      (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(
                          market,
                          index,
                          exactPtToMarket.neg(),
                          blockTime
                      );
                      netSyToAccount = _netSyToAccount.Uint();
                      netSyFee = _netSyFee.Uint();
                      netSyToReserve = _netSyToReserve.Uint();
                  }
                  function swapSyForExactPt(
                      MarketState memory market,
                      PYIndex index,
                      uint256 exactPtToAccount,
                      uint256 blockTime
                  ) internal pure returns (uint256 netSyToMarket, uint256 netSyFee, uint256 netSyToReserve) {
                      (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(
                          market,
                          index,
                          exactPtToAccount.Int(),
                          blockTime
                      );
                      netSyToMarket = _netSyToAccount.neg().Uint();
                      netSyFee = _netSyFee.Uint();
                      netSyToReserve = _netSyToReserve.Uint();
                  }
                  /*///////////////////////////////////////////////////////////////
                                  CORE FUNCTIONS
                  //////////////////////////////////////////////////////////////*/
                  function addLiquidityCore(
                      MarketState memory market,
                      int256 syDesired,
                      int256 ptDesired,
                      uint256 blockTime
                  ) internal pure returns (int256 lpToReserve, int256 lpToAccount, int256 syUsed, int256 ptUsed) {
                      /// ------------------------------------------------------------
                      /// CHECKS
                      /// ------------------------------------------------------------
                      if (syDesired == 0 || ptDesired == 0) revert Errors.MarketZeroAmountsInput();
                      if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
                      /// ------------------------------------------------------------
                      /// MATH
                      /// ------------------------------------------------------------
                      if (market.totalLp == 0) {
                          lpToAccount = PMath.sqrt((syDesired * ptDesired).Uint()).Int() - MINIMUM_LIQUIDITY;
                          lpToReserve = MINIMUM_LIQUIDITY;
                          syUsed = syDesired;
                          ptUsed = ptDesired;
                      } else {
                          int256 netLpByPt = (ptDesired * market.totalLp) / market.totalPt;
                          int256 netLpBySy = (syDesired * market.totalLp) / market.totalSy;
                          if (netLpByPt < netLpBySy) {
                              lpToAccount = netLpByPt;
                              ptUsed = ptDesired;
                              syUsed = (market.totalSy * lpToAccount).rawDivUp(market.totalLp);
                          } else {
                              lpToAccount = netLpBySy;
                              syUsed = syDesired;
                              ptUsed = (market.totalPt * lpToAccount).rawDivUp(market.totalLp);
                          }
                      }
                      if (lpToAccount <= 0 || syUsed <= 0 || ptUsed <= 0) revert Errors.MarketZeroAmountsOutput();
                      /// ------------------------------------------------------------
                      /// WRITE
                      /// ------------------------------------------------------------
                      market.totalSy += syUsed;
                      market.totalPt += ptUsed;
                      market.totalLp += lpToAccount + lpToReserve;
                  }
                  function removeLiquidityCore(
                      MarketState memory market,
                      int256 lpToRemove
                  ) internal pure returns (int256 netSyToAccount, int256 netPtToAccount) {
                      /// ------------------------------------------------------------
                      /// CHECKS
                      /// ------------------------------------------------------------
                      if (lpToRemove == 0) revert Errors.MarketZeroAmountsInput();
                      /// ------------------------------------------------------------
                      /// MATH
                      /// ------------------------------------------------------------
                      netSyToAccount = (lpToRemove * market.totalSy) / market.totalLp;
                      netPtToAccount = (lpToRemove * market.totalPt) / market.totalLp;
                      if (netSyToAccount == 0 && netPtToAccount == 0) revert Errors.MarketZeroAmountsOutput();
                      /// ------------------------------------------------------------
                      /// WRITE
                      /// ------------------------------------------------------------
                      market.totalLp = market.totalLp.subNoNeg(lpToRemove);
                      market.totalPt = market.totalPt.subNoNeg(netPtToAccount);
                      market.totalSy = market.totalSy.subNoNeg(netSyToAccount);
                  }
                  function executeTradeCore(
                      MarketState memory market,
                      PYIndex index,
                      int256 netPtToAccount,
                      uint256 blockTime
                  ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {
                      /// ------------------------------------------------------------
                      /// CHECKS
                      /// ------------------------------------------------------------
                      if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
                      if (market.totalPt <= netPtToAccount)
                          revert Errors.MarketInsufficientPtForTrade(market.totalPt, netPtToAccount);
                      /// ------------------------------------------------------------
                      /// MATH
                      /// ------------------------------------------------------------
                      MarketPreCompute memory comp = getMarketPreCompute(market, index, blockTime);
                      (netSyToAccount, netSyFee, netSyToReserve) = calcTrade(market, comp, index, netPtToAccount);
                      /// ------------------------------------------------------------
                      /// WRITE
                      /// ------------------------------------------------------------
                      _setNewMarketStateTrade(market, comp, index, netPtToAccount, netSyToAccount, netSyToReserve, blockTime);
                  }
                  function getMarketPreCompute(
                      MarketState memory market,
                      PYIndex index,
                      uint256 blockTime
                  ) internal pure returns (MarketPreCompute memory res) {
                      if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
                      uint256 timeToExpiry = market.expiry - blockTime;
                      res.rateScalar = _getRateScalar(market, timeToExpiry);
                      res.totalAsset = index.syToAsset(market.totalSy);
                      if (market.totalPt == 0 || res.totalAsset == 0)
                          revert Errors.MarketZeroTotalPtOrTotalAsset(market.totalPt, res.totalAsset);
                      res.rateAnchor = _getRateAnchor(
                          market.totalPt,
                          market.lastLnImpliedRate,
                          res.totalAsset,
                          res.rateScalar,
                          timeToExpiry
                      );
                      res.feeRate = _getExchangeRateFromImpliedRate(market.lnFeeRateRoot, timeToExpiry);
                  }
                  function calcTrade(
                      MarketState memory market,
                      MarketPreCompute memory comp,
                      PYIndex index,
                      int256 netPtToAccount
                  ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {
                      int256 preFeeExchangeRate = _getExchangeRate(
                          market.totalPt,
                          comp.totalAsset,
                          comp.rateScalar,
                          comp.rateAnchor,
                          netPtToAccount
                      );
                      int256 preFeeAssetToAccount = netPtToAccount.divDown(preFeeExchangeRate).neg();
                      int256 fee = comp.feeRate;
                      if (netPtToAccount > 0) {
                          int256 postFeeExchangeRate = preFeeExchangeRate.divDown(fee);
                          if (postFeeExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(postFeeExchangeRate);
                          fee = preFeeAssetToAccount.mulDown(PMath.IONE - fee);
                      } else {
                          fee = ((preFeeAssetToAccount * (PMath.IONE - fee)) / fee).neg();
                      }
                      int256 netAssetToReserve = (fee * market.reserveFeePercent.Int()) / PERCENTAGE_DECIMALS;
                      int256 netAssetToAccount = preFeeAssetToAccount - fee;
                      netSyToAccount = netAssetToAccount < 0
                          ? index.assetToSyUp(netAssetToAccount)
                          : index.assetToSy(netAssetToAccount);
                      netSyFee = index.assetToSy(fee);
                      netSyToReserve = index.assetToSy(netAssetToReserve);
                  }
                  function _setNewMarketStateTrade(
                      MarketState memory market,
                      MarketPreCompute memory comp,
                      PYIndex index,
                      int256 netPtToAccount,
                      int256 netSyToAccount,
                      int256 netSyToReserve,
                      uint256 blockTime
                  ) internal pure {
                      uint256 timeToExpiry = market.expiry - blockTime;
                      market.totalPt = market.totalPt.subNoNeg(netPtToAccount);
                      market.totalSy = market.totalSy.subNoNeg(netSyToAccount + netSyToReserve);
                      market.lastLnImpliedRate = _getLnImpliedRate(
                          market.totalPt,
                          index.syToAsset(market.totalSy),
                          comp.rateScalar,
                          comp.rateAnchor,
                          timeToExpiry
                      );
                      if (market.lastLnImpliedRate == 0) revert Errors.MarketZeroLnImpliedRate();
                  }
                  function _getRateAnchor(
                      int256 totalPt,
                      uint256 lastLnImpliedRate,
                      int256 totalAsset,
                      int256 rateScalar,
                      uint256 timeToExpiry
                  ) internal pure returns (int256 rateAnchor) {
                      int256 newExchangeRate = _getExchangeRateFromImpliedRate(lastLnImpliedRate, timeToExpiry);
                      if (newExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(newExchangeRate);
                      {
                          int256 proportion = totalPt.divDown(totalPt + totalAsset);
                          int256 lnProportion = _logProportion(proportion);
                          rateAnchor = newExchangeRate - lnProportion.divDown(rateScalar);
                      }
                  }
                  /// @notice Calculates the current market implied rate.
                  /// @return lnImpliedRate the implied rate
                  function _getLnImpliedRate(
                      int256 totalPt,
                      int256 totalAsset,
                      int256 rateScalar,
                      int256 rateAnchor,
                      uint256 timeToExpiry
                  ) internal pure returns (uint256 lnImpliedRate) {
                      // This will check for exchange rates < PMath.IONE
                      int256 exchangeRate = _getExchangeRate(totalPt, totalAsset, rateScalar, rateAnchor, 0);
                      // exchangeRate >= 1 so its ln >= 0
                      uint256 lnRate = exchangeRate.ln().Uint();
                      lnImpliedRate = (lnRate * IMPLIED_RATE_TIME) / timeToExpiry;
                  }
                  /// @notice Converts an implied rate to an exchange rate given a time to expiry. The
                  /// formula is E = e^rt
                  function _getExchangeRateFromImpliedRate(
                      uint256 lnImpliedRate,
                      uint256 timeToExpiry
                  ) internal pure returns (int256 exchangeRate) {
                      uint256 rt = (lnImpliedRate * timeToExpiry) / IMPLIED_RATE_TIME;
                      exchangeRate = LogExpMath.exp(rt.Int());
                  }
                  function _getExchangeRate(
                      int256 totalPt,
                      int256 totalAsset,
                      int256 rateScalar,
                      int256 rateAnchor,
                      int256 netPtToAccount
                  ) internal pure returns (int256 exchangeRate) {
                      int256 numerator = totalPt.subNoNeg(netPtToAccount);
                      int256 proportion = (numerator.divDown(totalPt + totalAsset));
                      if (proportion > MAX_MARKET_PROPORTION)
                          revert Errors.MarketProportionTooHigh(proportion, MAX_MARKET_PROPORTION);
                      int256 lnProportion = _logProportion(proportion);
                      exchangeRate = lnProportion.divDown(rateScalar) + rateAnchor;
                      if (exchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(exchangeRate);
                  }
                  function _logProportion(int256 proportion) internal pure returns (int256 res) {
                      if (proportion == PMath.IONE) revert Errors.MarketProportionMustNotEqualOne();
                      int256 logitP = proportion.divDown(PMath.IONE - proportion);
                      res = logitP.ln();
                  }
                  function _getRateScalar(MarketState memory market, uint256 timeToExpiry) internal pure returns (int256 rateScalar) {
                      rateScalar = (market.scalarRoot * IMPLIED_RATE_TIME.Int()) / timeToExpiry.Int();
                      if (rateScalar <= 0) revert Errors.MarketRateScalarBelowZero(rateScalar);
                  }
                  function setInitialLnImpliedRate(
                      MarketState memory market,
                      PYIndex index,
                      int256 initialAnchor,
                      uint256 blockTime
                  ) internal pure {
                      /// ------------------------------------------------------------
                      /// CHECKS
                      /// ------------------------------------------------------------
                      if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
                      /// ------------------------------------------------------------
                      /// MATH
                      /// ------------------------------------------------------------
                      int256 totalAsset = index.syToAsset(market.totalSy);
                      uint256 timeToExpiry = market.expiry - blockTime;
                      int256 rateScalar = _getRateScalar(market, timeToExpiry);
                      /// ------------------------------------------------------------
                      /// WRITE
                      /// ------------------------------------------------------------
                      market.lastLnImpliedRate = _getLnImpliedRate(
                          market.totalPt,
                          totalAsset,
                          rateScalar,
                          initialAnchor,
                          timeToExpiry
                      );
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "../libraries/Errors.sol";
              /// Adapted from UniswapV3's Oracle
              library OracleLib {
                  struct Observation {
                      uint32 blockTimestamp;
                      uint216 lnImpliedRateCumulative;
                      bool initialized;
                      // 1 SLOT = 256 bits
                  }
                  function transform(
                      Observation memory last,
                      uint32 blockTimestamp,
                      uint96 lnImpliedRate
                  ) public pure returns (Observation memory) {
                      return
                          Observation({
                              blockTimestamp: blockTimestamp,
                              lnImpliedRateCumulative: last.lnImpliedRateCumulative +
                                  uint216(lnImpliedRate) *
                                  (blockTimestamp - last.blockTimestamp),
                              initialized: true
                          });
                  }
                  function initialize(
                      Observation[65535] storage self,
                      uint32 time
                  ) public returns (uint16 cardinality, uint16 cardinalityNext) {
                      self[0] = Observation({blockTimestamp: time, lnImpliedRateCumulative: 0, initialized: true});
                      return (1, 1);
                  }
                  function write(
                      Observation[65535] storage self,
                      uint16 index,
                      uint32 blockTimestamp,
                      uint96 lnImpliedRate,
                      uint16 cardinality,
                      uint16 cardinalityNext
                  ) public returns (uint16 indexUpdated, uint16 cardinalityUpdated) {
                      Observation memory last = self[index];
                      // early return if we've already written an observation this block
                      if (last.blockTimestamp == blockTimestamp) return (index, cardinality);
                      // if the conditions are right, we can bump the cardinality
                      if (cardinalityNext > cardinality && index == (cardinality - 1)) {
                          cardinalityUpdated = cardinalityNext;
                      } else {
                          cardinalityUpdated = cardinality;
                      }
                      indexUpdated = (index + 1) % cardinalityUpdated;
                      self[indexUpdated] = transform(last, blockTimestamp, lnImpliedRate);
                  }
                  function grow(Observation[65535] storage self, uint16 current, uint16 next) public returns (uint16) {
                      if (current == 0) revert Errors.OracleUninitialized();
                      // no-op if the passed next value isn't greater than the current next value
                      if (next <= current) return current;
                      // store in each slot to prevent fresh SSTOREs in swaps
                      // this data will not be used because the initialized boolean is still false
                      for (uint16 i = current; i != next; ) {
                          self[i].blockTimestamp = 1;
                          unchecked {
                              ++i;
                          }
                      }
                      return next;
                  }
                  function binarySearch(
                      Observation[65535] storage self,
                      uint32 target,
                      uint16 index,
                      uint16 cardinality
                  ) public view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
                      uint256 l = (index + 1) % cardinality; // oldest observation
                      uint256 r = l + cardinality - 1; // newest observation
                      uint256 i;
                      while (true) {
                          i = (l + r) / 2;
                          beforeOrAt = self[i % cardinality];
                          // we've landed on an uninitialized observation, keep searching higher (more recently)
                          if (!beforeOrAt.initialized) {
                              l = i + 1;
                              continue;
                          }
                          atOrAfter = self[(i + 1) % cardinality];
                          bool targetAtOrAfter = beforeOrAt.blockTimestamp <= target;
                          // check if we've found the answer!
                          if (targetAtOrAfter && target <= atOrAfter.blockTimestamp) break;
                          if (!targetAtOrAfter) r = i - 1;
                          else l = i + 1;
                      }
                  }
                  function getSurroundingObservations(
                      Observation[65535] storage self,
                      uint32 target,
                      uint96 lnImpliedRate,
                      uint16 index,
                      uint16 cardinality
                  ) public view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
                      // optimistically set before to the newest observation
                      beforeOrAt = self[index];
                      // if the target is chronologically at or after the newest observation, we can early return
                      if (beforeOrAt.blockTimestamp <= target) {
                          if (beforeOrAt.blockTimestamp == target) {
                              // if newest observation equals target, we're in the same block, so we can ignore atOrAfter
                              return (beforeOrAt, atOrAfter);
                          } else {
                              // otherwise, we need to transform
                              return (beforeOrAt, transform(beforeOrAt, target, lnImpliedRate));
                          }
                      }
                      // now, set beforeOrAt to the oldest observation
                      beforeOrAt = self[(index + 1) % cardinality];
                      if (!beforeOrAt.initialized) beforeOrAt = self[0];
                      // ensure that the target is chronologically at or after the oldest observation
                      if (target < beforeOrAt.blockTimestamp) revert Errors.OracleTargetTooOld(target, beforeOrAt.blockTimestamp);
                      // if we've reached this point, we have to binary search
                      return binarySearch(self, target, index, cardinality);
                  }
                  function observeSingle(
                      Observation[65535] storage self,
                      uint32 time,
                      uint32 secondsAgo,
                      uint96 lnImpliedRate,
                      uint16 index,
                      uint16 cardinality
                  ) public view returns (uint216 lnImpliedRateCumulative) {
                      if (secondsAgo == 0) {
                          Observation memory last = self[index];
                          if (last.blockTimestamp != time) {
                              return transform(last, time, lnImpliedRate).lnImpliedRateCumulative;
                          }
                          return last.lnImpliedRateCumulative;
                      }
                      uint32 target = time - secondsAgo;
                      (Observation memory beforeOrAt, Observation memory atOrAfter) = getSurroundingObservations(
                          self,
                          target,
                          lnImpliedRate,
                          index,
                          cardinality
                      );
                      if (target == beforeOrAt.blockTimestamp) {
                          // we're at the left boundary
                          return beforeOrAt.lnImpliedRateCumulative;
                      } else if (target == atOrAfter.blockTimestamp) {
                          // we're at the right boundary
                          return atOrAfter.lnImpliedRateCumulative;
                      } else {
                          // we're in the middle
                          return (beforeOrAt.lnImpliedRateCumulative +
                              uint216(
                                  (uint256(atOrAfter.lnImpliedRateCumulative - beforeOrAt.lnImpliedRateCumulative) *
                                      (target - beforeOrAt.blockTimestamp)) / (atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp)
                              ));
                      }
                  }
                  function observe(
                      Observation[65535] storage self,
                      uint32 time,
                      uint32[] memory secondsAgos,
                      uint96 lnImpliedRate,
                      uint16 index,
                      uint16 cardinality
                  ) public view returns (uint216[] memory lnImpliedRateCumulative) {
                      if (cardinality == 0) revert Errors.OracleZeroCardinality();
                      lnImpliedRateCumulative = new uint216[](secondsAgos.length);
                      for (uint256 i = 0; i < lnImpliedRateCumulative.length; ++i) {
                          lnImpliedRateCumulative[i] = observeSingle(self, time, secondsAgos[i], lnImpliedRate, index, cardinality);
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "../../interfaces/IPGauge.sol";
              import "../../interfaces/IPVeToken.sol";
              import "../../interfaces/IPGaugeController.sol";
              import "../../interfaces/IStandardizedYield.sol";
              import "../RewardManager/RewardManager.sol";
              /**
              Invariants to maintain:
              - before any changes to active balance, updateAndDistributeRewards() must be called
               */
              abstract contract PendleGauge is RewardManager, IPGauge {
                  using PMath for uint256;
                  using SafeERC20 for IERC20;
                  using ArrayLib for address[];
                  address private immutable SY;
                  uint256 internal constant TOKENLESS_PRODUCTION = 40;
                  address internal immutable PENDLE;
                  IPVeToken internal immutable vePENDLE;
                  address internal immutable gaugeController;
                  uint256 public totalActiveSupply;
                  mapping(address => uint256) public activeBalance;
                  constructor(address _SY, address _vePendle, address _gaugeController) {
                      SY = _SY;
                      vePENDLE = IPVeToken(_vePendle);
                      gaugeController = _gaugeController;
                      PENDLE = IPGaugeController(gaugeController).pendle();
                  }
                  /**
                   * @dev Since rewardShares is based on activeBalance, user's activeBalance must be updated AFTER
                      rewards is updated
                   * @dev It's intended to have user's activeBalance updated when rewards is redeemed
                   */
                  function _redeemRewards(address user) internal virtual returns (uint256[] memory rewardsOut) {
                      _updateAndDistributeRewards(user);
                      _updateUserActiveBalance(user);
                      rewardsOut = _doTransferOutRewards(user, user);
                      emit RedeemRewards(user, rewardsOut);
                  }
                  function _updateUserActiveBalance(address user) internal virtual {
                      _updateUserActiveBalanceForTwo(user, address(0));
                  }
                  function _updateUserActiveBalanceForTwo(address user1, address user2) internal virtual {
                      if (user1 != address(0) && user1 != address(this)) _updateUserActiveBalancePrivate(user1);
                      if (user2 != address(0) && user2 != address(this)) _updateUserActiveBalancePrivate(user2);
                  }
                  /**
                   * @dev should only be callable from `_updateUserActiveBalanceForTwo` to guarantee user != address(0) && user != address(this)
                   */
                  function _updateUserActiveBalancePrivate(address user) private {
                      assert(user != address(0) && user != address(this));
                      uint256 lpBalance = _stakedBalance(user);
                      uint256 veBoostedLpBalance = _calcVeBoostedLpBalance(user, lpBalance);
                      uint256 newActiveBalance = PMath.min(veBoostedLpBalance, lpBalance);
                      totalActiveSupply = totalActiveSupply - activeBalance[user] + newActiveBalance;
                      activeBalance[user] = newActiveBalance;
                  }
                  function _calcVeBoostedLpBalance(address user, uint256 lpBalance) internal virtual returns (uint256) {
                      (uint256 vePendleSupply, uint256 vePendleBalance) = vePENDLE.totalSupplyAndBalanceCurrent(user);
                      // Inspired by Curve's Gauge
                      uint256 veBoostedLpBalance = (lpBalance * TOKENLESS_PRODUCTION) / 100;
                      if (vePendleSupply > 0) {
                          veBoostedLpBalance +=
                              (((_totalStaked() * vePendleBalance) / vePendleSupply) * (100 - TOKENLESS_PRODUCTION)) /
                              100;
                      }
                      return veBoostedLpBalance;
                  }
                  function _redeemExternalReward() internal virtual override {
                      IStandardizedYield(SY).claimRewards(address(this));
                      IPGaugeController(gaugeController).redeemMarketReward();
                  }
                  function _stakedBalance(address user) internal view virtual returns (uint256);
                  function _totalStaked() internal view virtual returns (uint256);
                  function _rewardSharesTotal() internal view virtual override returns (uint256) {
                      return totalActiveSupply;
                  }
                  function _rewardSharesUser(address user) internal view virtual override returns (uint256) {
                      return activeBalance[user];
                  }
                  function _getRewardTokens() internal view virtual override returns (address[] memory) {
                      address[] memory SYRewards = IStandardizedYield(SY).getRewardTokens();
                      if (SYRewards.contains(PENDLE)) return SYRewards;
                      return SYRewards.append(PENDLE);
                  }
                  function _beforeTokenTransfer(address from, address to, uint256) internal virtual {
                      _updateAndDistributeRewardsForTwo(from, to);
                  }
                  function _afterTokenTransfer(address from, address to, uint256) internal virtual {
                      _updateUserActiveBalanceForTwo(from, to);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.17;
              import "../../../interfaces/IPMarketV3.sol";
              import "../../../interfaces/IPMarketFactoryV3.sol";
              import "../../../interfaces/IPMarketSwapCallback.sol";
              import "../../erc20/PendleERC20.sol";
              import "../PendleGauge.sol";
              import "../OracleLib.sol";
              /**
              Invariance to maintain:
              - Internal balances totalPt & totalSy not interfered by people transferring tokens in directly
              - address(0) & address(this) should never have any rewards & activeBalance accounting done. This is
                  guaranteed by address(0) & address(this) check in each updateForTwo function
              */
              contract PendleMarketV3 is PendleERC20, PendleGauge, IPMarketV3 {
                  using PMath for uint256;
                  using PMath for int256;
                  using MarketMathCore for MarketState;
                  using SafeERC20 for IERC20;
                  using PYIndexLib for IPYieldToken;
                  using OracleLib for OracleLib.Observation[65535];
                  struct MarketStorage {
                      int128 totalPt;
                      int128 totalSy;
                      // 1 SLOT = 256 bits
                      uint96 lastLnImpliedRate;
                      uint16 observationIndex;
                      uint16 observationCardinality;
                      uint16 observationCardinalityNext;
                      // 1 SLOT = 144 bits
                  }
                  string private constant NAME = "Pendle Market";
                  string private constant SYMBOL = "PENDLE-LPT";
                  IPPrincipalToken internal immutable PT;
                  IStandardizedYield internal immutable SY;
                  IPYieldToken internal immutable YT;
                  address public immutable factory;
                  uint256 public immutable expiry;
                  int256 internal immutable scalarRoot;
                  int256 internal immutable initialAnchor;
                  uint80 internal immutable lnFeeRateRoot;
                  MarketStorage public _storage;
                  OracleLib.Observation[65535] public observations;
                  modifier notExpired() {
                      if (isExpired()) revert Errors.MarketExpired();
                      _;
                  }
                  constructor(
                      address _PT,
                      int256 _scalarRoot,
                      int256 _initialAnchor,
                      uint80 _lnFeeRateRoot,
                      address _vePendle,
                      address _gaugeController
                  ) PendleERC20(NAME, SYMBOL, 18) PendleGauge(IPPrincipalToken(_PT).SY(), _vePendle, _gaugeController) {
                      PT = IPPrincipalToken(_PT);
                      SY = IStandardizedYield(PT.SY());
                      YT = IPYieldToken(PT.YT());
                      (_storage.observationCardinality, _storage.observationCardinalityNext) = observations.initialize(
                          uint32(block.timestamp)
                      );
                      if (_scalarRoot <= 0) revert Errors.MarketScalarRootBelowZero(_scalarRoot);
                      scalarRoot = _scalarRoot;
                      initialAnchor = _initialAnchor;
                      lnFeeRateRoot = _lnFeeRateRoot;
                      expiry = IPPrincipalToken(_PT).expiry();
                      factory = msg.sender;
                  }
                  /**
                   * @notice PendleMarket allows users to provide in PT & SY in exchange for LPs, which
                   * will grant LP holders more exchange fee over time
                   * @dev will mint as much LP as possible such that the corresponding SY and PT used do
                   * not exceed `netSyDesired` and `netPtDesired`, respectively
                   * @dev PT and SY should be transferred to this contract prior to calling
                   * @dev will revert if PT is expired
                   */
                  function mint(
                      address receiver,
                      uint256 netSyDesired,
                      uint256 netPtDesired
                  ) external nonReentrant notExpired returns (uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed) {
                      MarketState memory market = readState(msg.sender);
                      PYIndex index = YT.newIndex();
                      uint256 lpToReserve;
                      (lpToReserve, netLpOut, netSyUsed, netPtUsed) = market.addLiquidity(
                          netSyDesired,
                          netPtDesired,
                          block.timestamp
                      );
                      // initializing the market
                      if (lpToReserve != 0) {
                          market.setInitialLnImpliedRate(index, initialAnchor, block.timestamp);
                          _mint(address(1), lpToReserve);
                      }
                      _mint(receiver, netLpOut);
                      _writeState(market);
                      if (_selfBalance(SY) < market.totalSy.Uint())
                          revert Errors.MarketInsufficientSyReceived(_selfBalance(SY), market.totalSy.Uint());
                      if (_selfBalance(PT) < market.totalPt.Uint())
                          revert Errors.MarketInsufficientPtReceived(_selfBalance(PT), market.totalPt.Uint());
                      emit Mint(receiver, netLpOut, netSyUsed, netPtUsed);
                  }
                  /**
                   * @notice LP Holders can burn their LP to receive back SY & PT proportionally
                   * to their share of the market
                   */
                  function burn(
                      address receiverSy,
                      address receiverPt,
                      uint256 netLpToBurn
                  ) external nonReentrant returns (uint256 netSyOut, uint256 netPtOut) {
                      MarketState memory market = readState(msg.sender);
                      _burn(address(this), netLpToBurn);
                      (netSyOut, netPtOut) = market.removeLiquidity(netLpToBurn);
                      if (receiverSy != address(this)) IERC20(SY).safeTransfer(receiverSy, netSyOut);
                      if (receiverPt != address(this)) IERC20(PT).safeTransfer(receiverPt, netPtOut);
                      _writeState(market);
                      emit Burn(receiverSy, receiverPt, netLpToBurn, netSyOut, netPtOut);
                  }
                  /**
                   * @notice Pendle Market allows swaps between PT & SY it is holding. This function
                   * aims to swap an exact amount of PT to SY.
                   * @dev steps working of this contract
                     - The outcome amount of SY will be precomputed by MarketMathLib
                     - Release the calculated amount of SY to receiver
                     - Callback to msg.sender if data.length > 0
                     - Ensure exactPtIn amount of PT has been transferred to this address
                   * @dev will revert if PT is expired
                   * @param data bytes data to be sent in the callback (if any)
                   */
                  function swapExactPtForSy(
                      address receiver,
                      uint256 exactPtIn,
                      bytes calldata data
                  ) external nonReentrant notExpired returns (uint256 netSyOut, uint256 netSyFee) {
                      MarketState memory market = readState(msg.sender);
                      uint256 netSyToReserve;
                      (netSyOut, netSyFee, netSyToReserve) = market.swapExactPtForSy(YT.newIndex(), exactPtIn, block.timestamp);
                      if (receiver != address(this)) IERC20(SY).safeTransfer(receiver, netSyOut);
                      IERC20(SY).safeTransfer(market.treasury, netSyToReserve);
                      _writeState(market);
                      if (data.length > 0) {
                          IPMarketSwapCallback(msg.sender).swapCallback(exactPtIn.neg(), netSyOut.Int(), data);
                      }
                      if (_selfBalance(PT) < market.totalPt.Uint())
                          revert Errors.MarketInsufficientPtReceived(_selfBalance(PT), market.totalPt.Uint());
                      emit Swap(msg.sender, receiver, exactPtIn.neg(), netSyOut.Int(), netSyFee, netSyToReserve);
                  }
                  /**
                   * @notice Pendle Market allows swaps between PT & SY it is holding. This function
                   * aims to swap SY for an exact amount of PT.
                   * @dev steps working of this function
                     - The exact outcome amount of PT will be transferred to receiver
                     - Callback to msg.sender if data.length > 0
                     - Ensure the calculated required amount of SY is transferred to this address
                   * @dev will revert if PT is expired
                   * @param data bytes data to be sent in the callback (if any)
                   */
                  function swapSyForExactPt(
                      address receiver,
                      uint256 exactPtOut,
                      bytes calldata data
                  ) external nonReentrant notExpired returns (uint256 netSyIn, uint256 netSyFee) {
                      MarketState memory market = readState(msg.sender);
                      uint256 netSyToReserve;
                      (netSyIn, netSyFee, netSyToReserve) = market.swapSyForExactPt(YT.newIndex(), exactPtOut, block.timestamp);
                      if (receiver != address(this)) IERC20(PT).safeTransfer(receiver, exactPtOut);
                      IERC20(SY).safeTransfer(market.treasury, netSyToReserve);
                      _writeState(market);
                      if (data.length > 0) {
                          IPMarketSwapCallback(msg.sender).swapCallback(exactPtOut.Int(), netSyIn.neg(), data);
                      }
                      // have received enough SY
                      if (_selfBalance(SY) < market.totalSy.Uint())
                          revert Errors.MarketInsufficientSyReceived(_selfBalance(SY), market.totalSy.Uint());
                      emit Swap(msg.sender, receiver, exactPtOut.Int(), netSyIn.neg(), netSyFee, netSyToReserve);
                  }
                  /// @notice forces balances to match reserves
                  function skim() external nonReentrant {
                      MarketState memory market = readState(msg.sender);
                      uint256 excessPt = _selfBalance(PT) - market.totalPt.Uint();
                      uint256 excessSy = _selfBalance(SY) - market.totalSy.Uint();
                      if (excessPt != 0) IERC20(PT).safeTransfer(market.treasury, excessPt);
                      if (excessSy != 0) IERC20(SY).safeTransfer(market.treasury, excessSy);
                  }
                  /**
                   * @notice redeems the user's reward
                   * @return amount of reward token redeemed, in the same order as `getRewardTokens()`
                   */
                  function redeemRewards(address user) external nonReentrant returns (uint256[] memory) {
                      return _redeemRewards(user);
                  }
                  /// @notice returns the list of reward tokens
                  function getRewardTokens() external view returns (address[] memory) {
                      return _getRewardTokens();
                  }
                  /*///////////////////////////////////////////////////////////////
                                              ORACLE
                  //////////////////////////////////////////////////////////////*/
                  function observe(uint32[] memory secondsAgos) external view returns (uint216[] memory lnImpliedRateCumulative) {
                      return
                          observations.observe(
                              uint32(block.timestamp),
                              secondsAgos,
                              _storage.lastLnImpliedRate,
                              _storage.observationIndex,
                              _storage.observationCardinality
                          );
                  }
                  function increaseObservationsCardinalityNext(uint16 cardinalityNext) external nonReentrant {
                      uint16 cardinalityNextOld = _storage.observationCardinalityNext;
                      uint16 cardinalityNextNew = observations.grow(cardinalityNextOld, cardinalityNext);
                      if (cardinalityNextOld != cardinalityNextNew) {
                          _storage.observationCardinalityNext = cardinalityNextNew;
                          emit IncreaseObservationCardinalityNext(cardinalityNextOld, cardinalityNextNew);
                      }
                  }
                  /*///////////////////////////////////////////////////////////////
                                              READ/WRITE STATES
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @notice read the state of the market from storage into memory for gas-efficient manipulation
                   */
                  function readState(address router) public view returns (MarketState memory market) {
                      market.totalPt = _storage.totalPt;
                      market.totalSy = _storage.totalSy;
                      market.totalLp = totalSupply().Int();
                      uint80 overriddenFee;
                      (market.treasury, overriddenFee, market.reserveFeePercent) = IPMarketFactoryV3(factory).getMarketConfig(
                          address(this),
                          router
                      );
                      market.lnFeeRateRoot = overriddenFee == 0 ? lnFeeRateRoot : overriddenFee;
                      market.scalarRoot = scalarRoot;
                      market.expiry = expiry;
                      market.lastLnImpliedRate = _storage.lastLnImpliedRate;
                  }
                  /// @notice write back the state of the market from memory to storage
                  function _writeState(MarketState memory market) internal {
                      uint96 lastLnImpliedRate96 = market.lastLnImpliedRate.Uint96();
                      int128 totalPt128 = market.totalPt.Int128();
                      int128 totalSy128 = market.totalSy.Int128();
                      (uint16 observationIndex, uint16 observationCardinality) = observations.write(
                          _storage.observationIndex,
                          uint32(block.timestamp),
                          _storage.lastLnImpliedRate,
                          _storage.observationCardinality,
                          _storage.observationCardinalityNext
                      );
                      _storage.totalPt = totalPt128;
                      _storage.totalSy = totalSy128;
                      _storage.lastLnImpliedRate = lastLnImpliedRate96;
                      _storage.observationIndex = observationIndex;
                      _storage.observationCardinality = observationCardinality;
                      emit UpdateImpliedRate(block.timestamp, market.lastLnImpliedRate);
                  }
                  function getNonOverrideLnFeeRateRoot() external view returns (uint80) {
                      return lnFeeRateRoot;
                  }
                  /*///////////////////////////////////////////////////////////////
                                          TRIVIAL FUNCTIONS
                  //////////////////////////////////////////////////////////////*/
                  function readTokens() external view returns (IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT) {
                      _SY = SY;
                      _PT = PT;
                      _YT = YT;
                  }
                  function isExpired() public view returns (bool) {
                      return MiniHelpers.isCurrentlyExpired(expiry);
                  }
                  /*///////////////////////////////////////////////////////////////
                                  PENDLE GAUGE - RELATED
                  //////////////////////////////////////////////////////////////*/
                  function _stakedBalance(address user) internal view override returns (uint256) {
                      return balanceOf(user);
                  }
                  function _totalStaked() internal view override returns (uint256) {
                      return totalSupply();
                  }
                  // solhint-disable-next-line ordering
                  function _beforeTokenTransfer(
                      address from,
                      address to,
                      uint256 amount
                  ) internal override(PendleERC20, PendleGauge) {
                      PendleGauge._beforeTokenTransfer(from, to, amount);
                  }
                  // solhint-disable-next-line ordering
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal override(PendleERC20, PendleGauge) {
                      PendleGauge._afterTokenTransfer(from, to, amount);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "./RewardManagerAbstract.sol";
              /// NOTE: This RewardManager is used with SY & YTv2 & PendleMarket. For YTv1, it will use RewardManagerAbstract
              /// NOTE: RewardManager must not have duplicated rewardTokens
              abstract contract RewardManager is RewardManagerAbstract {
                  using PMath for uint256;
                  using ArrayLib for uint256[];
                  uint256 public lastRewardBlock;
                  mapping(address => RewardState) public rewardState;
                  function _updateRewardIndex()
                      internal
                      virtual
                      override
                      returns (address[] memory tokens, uint256[] memory indexes)
                  {
                      tokens = _getRewardTokens();
                      indexes = new uint256[](tokens.length);
                      if (tokens.length == 0) return (tokens, indexes);
                      if (lastRewardBlock != block.number) {
                          // if we have not yet update the index for this block
                          lastRewardBlock = block.number;
                          uint256 totalShares = _rewardSharesTotal();
                          _redeemExternalReward();
                          for (uint256 i = 0; i < tokens.length; ++i) {
                              address token = tokens[i];
                              // the entire token balance of the contract must be the rewards of the contract
                              RewardState memory _state = rewardState[token];
                              (uint256 lastBalance, uint256 index) = (_state.lastBalance, _state.index);
                              uint256 accrued = _selfBalance(tokens[i]) - lastBalance;
                              if (index == 0) index = INITIAL_REWARD_INDEX;
                              if (totalShares != 0) index += accrued.divDown(totalShares);
                              rewardState[token] = RewardState({
                                  index: index.Uint128(),
                                  lastBalance: (lastBalance + accrued).Uint128()
                              });
                              indexes[i] = index;
                          }
                      } else {
                          for (uint256 i = 0; i < tokens.length; i++) {
                              indexes[i] = rewardState[tokens[i]].index;
                          }
                      }
                  }
                  /// @dev this function doesn't need redeemExternal since redeemExternal is bundled in updateRewardIndex
                  /// @dev this function also has to update rewardState.lastBalance
                  function _doTransferOutRewards(
                      address user,
                      address receiver
                  ) internal virtual override returns (uint256[] memory rewardAmounts) {
                      address[] memory tokens = _getRewardTokens();
                      rewardAmounts = new uint256[](tokens.length);
                      for (uint256 i = 0; i < tokens.length; i++) {
                          rewardAmounts[i] = userReward[tokens[i]][user].accrued;
                          if (rewardAmounts[i] != 0) {
                              userReward[tokens[i]][user].accrued = 0;
                              rewardState[tokens[i]].lastBalance -= rewardAmounts[i].Uint128();
                              _transferOut(tokens[i], receiver, rewardAmounts[i]);
                          }
                      }
                  }
                  function _getRewardTokens() internal view virtual returns (address[] memory);
                  function _rewardSharesTotal() internal view virtual returns (uint256);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "../../interfaces/IRewardManager.sol";
              import "../libraries/ArrayLib.sol";
              import "../libraries/TokenHelper.sol";
              import "../libraries/math/PMath.sol";
              import "./RewardManagerAbstract.sol";
              /// NOTE: RewardManager must not have duplicated rewardTokens
              abstract contract RewardManagerAbstract is IRewardManager, TokenHelper {
                  using PMath for uint256;
                  uint256 internal constant INITIAL_REWARD_INDEX = 1;
                  struct RewardState {
                      uint128 index;
                      uint128 lastBalance;
                  }
                  struct UserReward {
                      uint128 index;
                      uint128 accrued;
                  }
                  // [token] => [user] => (index,accrued)
                  mapping(address => mapping(address => UserReward)) public userReward;
                  function _updateAndDistributeRewards(address user) internal virtual {
                      _updateAndDistributeRewardsForTwo(user, address(0));
                  }
                  function _updateAndDistributeRewardsForTwo(address user1, address user2) internal virtual {
                      (address[] memory tokens, uint256[] memory indexes) = _updateRewardIndex();
                      if (tokens.length == 0) return;
                      if (user1 != address(0) && user1 != address(this)) _distributeRewardsPrivate(user1, tokens, indexes);
                      if (user2 != address(0) && user2 != address(this)) _distributeRewardsPrivate(user2, tokens, indexes);
                  }
                  // should only be callable from `_updateAndDistributeRewardsForTwo` to guarantee user != address(0) && user != address(this)
                  function _distributeRewardsPrivate(address user, address[] memory tokens, uint256[] memory indexes) private {
                      assert(user != address(0) && user != address(this));
                      uint256 userShares = _rewardSharesUser(user);
                      for (uint256 i = 0; i < tokens.length; ++i) {
                          address token = tokens[i];
                          uint256 index = indexes[i];
                          uint256 userIndex = userReward[token][user].index;
                          if (userIndex == 0) {
                              userIndex = INITIAL_REWARD_INDEX.Uint128();
                          }
                          if (userIndex == index || index == 0) continue;
                          uint256 deltaIndex = index - userIndex;
                          uint256 rewardDelta = userShares.mulDown(deltaIndex);
                          uint256 rewardAccrued = userReward[token][user].accrued + rewardDelta;
                          userReward[token][user] = UserReward({index: index.Uint128(), accrued: rewardAccrued.Uint128()});
                      }
                  }
                  function _updateRewardIndex() internal virtual returns (address[] memory tokens, uint256[] memory indexes);
                  function _redeemExternalReward() internal virtual;
                  function _doTransferOutRewards(
                      address user,
                      address receiver
                  ) internal virtual returns (uint256[] memory rewardAmounts);
                  function _rewardSharesUser(address user) internal view virtual returns (uint256);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "../../interfaces/IPYieldToken.sol";
              import "../../interfaces/IPPrincipalToken.sol";
              import "./SYUtils.sol";
              import "../libraries/math/PMath.sol";
              type PYIndex is uint256;
              library PYIndexLib {
                  using PMath for uint256;
                  using PMath for int256;
                  function newIndex(IPYieldToken YT) internal returns (PYIndex) {
                      return PYIndex.wrap(YT.pyIndexCurrent());
                  }
                  function syToAsset(PYIndex index, uint256 syAmount) internal pure returns (uint256) {
                      return SYUtils.syToAsset(PYIndex.unwrap(index), syAmount);
                  }
                  function assetToSy(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {
                      return SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount);
                  }
                  function assetToSyUp(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {
                      return SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount);
                  }
                  function syToAssetUp(PYIndex index, uint256 syAmount) internal pure returns (uint256) {
                      uint256 _index = PYIndex.unwrap(index);
                      return SYUtils.syToAssetUp(_index, syAmount);
                  }
                  function syToAsset(PYIndex index, int256 syAmount) internal pure returns (int256) {
                      int256 sign = syAmount < 0 ? int256(-1) : int256(1);
                      return sign * (SYUtils.syToAsset(PYIndex.unwrap(index), syAmount.abs())).Int();
                  }
                  function assetToSy(PYIndex index, int256 assetAmount) internal pure returns (int256) {
                      int256 sign = assetAmount < 0 ? int256(-1) : int256(1);
                      return sign * (SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount.abs())).Int();
                  }
                  function assetToSyUp(PYIndex index, int256 assetAmount) internal pure returns (int256) {
                      int256 sign = assetAmount < 0 ? int256(-1) : int256(1);
                      return sign * (SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount.abs())).Int();
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library SYUtils {
                  uint256 internal constant ONE = 1e18;
                  function syToAsset(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
                      return (syAmount * exchangeRate) / ONE;
                  }
                  function syToAssetUp(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
                      return (syAmount * exchangeRate + ONE - 1) / ONE;
                  }
                  function assetToSy(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
                      return (assetAmount * ONE) / exchangeRate;
                  }
                  function assetToSyUp(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
                      return (assetAmount * ONE + exchangeRate - 1) / exchangeRate;
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IPGauge {
                  function totalActiveSupply() external view returns (uint256);
                  function activeBalance(address user) external view returns (uint256);
                  // only available for newer factories. please check the verified contracts
                  event RedeemRewards(address indexed user, uint256[] rewardsOut);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IPGaugeController {
                  event MarketClaimReward(address indexed market, uint256 amount);
                  event ReceiveVotingResults(uint128 indexed wTime, address[] markets, uint256[] pendleAmounts);
                  event UpdateMarketReward(address indexed market, uint256 pendlePerSec, uint256 incentiveEndsAt);
                  function fundPendle(uint256 amount) external;
                  function withdrawPendle(uint256 amount) external;
                  function pendle() external returns (address);
                  function redeemMarketReward() external;
                  function rewardData(address pool) external view returns (uint128 pendlePerSec, uint128, uint128, uint128);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IPInterestManagerYT {
                  event CollectInterestFee(uint256 amountInterestFee);
                  function userInterest(address user) external view returns (uint128 lastPYIndex, uint128 accruedInterest);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "./IPPrincipalToken.sol";
              import "./IPYieldToken.sol";
              import "./IStandardizedYield.sol";
              import "./IPGauge.sol";
              import "../core/Market/MarketMathCore.sol";
              interface IPMarket is IERC20Metadata, IPGauge {
                  event Mint(address indexed receiver, uint256 netLpMinted, uint256 netSyUsed, uint256 netPtUsed);
                  event Burn(
                      address indexed receiverSy,
                      address indexed receiverPt,
                      uint256 netLpBurned,
                      uint256 netSyOut,
                      uint256 netPtOut
                  );
                  event Swap(
                      address indexed caller,
                      address indexed receiver,
                      int256 netPtOut,
                      int256 netSyOut,
                      uint256 netSyFee,
                      uint256 netSyToReserve
                  );
                  event UpdateImpliedRate(uint256 indexed timestamp, uint256 lnLastImpliedRate);
                  event IncreaseObservationCardinalityNext(
                      uint16 observationCardinalityNextOld,
                      uint16 observationCardinalityNextNew
                  );
                  function mint(
                      address receiver,
                      uint256 netSyDesired,
                      uint256 netPtDesired
                  ) external returns (uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed);
                  function burn(
                      address receiverSy,
                      address receiverPt,
                      uint256 netLpToBurn
                  ) external returns (uint256 netSyOut, uint256 netPtOut);
                  function swapExactPtForSy(
                      address receiver,
                      uint256 exactPtIn,
                      bytes calldata data
                  ) external returns (uint256 netSyOut, uint256 netSyFee);
                  function swapSyForExactPt(
                      address receiver,
                      uint256 exactPtOut,
                      bytes calldata data
                  ) external returns (uint256 netSyIn, uint256 netSyFee);
                  function redeemRewards(address user) external returns (uint256[] memory);
                  function readState(address router) external view returns (MarketState memory market);
                  function observe(uint32[] memory secondsAgos) external view returns (uint216[] memory lnImpliedRateCumulative);
                  function increaseObservationsCardinalityNext(uint16 cardinalityNext) external;
                  function readTokens() external view returns (IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT);
                  function getRewardTokens() external view returns (address[] memory);
                  function isExpired() external view returns (bool);
                  function expiry() external view returns (uint256);
                  function observations(
                      uint256 index
                  ) external view returns (uint32 blockTimestamp, uint216 lnImpliedRateCumulative, bool initialized);
                  function _storage()
                      external
                      view
                      returns (
                          int128 totalPt,
                          int128 totalSy,
                          uint96 lastLnImpliedRate,
                          uint16 observationIndex,
                          uint16 observationCardinality,
                          uint16 observationCardinalityNext
                      );
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IPMarketFactoryV3 {
                  event SetOverriddenFee(address indexed router, address indexed market, uint80 lnFeeRateRoot);
                  event CreateNewMarket(
                      address indexed market,
                      address indexed PT,
                      int256 scalarRoot,
                      int256 initialAnchor,
                      uint256 lnFeeRateRoot
                  );
                  event NewTreasuryAndFeeReserve(address indexed treasury, uint8 reserveFeePercent);
                  function isValidMarket(address market) external view returns (bool);
                  // If this is changed, change the readState function in market as well
                  function getMarketConfig(
                      address market,
                      address router
                  ) external view returns (address treasury, uint80 overriddenFee, uint8 reserveFeePercent);
                  function createNewMarket(
                      address PT,
                      int256 scalarRoot,
                      int256 initialAnchor,
                      uint80 lnFeeRateRoot
                  ) external returns (address market);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IPMarketSwapCallback {
                  function swapCallback(int256 ptToAccount, int256 syToAccount, bytes calldata data) external;
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "./IPMarket.sol";
              interface IPMarketV3 is IPMarket {
                  function getNonOverrideLnFeeRateRoot() external view returns (uint80);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              interface IPPrincipalToken is IERC20Metadata {
                  function burnByYT(address user, uint256 amount) external;
                  function mintByYT(address user, uint256 amount) external;
                  function initialize(address _YT) external;
                  function SY() external view returns (address);
                  function YT() external view returns (address);
                  function factory() external view returns (address);
                  function expiry() external view returns (uint256);
                  function isExpired() external view returns (bool);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IPVeToken {
                  // ============= USER INFO =============
                  function balanceOf(address user) external view returns (uint128);
                  function positionData(address user) external view returns (uint128 amount, uint128 expiry);
                  // ============= META DATA =============
                  function totalSupplyStored() external view returns (uint128);
                  function totalSupplyCurrent() external returns (uint128);
                  function totalSupplyAndBalanceCurrent(address user) external returns (uint128, uint128);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "./IRewardManager.sol";
              import "./IPInterestManagerYT.sol";
              interface IPYieldToken is IERC20Metadata, IRewardManager, IPInterestManagerYT {
                  event NewInterestIndex(uint256 indexed newIndex);
                  event Mint(
                      address indexed caller,
                      address indexed receiverPT,
                      address indexed receiverYT,
                      uint256 amountSyToMint,
                      uint256 amountPYOut
                  );
                  event Burn(address indexed caller, address indexed receiver, uint256 amountPYToRedeem, uint256 amountSyOut);
                  event RedeemRewards(address indexed user, uint256[] amountRewardsOut);
                  event RedeemInterest(address indexed user, uint256 interestOut);
                  event CollectRewardFee(address indexed rewardToken, uint256 amountRewardFee);
                  function mintPY(address receiverPT, address receiverYT) external returns (uint256 amountPYOut);
                  function redeemPY(address receiver) external returns (uint256 amountSyOut);
                  function redeemPYMulti(
                      address[] calldata receivers,
                      uint256[] calldata amountPYToRedeems
                  ) external returns (uint256[] memory amountSyOuts);
                  function redeemDueInterestAndRewards(
                      address user,
                      bool redeemInterest,
                      bool redeemRewards
                  ) external returns (uint256 interestOut, uint256[] memory rewardsOut);
                  function rewardIndexesCurrent() external returns (uint256[] memory);
                  function pyIndexCurrent() external returns (uint256);
                  function pyIndexStored() external view returns (uint256);
                  function getRewardTokens() external view returns (address[] memory);
                  function SY() external view returns (address);
                  function PT() external view returns (address);
                  function factory() external view returns (address);
                  function expiry() external view returns (uint256);
                  function isExpired() external view returns (bool);
                  function doCacheIndexSameBlock() external view returns (bool);
                  function pyIndexLastUpdatedBlock() external view returns (uint128);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IRewardManager {
                  function userReward(address token, address user) external view returns (uint128 index, uint128 accrued);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              /*
               * MIT License
               * ===========
               *
               * Permission is hereby granted, free of charge, to any person obtaining a copy
               * of this software and associated documentation files (the "Software"), to deal
               * in the Software without restriction, including without limitation the rights
               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
               * copies of the Software, and to permit persons to whom the Software is
               * furnished to do so, subject to the following conditions:
               *
               * The above copyright notice and this permission notice shall be included in all
               * copies or substantial portions of the Software.
               *
               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
               */
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              interface IStandardizedYield is IERC20Metadata {
                  /// @dev Emitted when any base tokens is deposited to mint shares
                  event Deposit(
                      address indexed caller,
                      address indexed receiver,
                      address indexed tokenIn,
                      uint256 amountDeposited,
                      uint256 amountSyOut
                  );
                  /// @dev Emitted when any shares are redeemed for base tokens
                  event Redeem(
                      address indexed caller,
                      address indexed receiver,
                      address indexed tokenOut,
                      uint256 amountSyToRedeem,
                      uint256 amountTokenOut
                  );
                  /// @dev check `assetInfo()` for more information
                  enum AssetType {
                      TOKEN,
                      LIQUIDITY
                  }
                  /// @dev Emitted when (`user`) claims their rewards
                  event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts);
                  /**
                   * @notice mints an amount of shares by depositing a base token.
                   * @param receiver shares recipient address
                   * @param tokenIn address of the base tokens to mint shares
                   * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`)
                   * @param minSharesOut reverts if amount of shares minted is lower than this
                   * @return amountSharesOut amount of shares minted
                   * @dev Emits a {Deposit} event
                   *
                   * Requirements:
                   * - (`tokenIn`) must be a valid base token.
                   */
                  function deposit(
                      address receiver,
                      address tokenIn,
                      uint256 amountTokenToDeposit,
                      uint256 minSharesOut
                  ) external payable returns (uint256 amountSharesOut);
                  /**
                   * @notice redeems an amount of base tokens by burning some shares
                   * @param receiver recipient address
                   * @param amountSharesToRedeem amount of shares to be burned
                   * @param tokenOut address of the base token to be redeemed
                   * @param minTokenOut reverts if amount of base token redeemed is lower than this
                   * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender`
                   * @return amountTokenOut amount of base tokens redeemed
                   * @dev Emits a {Redeem} event
                   *
                   * Requirements:
                   * - (`tokenOut`) must be a valid base token.
                   */
                  function redeem(
                      address receiver,
                      uint256 amountSharesToRedeem,
                      address tokenOut,
                      uint256 minTokenOut,
                      bool burnFromInternalBalance
                  ) external returns (uint256 amountTokenOut);
                  /**
                   * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account
                   * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy
                   he can mint must be X * exchangeRate / 1e18
                   * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication
                   & division
                   */
                  function exchangeRate() external view returns (uint256 res);
                  /**
                   * @notice claims reward for (`user`)
                   * @param user the user receiving their rewards
                   * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
                   * @dev
                   * Emits a `ClaimRewards` event
                   * See {getRewardTokens} for list of reward tokens
                   */
                  function claimRewards(address user) external returns (uint256[] memory rewardAmounts);
                  /**
                   * @notice get the amount of unclaimed rewards for (`user`)
                   * @param user the user to check for
                   * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
                   */
                  function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts);
                  function rewardIndexesCurrent() external returns (uint256[] memory indexes);
                  function rewardIndexesStored() external view returns (uint256[] memory indexes);
                  /**
                   * @notice returns the list of reward token addresses
                   */
                  function getRewardTokens() external view returns (address[] memory);
                  /**
                   * @notice returns the address of the underlying yield token
                   */
                  function yieldToken() external view returns (address);
                  /**
                   * @notice returns all tokens that can mint this SY
                   */
                  function getTokensIn() external view returns (address[] memory res);
                  /**
                   * @notice returns all tokens that can be redeemed by this SY
                   */
                  function getTokensOut() external view returns (address[] memory res);
                  function isValidTokenIn(address token) external view returns (bool);
                  function isValidTokenOut(address token) external view returns (bool);
                  function previewDeposit(
                      address tokenIn,
                      uint256 amountTokenToDeposit
                  ) external view returns (uint256 amountSharesOut);
                  function previewRedeem(
                      address tokenOut,
                      uint256 amountSharesToRedeem
                  ) external view returns (uint256 amountTokenOut);
                  /**
                   * @notice This function contains information to interpret what the asset is
                   * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens,
                      2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the chain)
                   * @return assetAddress the address of the asset
                   * @return assetDecimals the decimals of the asset
                   */
                  function assetInfo() external view returns (AssetType assetType, address assetAddress, uint8 assetDecimals);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              /*
               * MIT License
               * ===========
               *
               * Permission is hereby granted, free of charge, to any person obtaining a copy
               * of this software and associated documentation files (the "Software"), to deal
               * in the Software without restriction, including without limitation the rights
               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
               * copies of the Software, and to permit persons to whom the Software is
               * furnished to do so, subject to the following conditions:
               *
               * The above copyright notice and this permission notice shall be included in all
               * copies or substantial portions of the Software.
               *
               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
               */
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              interface IWETH is IERC20 {
                  event Deposit(address indexed dst, uint256 wad);
                  event Withdrawal(address indexed src, uint256 wad);
                  function deposit() external payable;
                  function withdraw(uint256 wad) external;
              }
              

              File 16 of 30: PendleUsualUSD0PPSY
              // 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 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) (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.7.0) (security/Pausable.sol)
              pragma solidity ^0.8.0;
              import "../utils/Context.sol";
              /**
               * @dev Contract module which allows children to implement an emergency stop
               * mechanism that can be triggered by an authorized account.
               *
               * This module is used through inheritance. It will make available the
               * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
               * the functions of your contract. Note that they will not be pausable by
               * simply including this module, only once the modifiers are put in place.
               */
              abstract contract Pausable is Context {
                  /**
                   * @dev Emitted when the pause is triggered by `account`.
                   */
                  event Paused(address account);
                  /**
                   * @dev Emitted when the pause is lifted by `account`.
                   */
                  event Unpaused(address account);
                  bool private _paused;
                  /**
                   * @dev Initializes the contract in unpaused state.
                   */
                  constructor() {
                      _paused = false;
                  }
                  /**
                   * @dev Modifier to make a function callable only when the contract is not paused.
                   *
                   * Requirements:
                   *
                   * - The contract must not be paused.
                   */
                  modifier whenNotPaused() {
                      _requireNotPaused();
                      _;
                  }
                  /**
                   * @dev Modifier to make a function callable only when the contract is paused.
                   *
                   * Requirements:
                   *
                   * - The contract must be paused.
                   */
                  modifier whenPaused() {
                      _requirePaused();
                      _;
                  }
                  /**
                   * @dev Returns true if the contract is paused, and false otherwise.
                   */
                  function paused() public view virtual returns (bool) {
                      return _paused;
                  }
                  /**
                   * @dev Throws if the contract is paused.
                   */
                  function _requireNotPaused() internal view virtual {
                      require(!paused(), "Pausable: paused");
                  }
                  /**
                   * @dev Throws if the contract is not paused.
                   */
                  function _requirePaused() internal view virtual {
                      require(paused(), "Pausable: not paused");
                  }
                  /**
                   * @dev Triggers stopped state.
                   *
                   * Requirements:
                   *
                   * - The contract must not be paused.
                   */
                  function _pause() internal virtual whenNotPaused {
                      _paused = true;
                      emit Paused(_msgSender());
                  }
                  /**
                   * @dev Returns to normal state.
                   *
                   * Requirements:
                   *
                   * - The contract must be paused.
                   */
                  function _unpause() internal virtual whenPaused {
                      _paused = false;
                      emit Unpaused(_msgSender());
                  }
              }
              // 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) (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: 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 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.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) (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.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) (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 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.8.0) (utils/cryptography/draft-EIP712.sol)
              pragma solidity ^0.8.0;
              // EIP-712 is Final as of 2022-08-11. This file is deprecated.
              import "./EIP712.sol";
              // 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 (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/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) (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 (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: GPL-3.0-or-later
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "@openzeppelin/contracts/utils/Context.sol";
              /**
               * @dev Pendle's ERC20 implementation, modified from @openzeppelin implementation
               * Changes are:
               * - comes with built-in reentrancy protection, storage-packed with totalSupply variable
               * - delete increaseAllowance / decreaseAllowance
               * - add nonReentrancy protection to transfer / transferFrom functions
               * - allow decimals to be passed in
               * - block self-transfer by default
               */
              // solhint-disable
              contract PendleERC20 is Context, IERC20, IERC20Metadata {
                  uint8 private constant _NOT_ENTERED = 1;
                  uint8 private constant _ENTERED = 2;
                  mapping(address => uint256) private _balances;
                  mapping(address => mapping(address => uint256)) private _allowances;
                  uint248 private _totalSupply;
                  uint8 private _status;
                  string private _name;
                  string private _symbol;
                  uint8 public immutable decimals;
                  /**
                   * @dev Prevents a contract from calling itself, directly or indirectly.
                   * Calling a `nonReentrant` function from another `nonReentrant`
                   * function is not supported. It is possible to prevent this from happening
                   * by making the `nonReentrant` function external, and making it call a
                   * `private` function that does the actual work.
                   */
                  modifier nonReentrant() {
                      // On the first call to nonReentrant, _notEntered will be true
                      require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                      // Any calls to nonReentrant after this point will fail
                      _status = _ENTERED;
                      _;
                      // By storing the original value once again, a refund is triggered (see
                      // https://eips.ethereum.org/EIPS/eip-2200)
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Sets the values for {name}, {symbol} and {decimals}.
                   *
                   * All three of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_, uint8 decimals_) {
                      _name = name_;
                      _symbol = symbol_;
                      decimals = decimals_;
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual override returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address to, uint256 amount) external virtual override nonReentrant returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) external virtual override returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Emits an {Approval} event indicating the updated allowance. This is not
                   * required by the EIP. See the note at the beginning of {ERC20}.
                   *
                   * NOTE: Does not update the allowance if the current allowance
                   * is the maximum `uint256`.
                   *
                   * Requirements:
                   *
                   * - `from` and `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``from``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) external virtual override nonReentrant returns (bool) {
                      address spender = _msgSender();
                      _spendAllowance(from, spender, amount);
                      _transfer(from, to, amount);
                      return true;
                  }
                  /**
                   * @dev Moves `amount` of tokens from `sender` to `recipient`.
                   *
                   * This internal function is equivalent to {transfer}, and can be used to
                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                   *
                   * Emits a {Transfer} event.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   */
                  function _transfer(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), "ERC20: transfer from the zero address");
                      require(to != address(0), "ERC20: transfer to the zero address");
                      require(from != to, "ERC20: transfer to self");
                      _beforeTokenTransfer(from, to, amount);
                      uint256 fromBalance = _balances[from];
                      require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                      unchecked {
                          _balances[from] = fromBalance - amount;
                      }
                      _balances[to] += amount;
                      emit Transfer(from, to, amount);
                      _afterTokenTransfer(from, to, amount);
                  }
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
                      _beforeTokenTransfer(address(0), account, amount);
                      _totalSupply += toUint248(amount);
                      _balances[account] += amount;
                      emit Transfer(address(0), account, amount);
                      _afterTokenTransfer(address(0), account, amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
                      _beforeTokenTransfer(account, address(0), amount);
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                      unchecked {
                          _balances[account] = accountBalance - amount;
                      }
                      _totalSupply -= toUint248(amount);
                      emit Transfer(account, address(0), amount);
                      _afterTokenTransfer(account, address(0), amount);
                  }
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                   *
                   * This internal function is equivalent to `approve`, and can be used to
                   * e.g. set automatic allowances for certain subsystems, etc.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `owner` cannot be the zero address.
                   * - `spender` cannot be the zero address.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      require(owner != address(0), "ERC20: approve from the zero address");
                      require(spender != address(0), "ERC20: approve to the zero address");
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                  }
                  /**
                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                   *
                   * Does not update the allowance amount in case of infinite allowance.
                   * Revert if not enough allowance is available.
                   *
                   * Might emit an {Approval} event.
                   */
                  function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                      uint256 currentAllowance = allowance(owner, spender);
                      if (currentAllowance != type(uint256).max) {
                          require(currentAllowance >= amount, "ERC20: insufficient allowance");
                          unchecked {
                              _approve(owner, spender, currentAllowance - amount);
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  /**
                   * @dev Hook that is called after any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * has been transferred to `to`.
                   * - when `from` is zero, `amount` tokens have been minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  function toUint248(uint256 x) internal virtual returns (uint248) {
                      require(x <= type(uint248).max); // signed, lim = bit-1
                      return uint248(x);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "./PendleERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "@openzeppelin/contracts/utils/Context.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
              import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
              import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
              import "@openzeppelin/contracts/utils/Counters.sol";
              /// @dev forked from OZ's ERC20Permit
              contract PendleERC20Permit is PendleERC20, 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)");
                  constructor(
                      string memory name_,
                      string memory symbol_,
                      uint8 decimals_
                  ) PendleERC20(name_, symbol_, decimals_) 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: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library ArrayLib {
                  function sum(uint256[] memory input) internal pure returns (uint256) {
                      uint256 value = 0;
                      for (uint256 i = 0; i < input.length; ) {
                          value += input[i];
                          unchecked {
                              i++;
                          }
                      }
                      return value;
                  }
                  /// @notice return index of the element if found, else return uint256.max
                  function find(address[] memory array, address element) internal pure returns (uint256 index) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return i;
                          unchecked {
                              i++;
                          }
                      }
                      return type(uint256).max;
                  }
                  function append(address[] memory inp, address element) internal pure returns (address[] memory out) {
                      uint256 length = inp.length;
                      out = new address[](length + 1);
                      for (uint256 i = 0; i < length; ) {
                          out[i] = inp[i];
                          unchecked {
                              i++;
                          }
                      }
                      out[length] = element;
                  }
                  function appendHead(address[] memory inp, address element) internal pure returns (address[] memory out) {
                      uint256 length = inp.length;
                      out = new address[](length + 1);
                      out[0] = element;
                      for (uint256 i = 1; i <= length; ) {
                          out[i] = inp[i - 1];
                          unchecked {
                              i++;
                          }
                      }
                  }
                  /**
                   * @dev This function assumes a and b each contains unidentical elements
                   * @param a array of addresses a
                   * @param b array of addresses b
                   * @return out Concatenation of a and b containing unidentical elements
                   */
                  function merge(address[] memory a, address[] memory b) internal pure returns (address[] memory out) {
                      unchecked {
                          uint256 countUnidenticalB = 0;
                          bool[] memory isUnidentical = new bool[](b.length);
                          for (uint256 i = 0; i < b.length; ++i) {
                              if (!contains(a, b[i])) {
                                  countUnidenticalB++;
                                  isUnidentical[i] = true;
                              }
                          }
                          out = new address[](a.length + countUnidenticalB);
                          for (uint256 i = 0; i < a.length; ++i) {
                              out[i] = a[i];
                          }
                          uint256 id = a.length;
                          for (uint256 i = 0; i < b.length; ++i) {
                              if (isUnidentical[i]) {
                                  out[id++] = b[i];
                              }
                          }
                      }
                  }
                  // various version of contains
                  function contains(address[] memory array, address element) internal pure returns (bool) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return true;
                          unchecked {
                              i++;
                          }
                      }
                      return false;
                  }
                  function contains(bytes4[] memory array, bytes4 element) internal pure returns (bool) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return true;
                          unchecked {
                              i++;
                          }
                      }
                      return false;
                  }
                  function create(address a) internal pure returns (address[] memory res) {
                      res = new address[](1);
                      res[0] = a;
                  }
                  function create(address a, address b) internal pure returns (address[] memory res) {
                      res = new address[](2);
                      res[0] = a;
                      res[1] = b;
                  }
                  function create(address a, address b, address c) internal pure returns (address[] memory res) {
                      res = new address[](3);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                  }
                  function create(address a, address b, address c, address d) internal pure returns (address[] memory res) {
                      res = new address[](4);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                      res[3] = d;
                  }
                  function create(
                      address a,
                      address b,
                      address c,
                      address d,
                      address e
                  ) internal pure returns (address[] memory res) {
                      res = new address[](5);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                      res[3] = d;
                      res[4] = e;
                  }
                  function create(uint256 a) internal pure returns (uint256[] memory res) {
                      res = new uint256[](1);
                      res[0] = a;
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
              contract BoringOwnableUpgradeableData {
                  address public owner;
                  address public pendingOwner;
              }
              abstract contract BoringOwnableUpgradeable is BoringOwnableUpgradeableData, Initializable {
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  function __BoringOwnable_init() internal onlyInitializing {
                      owner = msg.sender;
                  }
                  /// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
                  /// Can only be invoked by the current `owner`.
                  /// @param newOwner Address of the new owner.
                  /// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
                  /// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
                  function transferOwnership(address newOwner, bool direct, bool renounce) public onlyOwner {
                      if (direct) {
                          // Checks
                          require(newOwner != address(0) || renounce, "Ownable: zero address");
                          // Effects
                          emit OwnershipTransferred(owner, newOwner);
                          owner = newOwner;
                          pendingOwner = address(0);
                      } else {
                          // Effects
                          pendingOwner = newOwner;
                      }
                  }
                  /// @notice Needs to be called by `pendingOwner` to claim ownership.
                  function claimOwnership() public {
                      address _pendingOwner = pendingOwner;
                      // Checks
                      require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");
                      // Effects
                      emit OwnershipTransferred(owner, _pendingOwner);
                      owner = _pendingOwner;
                      pendingOwner = address(0);
                  }
                  /// @notice Only allows the `owner` to execute the function.
                  modifier onlyOwner() {
                      require(msg.sender == owner, "Ownable: caller is not the owner");
                      _;
                  }
                  uint256[48] private __gap;
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library Errors {
                  // BulkSeller
                  error BulkInsufficientSyForTrade(uint256 currentAmount, uint256 requiredAmount);
                  error BulkInsufficientTokenForTrade(uint256 currentAmount, uint256 requiredAmount);
                  error BulkInSufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
                  error BulkInSufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  error BulkInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
                  error BulkNotMaintainer();
                  error BulkNotAdmin();
                  error BulkSellerAlreadyExisted(address token, address SY, address bulk);
                  error BulkSellerInvalidToken(address token, address SY);
                  error BulkBadRateTokenToSy(uint256 actualRate, uint256 currentRate, uint256 eps);
                  error BulkBadRateSyToToken(uint256 actualRate, uint256 currentRate, uint256 eps);
                  // APPROX
                  error ApproxFail();
                  error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps);
                  error ApproxBinarySearchInputInvalid(
                      uint256 approxGuessMin,
                      uint256 approxGuessMax,
                      uint256 minGuessMin,
                      uint256 maxGuessMax
                  );
                  // MARKET + MARKET MATH CORE
                  error MarketExpired();
                  error MarketZeroAmountsInput();
                  error MarketZeroAmountsOutput();
                  error MarketZeroLnImpliedRate();
                  error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount);
                  error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance);
                  error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
                  error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset);
                  error MarketExchangeRateBelowOne(int256 exchangeRate);
                  error MarketProportionMustNotEqualOne();
                  error MarketRateScalarBelowZero(int256 rateScalar);
                  error MarketScalarRootBelowZero(int256 scalarRoot);
                  error MarketProportionTooHigh(int256 proportion, int256 maxProportion);
                  error OracleUninitialized();
                  error OracleTargetTooOld(uint32 target, uint32 oldest);
                  error OracleZeroCardinality();
                  error MarketFactoryExpiredPt();
                  error MarketFactoryInvalidPt();
                  error MarketFactoryMarketExists();
                  error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot);
                  error MarketFactoryOverriddenFeeTooHigh(uint80 overriddenFee, uint256 marketLnFeeRateRoot);
                  error MarketFactoryReserveFeePercentTooHigh(uint8 reserveFeePercent, uint8 maxReserveFeePercent);
                  error MarketFactoryZeroTreasury();
                  error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor);
                  error MFNotPendleMarket(address addr);
                  // ROUTER
                  error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut);
                  error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
                  error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut);
                  error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut);
                  error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut);
                  error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay);
                  error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay);
                  error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed);
                  error RouterTimeRangeZero();
                  error RouterCallbackNotPendleMarket(address caller);
                  error RouterInvalidAction(bytes4 selector);
                  error RouterInvalidFacet(address facet);
                  error RouterKyberSwapDataZero();
                  error SimulationResults(bool success, bytes res);
                  // YIELD CONTRACT
                  error YCExpired();
                  error YCNotExpired();
                  error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy);
                  error YCNothingToRedeem();
                  error YCPostExpiryDataNotSet();
                  error YCNoFloatingSy();
                  // YieldFactory
                  error YCFactoryInvalidExpiry();
                  error YCFactoryYieldContractExisted();
                  error YCFactoryZeroExpiryDivisor();
                  error YCFactoryZeroTreasury();
                  error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate);
                  error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate);
                  // SY
                  error SYInvalidTokenIn(address token);
                  error SYInvalidTokenOut(address token);
                  error SYZeroDeposit();
                  error SYZeroRedeem();
                  error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut);
                  error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  // SY-specific
                  error SYQiTokenMintFailed(uint256 errCode);
                  error SYQiTokenRedeemFailed(uint256 errCode);
                  error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1);
                  error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax);
                  error SYCurveInvalidPid();
                  error SYCurve3crvPoolNotFound();
                  error SYApeDepositAmountTooSmall(uint256 amountDeposited);
                  error SYBalancerInvalidPid();
                  error SYInvalidRewardToken(address token);
                  error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable);
                  error SYBalancerReentrancy();
                  error NotFromTrustedRemote(uint16 srcChainId, bytes path);
                  error ApxETHNotEnoughBuffer();
                  // Liquidity Mining
                  error VCInactivePool(address pool);
                  error VCPoolAlreadyActive(address pool);
                  error VCZeroVePendle(address user);
                  error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight);
                  error VCEpochNotFinalized(uint256 wTime);
                  error VCPoolAlreadyAddAndRemoved(address pool);
                  error VEInvalidNewExpiry(uint256 newExpiry);
                  error VEExceededMaxLockTime();
                  error VEInsufficientLockTime();
                  error VENotAllowedReduceExpiry();
                  error VEZeroAmountLocked();
                  error VEPositionNotExpired();
                  error VEZeroPosition();
                  error VEZeroSlope(uint128 bias, uint128 slope);
                  error VEReceiveOldSupply(uint256 msgTime);
                  error GCNotPendleMarket(address caller);
                  error GCNotVotingController(address caller);
                  error InvalidWTime(uint256 wTime);
                  error ExpiryInThePast(uint256 expiry);
                  error ChainNotSupported(uint256 chainId);
                  error FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount);
                  error FDEpochLengthMismatch();
                  error FDInvalidPool(address pool);
                  error FDPoolAlreadyExists(address pool);
                  error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch);
                  error FDInvalidStartEpoch(uint256 startEpoch);
                  error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime);
                  error FDFutureFunding(uint256 lastFunded, uint256 currentWTime);
                  error BDInvalidEpoch(uint256 epoch, uint256 startTime);
                  // Cross-Chain
                  error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path);
                  error MsgNotFromReceiveEndpoint(address sender);
                  error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee);
                  error ApproxDstExecutionGasNotSet();
                  error InvalidRetryData();
                  // GENERIC MSG
                  error ArrayLengthMismatch();
                  error ArrayEmpty();
                  error ArrayOutOfBounds();
                  error ZeroAddress();
                  error FailedToSendEther();
                  error InvalidMerkleProof();
                  error OnlyLayerZeroEndpoint();
                  error OnlyYT();
                  error OnlyYCFactory();
                  error OnlyWhitelisted();
                  // Swap Aggregator
                  error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual);
                  error UnsupportedSelector(uint256 aggregatorType, bytes4 selector);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              // This program is free software: you can redistribute it and/or modify
              // it under the terms of the GNU General Public License as published by
              // the Free Software Foundation, either version 3 of the License, or
              // (at your option) any later version.
              // This program is distributed in the hope that it will be useful,
              // but WITHOUT ANY WARRANTY; without even the implied warranty of
              // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
              // GNU General Public License for more details.
              // You should have received a copy of the GNU General Public License
              // along with this program.  If not, see <http://www.gnu.org/licenses/>.
              pragma solidity ^0.8.0;
              /* solhint-disable private-vars-leading-underscore, reason-string */
              library PMath {
                  uint256 internal constant ONE = 1e18; // 18 decimal places
                  int256 internal constant IONE = 1e18; // 18 decimal places
                  function subMax0(uint256 a, uint256 b) internal pure returns (uint256) {
                      unchecked {
                          return (a >= b ? a - b : 0);
                      }
                  }
                  function subNoNeg(int256 a, int256 b) internal pure returns (int256) {
                      require(a >= b, "negative");
                      return a - b; // no unchecked since if b is very negative, a - b might overflow
                  }
                  function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 product = a * b;
                      unchecked {
                          return product / ONE;
                      }
                  }
                  function mulDown(int256 a, int256 b) internal pure returns (int256) {
                      int256 product = a * b;
                      unchecked {
                          return product / IONE;
                      }
                  }
                  function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 aInflated = a * ONE;
                      unchecked {
                          return aInflated / b;
                      }
                  }
                  function divDown(int256 a, int256 b) internal pure returns (int256) {
                      int256 aInflated = a * IONE;
                      unchecked {
                          return aInflated / b;
                      }
                  }
                  function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
                      return (a + b - 1) / b;
                  }
                  function rawDivUp(int256 a, int256 b) internal pure returns (int256) {
                      return (a + b - 1) / b;
                  }
                  function slipUp(uint256 a, uint256 factor) internal pure returns (uint256) {
                      return mulDown(a, ONE + factor);
                  }
                  function slipDown(uint256 a, uint256 factor) internal pure returns (uint256) {
                      return mulDown(a, ONE - factor);
                  }
                  // @author Uniswap
                  function sqrt(uint256 y) internal pure returns (uint256 z) {
                      if (y > 3) {
                          z = y;
                          uint256 x = y / 2 + 1;
                          while (x < z) {
                              z = x;
                              x = (y / x + x) / 2;
                          }
                      } else if (y != 0) {
                          z = 1;
                      }
                  }
                  function square(uint256 x) internal pure returns (uint256) {
                      return x * x;
                  }
                  function squareDown(uint256 x) internal pure returns (uint256) {
                      return mulDown(x, x);
                  }
                  function abs(int256 x) internal pure returns (uint256) {
                      return uint256(x > 0 ? x : -x);
                  }
                  function neg(int256 x) internal pure returns (int256) {
                      return x * (-1);
                  }
                  function neg(uint256 x) internal pure returns (int256) {
                      return Int(x) * (-1);
                  }
                  function max(uint256 x, uint256 y) internal pure returns (uint256) {
                      return (x > y ? x : y);
                  }
                  function max(int256 x, int256 y) internal pure returns (int256) {
                      return (x > y ? x : y);
                  }
                  function min(uint256 x, uint256 y) internal pure returns (uint256) {
                      return (x < y ? x : y);
                  }
                  function min(int256 x, int256 y) internal pure returns (int256) {
                      return (x < y ? x : y);
                  }
                  /*///////////////////////////////////////////////////////////////
                                             SIGNED CASTS
                  //////////////////////////////////////////////////////////////*/
                  function Int(uint256 x) internal pure returns (int256) {
                      require(x <= uint256(type(int256).max));
                      return int256(x);
                  }
                  function Int128(int256 x) internal pure returns (int128) {
                      require(type(int128).min <= x && x <= type(int128).max);
                      return int128(x);
                  }
                  function Int128(uint256 x) internal pure returns (int128) {
                      return Int128(Int(x));
                  }
                  /*///////////////////////////////////////////////////////////////
                                             UNSIGNED CASTS
                  //////////////////////////////////////////////////////////////*/
                  function Uint(int256 x) internal pure returns (uint256) {
                      require(x >= 0);
                      return uint256(x);
                  }
                  function Uint32(uint256 x) internal pure returns (uint32) {
                      require(x <= type(uint32).max);
                      return uint32(x);
                  }
                  function Uint64(uint256 x) internal pure returns (uint64) {
                      require(x <= type(uint64).max);
                      return uint64(x);
                  }
                  function Uint112(uint256 x) internal pure returns (uint112) {
                      require(x <= type(uint112).max);
                      return uint112(x);
                  }
                  function Uint96(uint256 x) internal pure returns (uint96) {
                      require(x <= type(uint96).max);
                      return uint96(x);
                  }
                  function Uint128(uint256 x) internal pure returns (uint128) {
                      require(x <= type(uint128).max);
                      return uint128(x);
                  }
                  function Uint192(uint256 x) internal pure returns (uint192) {
                      require(x <= type(uint192).max);
                      return uint192(x);
                  }
                  function isAApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps);
                  }
                  function isAGreaterApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return a >= b && a <= mulDown(b, ONE + eps);
                  }
                  function isASmallerApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return a <= b && a >= mulDown(b, ONE - eps);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
              import "../../interfaces/IWETH.sol";
              abstract contract TokenHelper {
                  using SafeERC20 for IERC20;
                  address internal constant NATIVE = address(0);
                  uint256 internal constant LOWER_BOUND_APPROVAL = type(uint96).max / 2; // some tokens use 96 bits for approval
                  function _transferIn(address token, address from, uint256 amount) internal {
                      if (token == NATIVE) require(msg.value == amount, "eth mismatch");
                      else if (amount != 0) IERC20(token).safeTransferFrom(from, address(this), amount);
                  }
                  function _transferFrom(IERC20 token, address from, address to, uint256 amount) internal {
                      if (amount != 0) token.safeTransferFrom(from, to, amount);
                  }
                  function _transferOut(address token, address to, uint256 amount) internal {
                      if (amount == 0) return;
                      if (token == NATIVE) {
                          (bool success, ) = to.call{value: amount}("");
                          require(success, "eth send failed");
                      } else {
                          IERC20(token).safeTransfer(to, amount);
                      }
                  }
                  function _transferOut(address[] memory tokens, address to, uint256[] memory amounts) internal {
                      uint256 numTokens = tokens.length;
                      require(numTokens == amounts.length, "length mismatch");
                      for (uint256 i = 0; i < numTokens; ) {
                          _transferOut(tokens[i], to, amounts[i]);
                          unchecked {
                              i++;
                          }
                      }
                  }
                  function _selfBalance(address token) internal view returns (uint256) {
                      return (token == NATIVE) ? address(this).balance : IERC20(token).balanceOf(address(this));
                  }
                  function _selfBalance(IERC20 token) internal view returns (uint256) {
                      return token.balanceOf(address(this));
                  }
                  /// @notice Approves the stipulated contract to spend the given allowance in the given token
                  /// @dev PLS PAY ATTENTION to tokens that requires the approval to be set to 0 before changing it
                  function _safeApprove(address token, address to, uint256 value) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), "Safe Approve");
                  }
                  function _safeApproveInf(address token, address to) internal {
                      if (token == NATIVE) return;
                      if (IERC20(token).allowance(address(this), to) < LOWER_BOUND_APPROVAL) {
                          _safeApprove(token, to, 0);
                          _safeApprove(token, to, type(uint256).max);
                      }
                  }
                  function _wrap_unwrap_ETH(address tokenIn, address tokenOut, uint256 netTokenIn) internal {
                      if (tokenIn == NATIVE) IWETH(tokenOut).deposit{value: netTokenIn}();
                      else IWETH(tokenIn).withdraw(netTokenIn);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.17;
              import "../../SYBase.sol";
              import "../../../../interfaces/Usual/IUsualUSD0PP.sol";
              contract PendleUsualUSD0PPSY is SYBase {
                  address public constant USD0PP = 0x35D8949372D46B7a3D5A56006AE77B215fc69bC0;
                  address public constant USD0 = 0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5;
                  constructor() SYBase("SY USD0++", "SY-USD0++", USD0PP) {
                      _safeApproveInf(USD0, USD0PP);
                  }
                  function _deposit(
                      address tokenIn,
                      uint256 amountDeposited
                  ) internal virtual override returns (uint256 /*amountSharesOut*/) {
                      if (tokenIn == USD0) {
                          IUsualUSD0PP(USD0PP).mint(amountDeposited);
                      }
                      return amountDeposited;
                  }
                  function _redeem(
                      address receiver,
                      address /*tokenOut*/,
                      uint256 amountSharesToRedeem
                  ) internal override returns (uint256) {
                      _transferOut(yieldToken, receiver, amountSharesToRedeem);
                      return amountSharesToRedeem;
                  }
                  function exchangeRate() public view virtual override returns (uint256 res) {
                      return PMath.ONE;
                  }
                  function _previewDeposit(
                      address /*tokenIn*/,
                      uint256 amountTokenToDeposit
                  ) internal view virtual override returns (uint256 /*amountSharesOut*/) {
                      return amountTokenToDeposit;
                  }
                  function _previewRedeem(
                      address /*tokenOut*/,
                      uint256 amountSharesToRedeem
                  ) internal view virtual override returns (uint256 /*amountTokenOut*/) {
                      return amountSharesToRedeem;
                  }
                  function getTokensIn() public pure override returns (address[] memory res) {
                      return ArrayLib.create(USD0PP, USD0);
                  }
                  function getTokensOut() public pure override returns (address[] memory res) {
                      return ArrayLib.create(USD0PP);
                  }
                  function isValidTokenIn(address token) public pure override returns (bool) {
                      return token == USD0PP || token == USD0;
                  }
                  function isValidTokenOut(address token) public pure override returns (bool) {
                      return token == USD0PP;
                  }
                  function assetInfo() external view returns (AssetType assetType, address assetAddress, uint8 assetDecimals) {
                      return (AssetType.TOKEN, USD0PP, IERC20Metadata(USD0PP).decimals());
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "../../interfaces/IStandardizedYield.sol";
              import "../erc20/PendleERC20Permit.sol";
              import "../libraries/math/PMath.sol";
              import "../libraries/TokenHelper.sol";
              import "../libraries/ArrayLib.sol";
              import "../libraries/Errors.sol";
              import "../libraries/BoringOwnableUpgradeable.sol";
              import "@openzeppelin/contracts/security/Pausable.sol";
              abstract contract SYBase is IStandardizedYield, PendleERC20Permit, TokenHelper, BoringOwnableUpgradeable, Pausable {
                  using PMath for uint256;
                  address public immutable yieldToken;
                  constructor(
                      string memory _name,
                      string memory _symbol,
                      address _yieldToken
                  ) PendleERC20Permit(_name, _symbol, IERC20Metadata(_yieldToken).decimals()) initializer {
                      yieldToken = _yieldToken;
                      __BoringOwnable_init();
                  }
                  // solhint-disable no-empty-blocks
                  receive() external payable {}
                  /*///////////////////////////////////////////////////////////////
                                  DEPOSIT/REDEEM USING BASE TOKENS
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @dev See {IStandardizedYield-deposit}
                   */
                  function deposit(
                      address receiver,
                      address tokenIn,
                      uint256 amountTokenToDeposit,
                      uint256 minSharesOut
                  ) external payable nonReentrant returns (uint256 amountSharesOut) {
                      if (!isValidTokenIn(tokenIn)) revert Errors.SYInvalidTokenIn(tokenIn);
                      if (amountTokenToDeposit == 0) revert Errors.SYZeroDeposit();
                      _transferIn(tokenIn, msg.sender, amountTokenToDeposit);
                      amountSharesOut = _deposit(tokenIn, amountTokenToDeposit);
                      if (amountSharesOut < minSharesOut) revert Errors.SYInsufficientSharesOut(amountSharesOut, minSharesOut);
                      _mint(receiver, amountSharesOut);
                      emit Deposit(msg.sender, receiver, tokenIn, amountTokenToDeposit, amountSharesOut);
                  }
                  /**
                   * @dev See {IStandardizedYield-redeem}
                   */
                  function redeem(
                      address receiver,
                      uint256 amountSharesToRedeem,
                      address tokenOut,
                      uint256 minTokenOut,
                      bool burnFromInternalBalance
                  ) external nonReentrant returns (uint256 amountTokenOut) {
                      if (!isValidTokenOut(tokenOut)) revert Errors.SYInvalidTokenOut(tokenOut);
                      if (amountSharesToRedeem == 0) revert Errors.SYZeroRedeem();
                      if (burnFromInternalBalance) {
                          _burn(address(this), amountSharesToRedeem);
                      } else {
                          _burn(msg.sender, amountSharesToRedeem);
                      }
                      amountTokenOut = _redeem(receiver, tokenOut, amountSharesToRedeem);
                      if (amountTokenOut < minTokenOut) revert Errors.SYInsufficientTokenOut(amountTokenOut, minTokenOut);
                      emit Redeem(msg.sender, receiver, tokenOut, amountSharesToRedeem, amountTokenOut);
                  }
                  /**
                   * @notice mint shares based on the deposited base tokens
                   * @param tokenIn base token address used to mint shares
                   * @param amountDeposited amount of base tokens deposited
                   * @return amountSharesOut amount of shares minted
                   */
                  function _deposit(address tokenIn, uint256 amountDeposited) internal virtual returns (uint256 amountSharesOut);
                  /**
                   * @notice redeems base tokens based on amount of shares to be burned
                   * @param tokenOut address of the base token to be redeemed
                   * @param amountSharesToRedeem amount of shares to be burned
                   * @return amountTokenOut amount of base tokens redeemed
                   */
                  function _redeem(
                      address receiver,
                      address tokenOut,
                      uint256 amountSharesToRedeem
                  ) internal virtual returns (uint256 amountTokenOut);
                  /*///////////////////////////////////////////////////////////////
                                             EXCHANGE-RATE
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @dev See {IStandardizedYield-exchangeRate}
                   */
                  function exchangeRate() external view virtual override returns (uint256 res);
                  /*///////////////////////////////////////////////////////////////
                                             REWARDS-RELATED
                  //////////////////////////////////////////////////////////////*/
                  /**
                   * @dev See {IStandardizedYield-claimRewards}
                   */
                  function claimRewards(address /*user*/) external virtual override returns (uint256[] memory rewardAmounts) {
                      rewardAmounts = new uint256[](0);
                  }
                  /**
                   * @dev See {IStandardizedYield-getRewardTokens}
                   */
                  function getRewardTokens() external view virtual override returns (address[] memory rewardTokens) {
                      rewardTokens = new address[](0);
                  }
                  /**
                   * @dev See {IStandardizedYield-accruedRewards}
                   */
                  function accruedRewards(address /*user*/) external view virtual override returns (uint256[] memory rewardAmounts) {
                      rewardAmounts = new uint256[](0);
                  }
                  function rewardIndexesCurrent() external virtual override returns (uint256[] memory indexes) {
                      indexes = new uint256[](0);
                  }
                  function rewardIndexesStored() external view virtual override returns (uint256[] memory indexes) {
                      indexes = new uint256[](0);
                  }
                  /*///////////////////////////////////////////////////////////////
                              MISC METADATA FUNCTIONS
                  //////////////////////////////////////////////////////////////*/
                  function previewDeposit(
                      address tokenIn,
                      uint256 amountTokenToDeposit
                  ) external view virtual returns (uint256 amountSharesOut) {
                      if (!isValidTokenIn(tokenIn)) revert Errors.SYInvalidTokenIn(tokenIn);
                      return _previewDeposit(tokenIn, amountTokenToDeposit);
                  }
                  function previewRedeem(
                      address tokenOut,
                      uint256 amountSharesToRedeem
                  ) external view virtual returns (uint256 amountTokenOut) {
                      if (!isValidTokenOut(tokenOut)) revert Errors.SYInvalidTokenOut(tokenOut);
                      return _previewRedeem(tokenOut, amountSharesToRedeem);
                  }
                  function pause() external onlyOwner {
                      _pause();
                  }
                  function unpause() external onlyOwner {
                      _unpause();
                  }
                  function _beforeTokenTransfer(address, address, uint256) internal virtual override whenNotPaused {}
                  function _previewDeposit(
                      address tokenIn,
                      uint256 amountTokenToDeposit
                  ) internal view virtual returns (uint256 amountSharesOut);
                  function _previewRedeem(
                      address tokenOut,
                      uint256 amountSharesToRedeem
                  ) internal view virtual returns (uint256 amountTokenOut);
                  function getTokensIn() public view virtual returns (address[] memory res);
                  function getTokensOut() public view virtual returns (address[] memory res);
                  function isValidTokenIn(address token) public view virtual returns (bool);
                  function isValidTokenOut(address token) public view virtual returns (bool);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              /*
               * MIT License
               * ===========
               *
               * Permission is hereby granted, free of charge, to any person obtaining a copy
               * of this software and associated documentation files (the "Software"), to deal
               * in the Software without restriction, including without limitation the rights
               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
               * copies of the Software, and to permit persons to whom the Software is
               * furnished to do so, subject to the following conditions:
               *
               * The above copyright notice and this permission notice shall be included in all
               * copies or substantial portions of the Software.
               *
               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
               */
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              interface IStandardizedYield is IERC20Metadata {
                  /// @dev Emitted when any base tokens is deposited to mint shares
                  event Deposit(
                      address indexed caller,
                      address indexed receiver,
                      address indexed tokenIn,
                      uint256 amountDeposited,
                      uint256 amountSyOut
                  );
                  /// @dev Emitted when any shares are redeemed for base tokens
                  event Redeem(
                      address indexed caller,
                      address indexed receiver,
                      address indexed tokenOut,
                      uint256 amountSyToRedeem,
                      uint256 amountTokenOut
                  );
                  /// @dev check `assetInfo()` for more information
                  enum AssetType {
                      TOKEN,
                      LIQUIDITY
                  }
                  /// @dev Emitted when (`user`) claims their rewards
                  event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts);
                  /**
                   * @notice mints an amount of shares by depositing a base token.
                   * @param receiver shares recipient address
                   * @param tokenIn address of the base tokens to mint shares
                   * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`)
                   * @param minSharesOut reverts if amount of shares minted is lower than this
                   * @return amountSharesOut amount of shares minted
                   * @dev Emits a {Deposit} event
                   *
                   * Requirements:
                   * - (`tokenIn`) must be a valid base token.
                   */
                  function deposit(
                      address receiver,
                      address tokenIn,
                      uint256 amountTokenToDeposit,
                      uint256 minSharesOut
                  ) external payable returns (uint256 amountSharesOut);
                  /**
                   * @notice redeems an amount of base tokens by burning some shares
                   * @param receiver recipient address
                   * @param amountSharesToRedeem amount of shares to be burned
                   * @param tokenOut address of the base token to be redeemed
                   * @param minTokenOut reverts if amount of base token redeemed is lower than this
                   * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender`
                   * @return amountTokenOut amount of base tokens redeemed
                   * @dev Emits a {Redeem} event
                   *
                   * Requirements:
                   * - (`tokenOut`) must be a valid base token.
                   */
                  function redeem(
                      address receiver,
                      uint256 amountSharesToRedeem,
                      address tokenOut,
                      uint256 minTokenOut,
                      bool burnFromInternalBalance
                  ) external returns (uint256 amountTokenOut);
                  /**
                   * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account
                   * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy
                   he can mint must be X * exchangeRate / 1e18
                   * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication
                   & division
                   */
                  function exchangeRate() external view returns (uint256 res);
                  /**
                   * @notice claims reward for (`user`)
                   * @param user the user receiving their rewards
                   * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
                   * @dev
                   * Emits a `ClaimRewards` event
                   * See {getRewardTokens} for list of reward tokens
                   */
                  function claimRewards(address user) external returns (uint256[] memory rewardAmounts);
                  /**
                   * @notice get the amount of unclaimed rewards for (`user`)
                   * @param user the user to check for
                   * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
                   */
                  function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts);
                  function rewardIndexesCurrent() external returns (uint256[] memory indexes);
                  function rewardIndexesStored() external view returns (uint256[] memory indexes);
                  /**
                   * @notice returns the list of reward token addresses
                   */
                  function getRewardTokens() external view returns (address[] memory);
                  /**
                   * @notice returns the address of the underlying yield token
                   */
                  function yieldToken() external view returns (address);
                  /**
                   * @notice returns all tokens that can mint this SY
                   */
                  function getTokensIn() external view returns (address[] memory res);
                  /**
                   * @notice returns all tokens that can be redeemed by this SY
                   */
                  function getTokensOut() external view returns (address[] memory res);
                  function isValidTokenIn(address token) external view returns (bool);
                  function isValidTokenOut(address token) external view returns (bool);
                  function previewDeposit(
                      address tokenIn,
                      uint256 amountTokenToDeposit
                  ) external view returns (uint256 amountSharesOut);
                  function previewRedeem(
                      address tokenOut,
                      uint256 amountSharesToRedeem
                  ) external view returns (uint256 amountTokenOut);
                  /**
                   * @notice This function contains information to interpret what the asset is
                   * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens,
                      2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the chain)
                   * @return assetAddress the address of the asset
                   * @return assetDecimals the decimals of the asset
                   */
                  function assetInfo() external view returns (AssetType assetType, address assetAddress, uint8 assetDecimals);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              /*
               * MIT License
               * ===========
               *
               * Permission is hereby granted, free of charge, to any person obtaining a copy
               * of this software and associated documentation files (the "Software"), to deal
               * in the Software without restriction, including without limitation the rights
               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
               * copies of the Software, and to permit persons to whom the Software is
               * furnished to do so, subject to the following conditions:
               *
               * The above copyright notice and this permission notice shall be included in all
               * copies or substantial portions of the Software.
               *
               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
               */
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              interface IWETH is IERC20 {
                  event Deposit(address indexed dst, uint256 wad);
                  event Withdrawal(address indexed src, uint256 wad);
                  function deposit() external payable;
                  function withdraw(uint256 wad) external;
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.17;
              interface IUsualUSD0PP {
                  function mint(uint256 amountUsd0) external;
              }
              

              File 17 of 30: PendleYieldToken
              // 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.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) (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.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) (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: GPL-3.0-or-later
              // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "@openzeppelin/contracts/utils/Context.sol";
              /**
               * @dev Pendle's ERC20 implementation, modified from @openzeppelin implementation
               * Changes are:
               * - comes with built-in reentrancy protection, storage-packed with totalSupply variable
               * - delete increaseAllowance / decreaseAllowance
               * - add nonReentrancy protection to transfer / transferFrom functions
               * - allow decimals to be passed in
               * - block self-transfer by default
               */
              // solhint-disable
              contract PendleERC20 is Context, IERC20, IERC20Metadata {
                  uint8 private constant _NOT_ENTERED = 1;
                  uint8 private constant _ENTERED = 2;
                  mapping(address => uint256) private _balances;
                  mapping(address => mapping(address => uint256)) private _allowances;
                  uint248 private _totalSupply;
                  uint8 private _status;
                  string private _name;
                  string private _symbol;
                  uint8 public immutable decimals;
                  /**
                   * @dev Prevents a contract from calling itself, directly or indirectly.
                   * Calling a `nonReentrant` function from another `nonReentrant`
                   * function is not supported. It is possible to prevent this from happening
                   * by making the `nonReentrant` function external, and making it call a
                   * `private` function that does the actual work.
                   */
                  modifier nonReentrant() {
                      // On the first call to nonReentrant, _notEntered will be true
                      require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
                      // Any calls to nonReentrant after this point will fail
                      _status = _ENTERED;
                      _;
                      // By storing the original value once again, a refund is triggered (see
                      // https://eips.ethereum.org/EIPS/eip-2200)
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Sets the values for {name}, {symbol} and {decimals}.
                   *
                   * All three of these values are immutable: they can only be set once during
                   * construction.
                   */
                  constructor(string memory name_, string memory symbol_, uint8 decimals_) {
                      _name = name_;
                      _symbol = symbol_;
                      decimals = decimals_;
                      _status = _NOT_ENTERED;
                  }
                  /**
                   * @dev Returns the name of the token.
                   */
                  function name() public view virtual override returns (string memory) {
                      return _name;
                  }
                  /**
                   * @dev Returns the symbol of the token, usually a shorter version of the
                   * name.
                   */
                  function symbol() public view virtual override returns (string memory) {
                      return _symbol;
                  }
                  /**
                   * @dev See {IERC20-totalSupply}.
                   */
                  function totalSupply() public view virtual override returns (uint256) {
                      return _totalSupply;
                  }
                  /**
                   * @dev See {IERC20-balanceOf}.
                   */
                  function balanceOf(address account) public view virtual override returns (uint256) {
                      return _balances[account];
                  }
                  /**
                   * @dev See {IERC20-transfer}.
                   *
                   * Requirements:
                   *
                   * - `to` cannot be the zero address.
                   * - the caller must have a balance of at least `amount`.
                   */
                  function transfer(address to, uint256 amount) external virtual override nonReentrant returns (bool) {
                      address owner = _msgSender();
                      _transfer(owner, to, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-allowance}.
                   */
                  function allowance(address owner, address spender) public view virtual override returns (uint256) {
                      return _allowances[owner][spender];
                  }
                  /**
                   * @dev See {IERC20-approve}.
                   *
                   * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
                   * `transferFrom`. This is semantically equivalent to an infinite approval.
                   *
                   * Requirements:
                   *
                   * - `spender` cannot be the zero address.
                   */
                  function approve(address spender, uint256 amount) external virtual override returns (bool) {
                      address owner = _msgSender();
                      _approve(owner, spender, amount);
                      return true;
                  }
                  /**
                   * @dev See {IERC20-transferFrom}.
                   *
                   * Emits an {Approval} event indicating the updated allowance. This is not
                   * required by the EIP. See the note at the beginning of {ERC20}.
                   *
                   * NOTE: Does not update the allowance if the current allowance
                   * is the maximum `uint256`.
                   *
                   * Requirements:
                   *
                   * - `from` and `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   * - the caller must have allowance for ``from``'s tokens of at least
                   * `amount`.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) external virtual override nonReentrant returns (bool) {
                      address spender = _msgSender();
                      _spendAllowance(from, spender, amount);
                      _transfer(from, to, amount);
                      return true;
                  }
                  /**
                   * @dev Moves `amount` of tokens from `sender` to `recipient`.
                   *
                   * This internal function is equivalent to {transfer}, and can be used to
                   * e.g. implement automatic token fees, slashing mechanisms, etc.
                   *
                   * Emits a {Transfer} event.
                   *
                   * Requirements:
                   *
                   * - `from` cannot be the zero address.
                   * - `to` cannot be the zero address.
                   * - `from` must have a balance of at least `amount`.
                   */
                  function _transfer(address from, address to, uint256 amount) internal virtual {
                      require(from != address(0), "ERC20: transfer from the zero address");
                      require(to != address(0), "ERC20: transfer to the zero address");
                      require(from != to, "ERC20: transfer to self");
                      _beforeTokenTransfer(from, to, amount);
                      uint256 fromBalance = _balances[from];
                      require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
                      unchecked {
                          _balances[from] = fromBalance - amount;
                      }
                      _balances[to] += amount;
                      emit Transfer(from, to, amount);
                      _afterTokenTransfer(from, to, amount);
                  }
                  /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                   * the total supply.
                   *
                   * Emits a {Transfer} event with `from` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   */
                  function _mint(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: mint to the zero address");
                      _beforeTokenTransfer(address(0), account, amount);
                      _totalSupply += toUint248(amount);
                      _balances[account] += amount;
                      emit Transfer(address(0), account, amount);
                      _afterTokenTransfer(address(0), account, amount);
                  }
                  /**
                   * @dev Destroys `amount` tokens from `account`, reducing the
                   * total supply.
                   *
                   * Emits a {Transfer} event with `to` set to the zero address.
                   *
                   * Requirements:
                   *
                   * - `account` cannot be the zero address.
                   * - `account` must have at least `amount` tokens.
                   */
                  function _burn(address account, uint256 amount) internal virtual {
                      require(account != address(0), "ERC20: burn from the zero address");
                      _beforeTokenTransfer(account, address(0), amount);
                      uint256 accountBalance = _balances[account];
                      require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
                      unchecked {
                          _balances[account] = accountBalance - amount;
                      }
                      _totalSupply -= toUint248(amount);
                      emit Transfer(account, address(0), amount);
                      _afterTokenTransfer(account, address(0), amount);
                  }
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
                   *
                   * This internal function is equivalent to `approve`, and can be used to
                   * e.g. set automatic allowances for certain subsystems, etc.
                   *
                   * Emits an {Approval} event.
                   *
                   * Requirements:
                   *
                   * - `owner` cannot be the zero address.
                   * - `spender` cannot be the zero address.
                   */
                  function _approve(address owner, address spender, uint256 amount) internal virtual {
                      require(owner != address(0), "ERC20: approve from the zero address");
                      require(spender != address(0), "ERC20: approve to the zero address");
                      _allowances[owner][spender] = amount;
                      emit Approval(owner, spender, amount);
                  }
                  /**
                   * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
                   *
                   * Does not update the allowance amount in case of infinite allowance.
                   * Revert if not enough allowance is available.
                   *
                   * Might emit an {Approval} event.
                   */
                  function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
                      uint256 currentAllowance = allowance(owner, spender);
                      if (currentAllowance != type(uint256).max) {
                          require(currentAllowance >= amount, "ERC20: insufficient allowance");
                          unchecked {
                              _approve(owner, spender, currentAllowance - amount);
                          }
                      }
                  }
                  /**
                   * @dev Hook that is called before any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * will be transferred to `to`.
                   * - when `from` is zero, `amount` tokens will be minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  /**
                   * @dev Hook that is called after any transfer of tokens. This includes
                   * minting and burning.
                   *
                   * Calling conditions:
                   *
                   * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                   * has been transferred to `to`.
                   * - when `from` is zero, `amount` tokens have been minted for `to`.
                   * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
                   * - `from` and `to` are never both zero.
                   *
                   * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                   */
                  function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
                  function toUint248(uint256 x) internal virtual returns (uint248) {
                      require(x <= type(uint248).max); // signed, lim = bit-1
                      return uint248(x);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library ArrayLib {
                  function sum(uint256[] memory input) internal pure returns (uint256) {
                      uint256 value = 0;
                      for (uint256 i = 0; i < input.length; ) {
                          value += input[i];
                          unchecked {
                              i++;
                          }
                      }
                      return value;
                  }
                  /// @notice return index of the element if found, else return uint256.max
                  function find(address[] memory array, address element) internal pure returns (uint256 index) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return i;
                          unchecked {
                              i++;
                          }
                      }
                      return type(uint256).max;
                  }
                  function append(address[] memory inp, address element) internal pure returns (address[] memory out) {
                      uint256 length = inp.length;
                      out = new address[](length + 1);
                      for (uint256 i = 0; i < length; ) {
                          out[i] = inp[i];
                          unchecked {
                              i++;
                          }
                      }
                      out[length] = element;
                  }
                  function appendHead(address[] memory inp, address element) internal pure returns (address[] memory out) {
                      uint256 length = inp.length;
                      out = new address[](length + 1);
                      out[0] = element;
                      for (uint256 i = 1; i <= length; ) {
                          out[i] = inp[i - 1];
                          unchecked {
                              i++;
                          }
                      }
                  }
                  /**
                   * @dev This function assumes a and b each contains unidentical elements
                   * @param a array of addresses a
                   * @param b array of addresses b
                   * @return out Concatenation of a and b containing unidentical elements
                   */
                  function merge(address[] memory a, address[] memory b) internal pure returns (address[] memory out) {
                      unchecked {
                          uint256 countUnidenticalB = 0;
                          bool[] memory isUnidentical = new bool[](b.length);
                          for (uint256 i = 0; i < b.length; ++i) {
                              if (!contains(a, b[i])) {
                                  countUnidenticalB++;
                                  isUnidentical[i] = true;
                              }
                          }
                          out = new address[](a.length + countUnidenticalB);
                          for (uint256 i = 0; i < a.length; ++i) {
                              out[i] = a[i];
                          }
                          uint256 id = a.length;
                          for (uint256 i = 0; i < b.length; ++i) {
                              if (isUnidentical[i]) {
                                  out[id++] = b[i];
                              }
                          }
                      }
                  }
                  // various version of contains
                  function contains(address[] memory array, address element) internal pure returns (bool) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return true;
                          unchecked {
                              i++;
                          }
                      }
                      return false;
                  }
                  function contains(bytes4[] memory array, bytes4 element) internal pure returns (bool) {
                      uint256 length = array.length;
                      for (uint256 i = 0; i < length; ) {
                          if (array[i] == element) return true;
                          unchecked {
                              i++;
                          }
                      }
                      return false;
                  }
                  function create(address a) internal pure returns (address[] memory res) {
                      res = new address[](1);
                      res[0] = a;
                  }
                  function create(address a, address b) internal pure returns (address[] memory res) {
                      res = new address[](2);
                      res[0] = a;
                      res[1] = b;
                  }
                  function create(address a, address b, address c) internal pure returns (address[] memory res) {
                      res = new address[](3);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                  }
                  function create(address a, address b, address c, address d) internal pure returns (address[] memory res) {
                      res = new address[](4);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                      res[3] = d;
                  }
                  function create(
                      address a,
                      address b,
                      address c,
                      address d,
                      address e
                  ) internal pure returns (address[] memory res) {
                      res = new address[](5);
                      res[0] = a;
                      res[1] = b;
                      res[2] = c;
                      res[3] = d;
                      res[4] = e;
                  }
                  function create(uint256 a) internal pure returns (uint256[] memory res) {
                      res = new uint256[](1);
                      res[0] = a;
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library Errors {
                  // BulkSeller
                  error BulkInsufficientSyForTrade(uint256 currentAmount, uint256 requiredAmount);
                  error BulkInsufficientTokenForTrade(uint256 currentAmount, uint256 requiredAmount);
                  error BulkInSufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
                  error BulkInSufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  error BulkInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
                  error BulkNotMaintainer();
                  error BulkNotAdmin();
                  error BulkSellerAlreadyExisted(address token, address SY, address bulk);
                  error BulkSellerInvalidToken(address token, address SY);
                  error BulkBadRateTokenToSy(uint256 actualRate, uint256 currentRate, uint256 eps);
                  error BulkBadRateSyToToken(uint256 actualRate, uint256 currentRate, uint256 eps);
                  // APPROX
                  error ApproxFail();
                  error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps);
                  error ApproxBinarySearchInputInvalid(
                      uint256 approxGuessMin,
                      uint256 approxGuessMax,
                      uint256 minGuessMin,
                      uint256 maxGuessMax
                  );
                  // MARKET + MARKET MATH CORE
                  error MarketExpired();
                  error MarketZeroAmountsInput();
                  error MarketZeroAmountsOutput();
                  error MarketZeroLnImpliedRate();
                  error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount);
                  error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance);
                  error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
                  error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset);
                  error MarketExchangeRateBelowOne(int256 exchangeRate);
                  error MarketProportionMustNotEqualOne();
                  error MarketRateScalarBelowZero(int256 rateScalar);
                  error MarketScalarRootBelowZero(int256 scalarRoot);
                  error MarketProportionTooHigh(int256 proportion, int256 maxProportion);
                  error OracleUninitialized();
                  error OracleTargetTooOld(uint32 target, uint32 oldest);
                  error OracleZeroCardinality();
                  error MarketFactoryExpiredPt();
                  error MarketFactoryInvalidPt();
                  error MarketFactoryMarketExists();
                  error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot);
                  error MarketFactoryOverriddenFeeTooHigh(uint80 overriddenFee, uint256 marketLnFeeRateRoot);
                  error MarketFactoryReserveFeePercentTooHigh(uint8 reserveFeePercent, uint8 maxReserveFeePercent);
                  error MarketFactoryZeroTreasury();
                  error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor);
                  error MFNotPendleMarket(address addr);
                  // ROUTER
                  error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut);
                  error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
                  error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut);
                  error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut);
                  error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut);
                  error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay);
                  error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay);
                  error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed);
                  error RouterTimeRangeZero();
                  error RouterCallbackNotPendleMarket(address caller);
                  error RouterInvalidAction(bytes4 selector);
                  error RouterInvalidFacet(address facet);
                  error RouterKyberSwapDataZero();
                  error SimulationResults(bool success, bytes res);
                  // YIELD CONTRACT
                  error YCExpired();
                  error YCNotExpired();
                  error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy);
                  error YCNothingToRedeem();
                  error YCPostExpiryDataNotSet();
                  error YCNoFloatingSy();
                  // YieldFactory
                  error YCFactoryInvalidExpiry();
                  error YCFactoryYieldContractExisted();
                  error YCFactoryZeroExpiryDivisor();
                  error YCFactoryZeroTreasury();
                  error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate);
                  error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate);
                  // SY
                  error SYInvalidTokenIn(address token);
                  error SYInvalidTokenOut(address token);
                  error SYZeroDeposit();
                  error SYZeroRedeem();
                  error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut);
                  error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
                  // SY-specific
                  error SYQiTokenMintFailed(uint256 errCode);
                  error SYQiTokenRedeemFailed(uint256 errCode);
                  error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1);
                  error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax);
                  error SYCurveInvalidPid();
                  error SYCurve3crvPoolNotFound();
                  error SYApeDepositAmountTooSmall(uint256 amountDeposited);
                  error SYBalancerInvalidPid();
                  error SYInvalidRewardToken(address token);
                  error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable);
                  error SYBalancerReentrancy();
                  error NotFromTrustedRemote(uint16 srcChainId, bytes path);
                  error ApxETHNotEnoughBuffer();
                  // Liquidity Mining
                  error VCInactivePool(address pool);
                  error VCPoolAlreadyActive(address pool);
                  error VCZeroVePendle(address user);
                  error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight);
                  error VCEpochNotFinalized(uint256 wTime);
                  error VCPoolAlreadyAddAndRemoved(address pool);
                  error VEInvalidNewExpiry(uint256 newExpiry);
                  error VEExceededMaxLockTime();
                  error VEInsufficientLockTime();
                  error VENotAllowedReduceExpiry();
                  error VEZeroAmountLocked();
                  error VEPositionNotExpired();
                  error VEZeroPosition();
                  error VEZeroSlope(uint128 bias, uint128 slope);
                  error VEReceiveOldSupply(uint256 msgTime);
                  error GCNotPendleMarket(address caller);
                  error GCNotVotingController(address caller);
                  error InvalidWTime(uint256 wTime);
                  error ExpiryInThePast(uint256 expiry);
                  error ChainNotSupported(uint256 chainId);
                  error FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount);
                  error FDEpochLengthMismatch();
                  error FDInvalidPool(address pool);
                  error FDPoolAlreadyExists(address pool);
                  error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch);
                  error FDInvalidStartEpoch(uint256 startEpoch);
                  error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime);
                  error FDFutureFunding(uint256 lastFunded, uint256 currentWTime);
                  error BDInvalidEpoch(uint256 epoch, uint256 startTime);
                  // Cross-Chain
                  error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path);
                  error MsgNotFromReceiveEndpoint(address sender);
                  error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee);
                  error ApproxDstExecutionGasNotSet();
                  error InvalidRetryData();
                  // GENERIC MSG
                  error ArrayLengthMismatch();
                  error ArrayEmpty();
                  error ArrayOutOfBounds();
                  error ZeroAddress();
                  error FailedToSendEther();
                  error InvalidMerkleProof();
                  error OnlyLayerZeroEndpoint();
                  error OnlyYT();
                  error OnlyYCFactory();
                  error OnlyWhitelisted();
                  // Swap Aggregator
                  error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual);
                  error UnsupportedSelector(uint256 aggregatorType, bytes4 selector);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              // This program is free software: you can redistribute it and/or modify
              // it under the terms of the GNU General Public License as published by
              // the Free Software Foundation, either version 3 of the License, or
              // (at your option) any later version.
              // This program is distributed in the hope that it will be useful,
              // but WITHOUT ANY WARRANTY; without even the implied warranty of
              // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
              // GNU General Public License for more details.
              // You should have received a copy of the GNU General Public License
              // along with this program.  If not, see <http://www.gnu.org/licenses/>.
              pragma solidity ^0.8.0;
              /* solhint-disable private-vars-leading-underscore, reason-string */
              library PMath {
                  uint256 internal constant ONE = 1e18; // 18 decimal places
                  int256 internal constant IONE = 1e18; // 18 decimal places
                  function subMax0(uint256 a, uint256 b) internal pure returns (uint256) {
                      unchecked {
                          return (a >= b ? a - b : 0);
                      }
                  }
                  function subNoNeg(int256 a, int256 b) internal pure returns (int256) {
                      require(a >= b, "negative");
                      return a - b; // no unchecked since if b is very negative, a - b might overflow
                  }
                  function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 product = a * b;
                      unchecked {
                          return product / ONE;
                      }
                  }
                  function mulDown(int256 a, int256 b) internal pure returns (int256) {
                      int256 product = a * b;
                      unchecked {
                          return product / IONE;
                      }
                  }
                  function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 aInflated = a * ONE;
                      unchecked {
                          return aInflated / b;
                      }
                  }
                  function divDown(int256 a, int256 b) internal pure returns (int256) {
                      int256 aInflated = a * IONE;
                      unchecked {
                          return aInflated / b;
                      }
                  }
                  function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
                      return (a + b - 1) / b;
                  }
                  function rawDivUp(int256 a, int256 b) internal pure returns (int256) {
                      return (a + b - 1) / b;
                  }
                  function slipUp(uint256 a, uint256 factor) internal pure returns (uint256) {
                      return mulDown(a, ONE + factor);
                  }
                  function slipDown(uint256 a, uint256 factor) internal pure returns (uint256) {
                      return mulDown(a, ONE - factor);
                  }
                  // @author Uniswap
                  function sqrt(uint256 y) internal pure returns (uint256 z) {
                      if (y > 3) {
                          z = y;
                          uint256 x = y / 2 + 1;
                          while (x < z) {
                              z = x;
                              x = (y / x + x) / 2;
                          }
                      } else if (y != 0) {
                          z = 1;
                      }
                  }
                  function square(uint256 x) internal pure returns (uint256) {
                      return x * x;
                  }
                  function squareDown(uint256 x) internal pure returns (uint256) {
                      return mulDown(x, x);
                  }
                  function abs(int256 x) internal pure returns (uint256) {
                      return uint256(x > 0 ? x : -x);
                  }
                  function neg(int256 x) internal pure returns (int256) {
                      return x * (-1);
                  }
                  function neg(uint256 x) internal pure returns (int256) {
                      return Int(x) * (-1);
                  }
                  function max(uint256 x, uint256 y) internal pure returns (uint256) {
                      return (x > y ? x : y);
                  }
                  function max(int256 x, int256 y) internal pure returns (int256) {
                      return (x > y ? x : y);
                  }
                  function min(uint256 x, uint256 y) internal pure returns (uint256) {
                      return (x < y ? x : y);
                  }
                  function min(int256 x, int256 y) internal pure returns (int256) {
                      return (x < y ? x : y);
                  }
                  /*///////////////////////////////////////////////////////////////
                                             SIGNED CASTS
                  //////////////////////////////////////////////////////////////*/
                  function Int(uint256 x) internal pure returns (int256) {
                      require(x <= uint256(type(int256).max));
                      return int256(x);
                  }
                  function Int128(int256 x) internal pure returns (int128) {
                      require(type(int128).min <= x && x <= type(int128).max);
                      return int128(x);
                  }
                  function Int128(uint256 x) internal pure returns (int128) {
                      return Int128(Int(x));
                  }
                  /*///////////////////////////////////////////////////////////////
                                             UNSIGNED CASTS
                  //////////////////////////////////////////////////////////////*/
                  function Uint(int256 x) internal pure returns (uint256) {
                      require(x >= 0);
                      return uint256(x);
                  }
                  function Uint32(uint256 x) internal pure returns (uint32) {
                      require(x <= type(uint32).max);
                      return uint32(x);
                  }
                  function Uint64(uint256 x) internal pure returns (uint64) {
                      require(x <= type(uint64).max);
                      return uint64(x);
                  }
                  function Uint112(uint256 x) internal pure returns (uint112) {
                      require(x <= type(uint112).max);
                      return uint112(x);
                  }
                  function Uint96(uint256 x) internal pure returns (uint96) {
                      require(x <= type(uint96).max);
                      return uint96(x);
                  }
                  function Uint128(uint256 x) internal pure returns (uint128) {
                      require(x <= type(uint128).max);
                      return uint128(x);
                  }
                  function Uint192(uint256 x) internal pure returns (uint192) {
                      require(x <= type(uint192).max);
                      return uint192(x);
                  }
                  function isAApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps);
                  }
                  function isAGreaterApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return a >= b && a <= mulDown(b, ONE + eps);
                  }
                  function isASmallerApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
                      return a <= b && a >= mulDown(b, ONE - eps);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library MiniHelpers {
                  function isCurrentlyExpired(uint256 expiry) internal view returns (bool) {
                      return (expiry <= block.timestamp);
                  }
                  function isExpired(uint256 expiry, uint256 blockTime) internal pure returns (bool) {
                      return (expiry <= blockTime);
                  }
                  function isTimeInThePast(uint256 timestamp) internal view returns (bool) {
                      return (timestamp <= block.timestamp); // same definition as isCurrentlyExpired
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
              import "../../interfaces/IWETH.sol";
              abstract contract TokenHelper {
                  using SafeERC20 for IERC20;
                  address internal constant NATIVE = address(0);
                  uint256 internal constant LOWER_BOUND_APPROVAL = type(uint96).max / 2; // some tokens use 96 bits for approval
                  function _transferIn(address token, address from, uint256 amount) internal {
                      if (token == NATIVE) require(msg.value == amount, "eth mismatch");
                      else if (amount != 0) IERC20(token).safeTransferFrom(from, address(this), amount);
                  }
                  function _transferFrom(IERC20 token, address from, address to, uint256 amount) internal {
                      if (amount != 0) token.safeTransferFrom(from, to, amount);
                  }
                  function _transferOut(address token, address to, uint256 amount) internal {
                      if (amount == 0) return;
                      if (token == NATIVE) {
                          (bool success, ) = to.call{value: amount}("");
                          require(success, "eth send failed");
                      } else {
                          IERC20(token).safeTransfer(to, amount);
                      }
                  }
                  function _transferOut(address[] memory tokens, address to, uint256[] memory amounts) internal {
                      uint256 numTokens = tokens.length;
                      require(numTokens == amounts.length, "length mismatch");
                      for (uint256 i = 0; i < numTokens; ) {
                          _transferOut(tokens[i], to, amounts[i]);
                          unchecked {
                              i++;
                          }
                      }
                  }
                  function _selfBalance(address token) internal view returns (uint256) {
                      return (token == NATIVE) ? address(this).balance : IERC20(token).balanceOf(address(this));
                  }
                  function _selfBalance(IERC20 token) internal view returns (uint256) {
                      return token.balanceOf(address(this));
                  }
                  /// @notice Approves the stipulated contract to spend the given allowance in the given token
                  /// @dev PLS PAY ATTENTION to tokens that requires the approval to be set to 0 before changing it
                  function _safeApprove(address token, address to, uint256 value) internal {
                      (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
                      require(success && (data.length == 0 || abi.decode(data, (bool))), "Safe Approve");
                  }
                  function _safeApproveInf(address token, address to) internal {
                      if (token == NATIVE) return;
                      if (IERC20(token).allowance(address(this), to) < LOWER_BOUND_APPROVAL) {
                          _safeApprove(token, to, 0);
                          _safeApprove(token, to, type(uint256).max);
                      }
                  }
                  function _wrap_unwrap_ETH(address tokenIn, address tokenOut, uint256 netTokenIn) internal {
                      if (tokenIn == NATIVE) IWETH(tokenOut).deposit{value: netTokenIn}();
                      else IWETH(tokenIn).withdraw(netTokenIn);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "../../interfaces/IRewardManager.sol";
              import "../libraries/ArrayLib.sol";
              import "../libraries/TokenHelper.sol";
              import "../libraries/math/PMath.sol";
              import "./RewardManagerAbstract.sol";
              /// NOTE: RewardManager must not have duplicated rewardTokens
              abstract contract RewardManagerAbstract is IRewardManager, TokenHelper {
                  using PMath for uint256;
                  uint256 internal constant INITIAL_REWARD_INDEX = 1;
                  struct RewardState {
                      uint128 index;
                      uint128 lastBalance;
                  }
                  struct UserReward {
                      uint128 index;
                      uint128 accrued;
                  }
                  // [token] => [user] => (index,accrued)
                  mapping(address => mapping(address => UserReward)) public userReward;
                  function _updateAndDistributeRewards(address user) internal virtual {
                      _updateAndDistributeRewardsForTwo(user, address(0));
                  }
                  function _updateAndDistributeRewardsForTwo(address user1, address user2) internal virtual {
                      (address[] memory tokens, uint256[] memory indexes) = _updateRewardIndex();
                      if (tokens.length == 0) return;
                      if (user1 != address(0) && user1 != address(this)) _distributeRewardsPrivate(user1, tokens, indexes);
                      if (user2 != address(0) && user2 != address(this)) _distributeRewardsPrivate(user2, tokens, indexes);
                  }
                  // should only be callable from `_updateAndDistributeRewardsForTwo` to guarantee user != address(0) && user != address(this)
                  function _distributeRewardsPrivate(address user, address[] memory tokens, uint256[] memory indexes) private {
                      assert(user != address(0) && user != address(this));
                      uint256 userShares = _rewardSharesUser(user);
                      for (uint256 i = 0; i < tokens.length; ++i) {
                          address token = tokens[i];
                          uint256 index = indexes[i];
                          uint256 userIndex = userReward[token][user].index;
                          if (userIndex == 0) {
                              userIndex = INITIAL_REWARD_INDEX.Uint128();
                          }
                          if (userIndex == index || index == 0) continue;
                          uint256 deltaIndex = index - userIndex;
                          uint256 rewardDelta = userShares.mulDown(deltaIndex);
                          uint256 rewardAccrued = userReward[token][user].accrued + rewardDelta;
                          userReward[token][user] = UserReward({index: index.Uint128(), accrued: rewardAccrued.Uint128()});
                      }
                  }
                  function _updateRewardIndex() internal virtual returns (address[] memory tokens, uint256[] memory indexes);
                  function _redeemExternalReward() internal virtual;
                  function _doTransferOutRewards(
                      address user,
                      address receiver
                  ) internal virtual returns (uint256[] memory rewardAmounts);
                  function _rewardSharesUser(address user) internal view virtual returns (uint256);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              library SYUtils {
                  uint256 internal constant ONE = 1e18;
                  function syToAsset(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
                      return (syAmount * exchangeRate) / ONE;
                  }
                  function syToAssetUp(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
                      return (syAmount * exchangeRate + ONE - 1) / ONE;
                  }
                  function assetToSy(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
                      return (assetAmount * ONE) / exchangeRate;
                  }
                  function assetToSyUp(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
                      return (assetAmount * ONE + exchangeRate - 1) / exchangeRate;
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.17;
              import "../../interfaces/IPYieldToken.sol";
              import "../../interfaces/IPPrincipalToken.sol";
              import "../../interfaces/IPInterestManagerYT.sol";
              import "../../interfaces/IPYieldContractFactory.sol";
              import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
              import "../libraries/math/PMath.sol";
              import "../libraries/TokenHelper.sol";
              import "../StandardizedYield/SYUtils.sol";
              /*
              With YT yielding more SYs overtime, which is allowed to be redeemed by users, the reward distribution should
              be based on the amount of SYs that their YT currently represent, plus with their dueInterest.
              It has been proven and tested that totalSyRedeemable will not change over time, unless users redeem their interest or redeemPY.
              Due to this, it is required to update users' accruedReward STRICTLY BEFORE redeeming their interest.
              */
              abstract contract InterestManagerYT is TokenHelper, IPInterestManagerYT {
                  using PMath for uint256;
                  struct UserInterest {
                      uint128 index;
                      uint128 accrued;
                  }
                  mapping(address => UserInterest) public userInterest;
                  function _distributeInterest(address user) internal {
                      _distributeInterestForTwo(user, address(0));
                  }
                  function _distributeInterestForTwo(address user1, address user2) internal {
                      uint256 index = _getInterestIndex();
                      if (user1 != address(0) && user1 != address(this)) _distributeInterestPrivate(user1, index);
                      if (user2 != address(0) && user2 != address(this)) _distributeInterestPrivate(user2, index);
                  }
                  function _doTransferOutInterest(
                      address user,
                      address SY,
                      address factory
                  ) internal returns (uint256 interestAmount) {
                      address treasury = IPYieldContractFactory(factory).treasury();
                      uint256 feeRate = IPYieldContractFactory(factory).interestFeeRate();
                      uint256 interestPreFee = userInterest[user].accrued;
                      userInterest[user].accrued = 0;
                      uint256 feeAmount = interestPreFee.mulDown(feeRate);
                      interestAmount = interestPreFee - feeAmount;
                      _transferOut(SY, treasury, feeAmount);
                      _transferOut(SY, user, interestAmount);
                      emit CollectInterestFee(feeAmount);
                  }
                  // should only be callable from `_distributeInterestForTwo` & make sure user != address(0) && user != address(this)
                  function _distributeInterestPrivate(address user, uint256 currentIndex) private {
                      assert(user != address(0) && user != address(this));
                      uint256 prevIndex = userInterest[user].index;
                      if (prevIndex == currentIndex) return;
                      if (prevIndex == 0) {
                          userInterest[user].index = currentIndex.Uint128();
                          return;
                      }
                      uint256 principal = _YTbalance(user);
                      uint256 interestFromYT = (principal * (currentIndex - prevIndex)).divDown(prevIndex * currentIndex);
                      userInterest[user].accrued += interestFromYT.Uint128();
                      userInterest[user].index = currentIndex.Uint128();
                  }
                  function _getInterestIndex() internal virtual returns (uint256 index);
                  function _YTbalance(address user) internal view virtual returns (uint256);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.17;
              import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
              import "../../interfaces/IStandardizedYield.sol";
              import "../../interfaces/IPYieldToken.sol";
              import "../../interfaces/IPPrincipalToken.sol";
              import "../libraries/math/PMath.sol";
              import "../libraries/ArrayLib.sol";
              import "../../interfaces/IPYieldContractFactory.sol";
              import "../StandardizedYield/SYUtils.sol";
              import "../libraries/Errors.sol";
              import "../libraries/MiniHelpers.sol";
              import "../RewardManager/RewardManagerAbstract.sol";
              import "../erc20/PendleERC20.sol";
              import "./InterestManagerYT.sol";
              /**
              Invariance to maintain:
              - address(0) & address(this) should never have any rewards & activeBalance accounting done. This is
                  guaranteed by address(0) & address(this) check in each updateForTwo function
              */
              contract PendleYieldToken is IPYieldToken, PendleERC20, RewardManagerAbstract, InterestManagerYT {
                  using PMath for uint256;
                  using SafeERC20 for IERC20;
                  using ArrayLib for uint256[];
                  struct PostExpiryData {
                      uint128 firstPYIndex;
                      uint128 totalSyInterestForTreasury;
                      mapping(address => uint256) firstRewardIndex;
                      mapping(address => uint256) userRewardOwed;
                  }
                  address public immutable SY;
                  address public immutable PT;
                  address public immutable factory;
                  uint256 public immutable expiry;
                  bool public immutable doCacheIndexSameBlock;
                  uint256 public syReserve;
                  uint128 public pyIndexLastUpdatedBlock;
                  uint128 internal _pyIndexStored;
                  PostExpiryData public postExpiry;
                  modifier updateData() {
                      if (isExpired()) _setPostExpiryData();
                      _;
                      _updateSyReserve();
                  }
                  modifier notExpired() {
                      if (isExpired()) revert Errors.YCExpired();
                      _;
                  }
                  /**
                   * @param _doCacheIndexSameBlock if true, the PY index is cached for each block, and thus is
                   * constant for all txs within the same block. Otherwise, the PY index is recalculated for
                   * every tx.
                   */
                  constructor(
                      address _SY,
                      address _PT,
                      string memory _name,
                      string memory _symbol,
                      uint8 __decimals,
                      uint256 _expiry,
                      bool _doCacheIndexSameBlock
                  ) PendleERC20(_name, _symbol, __decimals) {
                      SY = _SY;
                      PT = _PT;
                      expiry = _expiry;
                      factory = msg.sender;
                      doCacheIndexSameBlock = _doCacheIndexSameBlock;
                  }
                  /**
                   * @notice Tokenize SY into PT + YT of equal qty. Every unit of asset of SY will create 1 PT + 1 YT
                   * @dev SY must be transferred to this contract prior to calling
                   */
                  function mintPY(
                      address receiverPT,
                      address receiverYT
                  ) external nonReentrant notExpired updateData returns (uint256 amountPYOut) {
                      address[] memory receiverPTs = new address[](1);
                      address[] memory receiverYTs = new address[](1);
                      uint256[] memory amountSyToMints = new uint256[](1);
                      (receiverPTs[0], receiverYTs[0], amountSyToMints[0]) = (receiverPT, receiverYT, _getFloatingSyAmount());
                      uint256[] memory amountPYOuts = _mintPY(receiverPTs, receiverYTs, amountSyToMints);
                      amountPYOut = amountPYOuts[0];
                  }
                  /// @notice Tokenize SY into PT + YT for multiple receivers. See `mintPY()` for more details
                  function mintPYMulti(
                      address[] calldata receiverPTs,
                      address[] calldata receiverYTs,
                      uint256[] calldata amountSyToMints
                  ) external nonReentrant notExpired updateData returns (uint256[] memory amountPYOuts) {
                      uint256 length = receiverPTs.length;
                      if (length == 0) revert Errors.ArrayEmpty();
                      if (receiverYTs.length != length || amountSyToMints.length != length) revert Errors.ArrayLengthMismatch();
                      uint256 totalSyToMint = amountSyToMints.sum();
                      if (totalSyToMint > _getFloatingSyAmount())
                          revert Errors.YieldContractInsufficientSy(totalSyToMint, _getFloatingSyAmount());
                      amountPYOuts = _mintPY(receiverPTs, receiverYTs, amountSyToMints);
                  }
                  /**
                   * @notice converts PT(+YT) tokens into SY, but interests & rewards are not redeemed at the
                   * same time
                   * @dev PT/YT must be transferred to this contract prior to calling
                   */
                  function redeemPY(address receiver) external nonReentrant updateData returns (uint256 amountSyOut) {
                      address[] memory receivers = new address[](1);
                      uint256[] memory amounts = new uint256[](1);
                      (receivers[0], amounts[0]) = (receiver, _getAmountPYToRedeem());
                      uint256[] memory amountSyOuts;
                      amountSyOuts = _redeemPY(receivers, amounts);
                      amountSyOut = amountSyOuts[0];
                  }
                  /**
                   * @notice redeems PT(+YT) for multiple users. See `redeemPY()`
                   * @dev PT/YT must be transferred to this contract prior to calling
                   * @dev fails if unable to redeem the total PY amount in `amountPYToRedeems`
                   */
                  function redeemPYMulti(
                      address[] calldata receivers,
                      uint256[] calldata amountPYToRedeems
                  ) external nonReentrant updateData returns (uint256[] memory amountSyOuts) {
                      if (receivers.length != amountPYToRedeems.length) revert Errors.ArrayLengthMismatch();
                      if (receivers.length == 0) revert Errors.ArrayEmpty();
                      amountSyOuts = _redeemPY(receivers, amountPYToRedeems);
                  }
                  /**
                   * @notice Redeems interests and rewards for `user`
                   * @param redeemInterest will only transfer out interest for user if true
                   * @param redeemRewards will only transfer out rewards for user if true
                   * @dev With YT yielding interest in the form of SY, which is redeemable by users, the reward
                   * distribution should be based on the amount of SYs that their YT currently represent, plus
                   * their dueInterest. It has been proven and tested that _rewardSharesUser will not change over
                   * time, unless users redeem their dueInterest or redeemPY. Due to this, it is required to
                   * update users' accruedReward STRICTLY BEFORE transferring out their interest.
                   */
                  function redeemDueInterestAndRewards(
                      address user,
                      bool redeemInterest,
                      bool redeemRewards
                  ) external nonReentrant updateData returns (uint256 interestOut, uint256[] memory rewardsOut) {
                      if (!redeemInterest && !redeemRewards) revert Errors.YCNothingToRedeem();
                      // if redeemRewards == true, this line must be here for obvious reason
                      // if redeemInterest == true, this line must be here because of the reason above
                      _updateAndDistributeRewards(user);
                      if (redeemRewards) {
                          rewardsOut = _doTransferOutRewards(user, user);
                          emit RedeemRewards(user, rewardsOut);
                      } else {
                          address[] memory tokens = getRewardTokens();
                          rewardsOut = new uint256[](tokens.length);
                      }
                      if (redeemInterest) {
                          _distributeInterest(user);
                          interestOut = _doTransferOutInterest(user, SY, factory);
                          emit RedeemInterest(user, interestOut);
                      } else {
                          interestOut = 0;
                      }
                  }
                  /**
                   * @dev All rewards and interests accrued post-expiry goes to the treasury.
                   * Reverts if called pre-expiry.
                   */
                  function redeemInterestAndRewardsPostExpiryForTreasury()
                      external
                      nonReentrant
                      updateData
                      returns (uint256 interestOut, uint256[] memory rewardsOut)
                  {
                      if (!isExpired()) revert Errors.YCNotExpired();
                      address treasury = IPYieldContractFactory(factory).treasury();
                      address[] memory tokens = getRewardTokens();
                      rewardsOut = new uint256[](tokens.length);
                      _redeemExternalReward();
                      for (uint256 i = 0; i < tokens.length; i++) {
                          rewardsOut[i] = _selfBalance(tokens[i]) - postExpiry.userRewardOwed[tokens[i]];
                          emit CollectRewardFee(tokens[i], rewardsOut[i]);
                      }
                      _transferOut(tokens, treasury, rewardsOut);
                      interestOut = postExpiry.totalSyInterestForTreasury;
                      postExpiry.totalSyInterestForTreasury = 0;
                      _transferOut(SY, treasury, interestOut);
                      emit CollectInterestFee(interestOut);
                  }
                  /// @notice updates and returns the reward indexes
                  function rewardIndexesCurrent() external override nonReentrant returns (uint256[] memory) {
                      return IStandardizedYield(SY).rewardIndexesCurrent();
                  }
                  /**
                   * @notice updates and returns the current PY index
                   * @dev this function maximizes the current PY index with the previous index, guaranteeing
                   * non-decreasing PY index
                   * @dev if `doCacheIndexSameBlock` is true, PY index only updates at most once per block,
                   * and has no state changes on the second call onwards (within the same block).
                   * @dev see `pyIndexStored()` for view function for cached value.
                   */
                  function pyIndexCurrent() public nonReentrant returns (uint256 currentIndex) {
                      currentIndex = _pyIndexCurrent();
                  }
                  /// @notice returns the last-updated PY index
                  function pyIndexStored() public view returns (uint256) {
                      return _pyIndexStored;
                  }
                  /**
                   * @notice do a final rewards redeeming, and sets post-expiry data
                   * @dev has no effect if called pre-expiry
                   */
                  function setPostExpiryData() external nonReentrant {
                      if (isExpired()) {
                          _setPostExpiryData();
                      }
                  }
                  /**
                   * @notice returns the current data post-expiry, if exists
                   * @dev reverts if post-expiry data not set (see `setPostExpiryData()`)
                   * @return firstPYIndex the earliest PY index post-expiry
                   * @return totalSyInterestForTreasury current amount of SY interests post-expiry for treasury
                   * @return firstRewardIndexes the earliest reward indices post-expiry, for each reward token
                   * @return userRewardOwed amount of unclaimed user rewards, for each reward token
                   */
                  function getPostExpiryData()
                      external
                      view
                      returns (
                          uint256 firstPYIndex,
                          uint256 totalSyInterestForTreasury,
                          uint256[] memory firstRewardIndexes,
                          uint256[] memory userRewardOwed
                      )
                  {
                      if (postExpiry.firstPYIndex == 0) revert Errors.YCPostExpiryDataNotSet();
                      firstPYIndex = postExpiry.firstPYIndex;
                      totalSyInterestForTreasury = postExpiry.totalSyInterestForTreasury;
                      address[] memory tokens = getRewardTokens();
                      firstRewardIndexes = new uint256[](tokens.length);
                      userRewardOwed = new uint256[](tokens.length);
                      for (uint256 i = 0; i < tokens.length; ++i) {
                          firstRewardIndexes[i] = postExpiry.firstRewardIndex[tokens[i]];
                          userRewardOwed[i] = postExpiry.userRewardOwed[tokens[i]];
                      }
                  }
                  function _mintPY(
                      address[] memory receiverPTs,
                      address[] memory receiverYTs,
                      uint256[] memory amountSyToMints
                  ) internal returns (uint256[] memory amountPYOuts) {
                      amountPYOuts = new uint256[](amountSyToMints.length);
                      uint256 index = _pyIndexCurrent();
                      for (uint256 i = 0; i < amountSyToMints.length; i++) {
                          amountPYOuts[i] = _calcPYToMint(amountSyToMints[i], index);
                          _mint(receiverYTs[i], amountPYOuts[i]);
                          IPPrincipalToken(PT).mintByYT(receiverPTs[i], amountPYOuts[i]);
                          emit Mint(msg.sender, receiverPTs[i], receiverYTs[i], amountSyToMints[i], amountPYOuts[i]);
                      }
                  }
                  function isExpired() public view returns (bool) {
                      return MiniHelpers.isCurrentlyExpired(expiry);
                  }
                  function _redeemPY(
                      address[] memory receivers,
                      uint256[] memory amountPYToRedeems
                  ) internal returns (uint256[] memory amountSyOuts) {
                      uint256 totalAmountPYToRedeem = amountPYToRedeems.sum();
                      IPPrincipalToken(PT).burnByYT(address(this), totalAmountPYToRedeem);
                      if (!isExpired()) _burn(address(this), totalAmountPYToRedeem);
                      uint256 index = _pyIndexCurrent();
                      uint256 totalSyInterestPostExpiry;
                      amountSyOuts = new uint256[](receivers.length);
                      for (uint256 i = 0; i < receivers.length; i++) {
                          uint256 syInterestPostExpiry;
                          (amountSyOuts[i], syInterestPostExpiry) = _calcSyRedeemableFromPY(amountPYToRedeems[i], index);
                          _transferOut(SY, receivers[i], amountSyOuts[i]);
                          totalSyInterestPostExpiry += syInterestPostExpiry;
                          emit Burn(msg.sender, receivers[i], amountPYToRedeems[i], amountSyOuts[i]);
                      }
                      if (totalSyInterestPostExpiry != 0) {
                          postExpiry.totalSyInterestForTreasury += totalSyInterestPostExpiry.Uint128();
                      }
                  }
                  function _calcPYToMint(uint256 amountSy, uint256 indexCurrent) internal pure returns (uint256 amountPY) {
                      // doesn't matter before or after expiry, since mintPY is only allowed before expiry
                      return SYUtils.syToAsset(indexCurrent, amountSy);
                  }
                  function _calcSyRedeemableFromPY(
                      uint256 amountPY,
                      uint256 indexCurrent
                  ) internal view returns (uint256 syToUser, uint256 syInterestPostExpiry) {
                      syToUser = SYUtils.assetToSy(indexCurrent, amountPY);
                      if (isExpired()) {
                          uint256 totalSyRedeemable = SYUtils.assetToSy(postExpiry.firstPYIndex, amountPY);
                          syInterestPostExpiry = totalSyRedeemable - syToUser;
                      }
                  }
                  function _getAmountPYToRedeem() internal view returns (uint256) {
                      if (!isExpired()) return PMath.min(_selfBalance(PT), balanceOf(address(this)));
                      else return _selfBalance(PT);
                  }
                  function _updateSyReserve() internal virtual {
                      syReserve = _selfBalance(SY);
                  }
                  function _getFloatingSyAmount() internal view returns (uint256 amount) {
                      amount = _selfBalance(SY) - syReserve;
                      if (amount == 0) revert Errors.YCNoFloatingSy();
                  }
                  function _setPostExpiryData() internal {
                      PostExpiryData storage local = postExpiry;
                      if (local.firstPYIndex != 0) return; // already set
                      _redeemExternalReward(); // do a final redeem. All the future reward income will belong to the treasury
                      local.firstPYIndex = _pyIndexCurrent().Uint128();
                      address[] memory rewardTokens = IStandardizedYield(SY).getRewardTokens();
                      uint256[] memory rewardIndexes = IStandardizedYield(SY).rewardIndexesCurrent();
                      for (uint256 i = 0; i < rewardTokens.length; i++) {
                          local.firstRewardIndex[rewardTokens[i]] = rewardIndexes[i];
                          local.userRewardOwed[rewardTokens[i]] = _selfBalance(rewardTokens[i]);
                      }
                  }
                  /*///////////////////////////////////////////////////////////////
                                             INTEREST-RELATED
                  //////////////////////////////////////////////////////////////*/
                  function _getInterestIndex() internal virtual override returns (uint256 index) {
                      if (isExpired()) index = postExpiry.firstPYIndex;
                      else index = _pyIndexCurrent();
                  }
                  function _pyIndexCurrent() internal returns (uint256 currentIndex) {
                      if (doCacheIndexSameBlock && pyIndexLastUpdatedBlock == block.number) return _pyIndexStored;
                      uint128 index128 = PMath.max(IStandardizedYield(SY).exchangeRate(), _pyIndexStored).Uint128();
                      currentIndex = index128;
                      _pyIndexStored = index128;
                      pyIndexLastUpdatedBlock = uint128(block.number);
                      emit NewInterestIndex(currentIndex);
                  }
                  function _YTbalance(address user) internal view override returns (uint256) {
                      return balanceOf(user);
                  }
                  /*///////////////////////////////////////////////////////////////
                                             REWARDS-RELATED
                  //////////////////////////////////////////////////////////////*/
                  function getRewardTokens() public view returns (address[] memory) {
                      return IStandardizedYield(SY).getRewardTokens();
                  }
                  function _doTransferOutRewards(
                      address user,
                      address receiver
                  ) internal virtual override returns (uint256[] memory rewardAmounts) {
                      address[] memory tokens = getRewardTokens();
                      if (isExpired()) {
                          // post-expiry, all incoming rewards will go to the treasury
                          // hence, we can save users one _redeemExternal here
                          for (uint256 i = 0; i < tokens.length; i++) {
                              uint256 owed = postExpiry.userRewardOwed[tokens[i]];
                              uint256 accrued = userReward[tokens[i]][user].accrued;
                              postExpiry.userRewardOwed[tokens[i]] = (owed < accrued) ? 0 : owed - accrued;
                          }
                          rewardAmounts = __doTransferOutRewardsLocal(tokens, user, receiver, false);
                      } else {
                          rewardAmounts = __doTransferOutRewardsLocal(tokens, user, receiver, true);
                      }
                  }
                  function __doTransferOutRewardsLocal(
                      address[] memory tokens,
                      address user,
                      address receiver,
                      bool allowedToRedeemExternalReward
                  ) internal returns (uint256[] memory rewardAmounts) {
                      address treasury = IPYieldContractFactory(factory).treasury();
                      uint256 feeRate = IPYieldContractFactory(factory).rewardFeeRate();
                      bool redeemExternalThisRound;
                      rewardAmounts = new uint256[](tokens.length);
                      for (uint256 i = 0; i < tokens.length; i++) {
                          uint256 rewardPreFee = userReward[tokens[i]][user].accrued;
                          userReward[tokens[i]][user].accrued = 0;
                          uint256 feeAmount = rewardPreFee.mulDown(feeRate);
                          rewardAmounts[i] = rewardPreFee - feeAmount;
                          if (!redeemExternalThisRound && allowedToRedeemExternalReward) {
                              if (_selfBalance(tokens[i]) < rewardPreFee) {
                                  _redeemExternalReward();
                                  redeemExternalThisRound = true;
                              }
                          }
                          _transferOut(tokens[i], treasury, feeAmount);
                          _transferOut(tokens[i], receiver, rewardAmounts[i]);
                          emit CollectRewardFee(tokens[i], feeAmount);
                      }
                  }
                  function _redeemExternalReward() internal virtual override {
                      IStandardizedYield(SY).claimRewards(address(this));
                  }
                  /// @dev effectively returning the amount of SY generating rewards for this user
                  function _rewardSharesUser(address user) internal view virtual override returns (uint256) {
                      uint256 index = userInterest[user].index;
                      if (index == 0) return 0;
                      return SYUtils.assetToSy(index, balanceOf(user)) + userInterest[user].accrued;
                  }
                  function _updateRewardIndex() internal override returns (address[] memory tokens, uint256[] memory indexes) {
                      tokens = getRewardTokens();
                      if (isExpired()) {
                          indexes = new uint256[](tokens.length);
                          for (uint256 i = 0; i < tokens.length; i++) indexes[i] = postExpiry.firstRewardIndex[tokens[i]];
                      } else {
                          indexes = IStandardizedYield(SY).rewardIndexesCurrent();
                      }
                  }
                  //solhint-disable-next-line ordering
                  function _beforeTokenTransfer(address from, address to, uint256) internal override {
                      if (isExpired()) _setPostExpiryData();
                      _updateAndDistributeRewardsForTwo(from, to);
                      _distributeInterestForTwo(from, to);
                  }
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IPInterestManagerYT {
                  event CollectInterestFee(uint256 amountInterestFee);
                  function userInterest(address user) external view returns (uint128 lastPYIndex, uint128 accruedInterest);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              interface IPPrincipalToken is IERC20Metadata {
                  function burnByYT(address user, uint256 amount) external;
                  function mintByYT(address user, uint256 amount) external;
                  function initialize(address _YT) external;
                  function SY() external view returns (address);
                  function YT() external view returns (address);
                  function factory() external view returns (address);
                  function expiry() external view returns (uint256);
                  function isExpired() external view returns (bool);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              /*
               * MIT License
               * ===========
               *
               * Permission is hereby granted, free of charge, to any person obtaining a copy
               * of this software and associated documentation files (the "Software"), to deal
               * in the Software without restriction, including without limitation the rights
               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
               * copies of the Software, and to permit persons to whom the Software is
               * furnished to do so, subject to the following conditions:
               *
               * The above copyright notice and this permission notice shall be included in all
               * copies or substantial portions of the Software.
               *
               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
               */
              pragma solidity ^0.8.0;
              interface IPYieldContractFactory {
                  event CreateYieldContract(address indexed SY, uint256 indexed expiry, address PT, address YT);
                  event SetExpiryDivisor(uint256 newExpiryDivisor);
                  event SetInterestFeeRate(uint256 newInterestFeeRate);
                  event SetRewardFeeRate(uint256 newRewardFeeRate);
                  event SetTreasury(address indexed treasury);
                  function getPT(address SY, uint256 expiry) external view returns (address);
                  function getYT(address SY, uint256 expiry) external view returns (address);
                  function expiryDivisor() external view returns (uint96);
                  function interestFeeRate() external view returns (uint128);
                  function rewardFeeRate() external view returns (uint128);
                  function treasury() external view returns (address);
                  function isPT(address) external view returns (bool);
                  function isYT(address) external view returns (bool);
                  function createYieldContract(
                      address SY,
                      uint32 expiry,
                      bool doCacheIndexSameBlock
                  ) external returns (address PT, address YT);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              import "./IRewardManager.sol";
              import "./IPInterestManagerYT.sol";
              interface IPYieldToken is IERC20Metadata, IRewardManager, IPInterestManagerYT {
                  event NewInterestIndex(uint256 indexed newIndex);
                  event Mint(
                      address indexed caller,
                      address indexed receiverPT,
                      address indexed receiverYT,
                      uint256 amountSyToMint,
                      uint256 amountPYOut
                  );
                  event Burn(address indexed caller, address indexed receiver, uint256 amountPYToRedeem, uint256 amountSyOut);
                  event RedeemRewards(address indexed user, uint256[] amountRewardsOut);
                  event RedeemInterest(address indexed user, uint256 interestOut);
                  event CollectRewardFee(address indexed rewardToken, uint256 amountRewardFee);
                  function mintPY(address receiverPT, address receiverYT) external returns (uint256 amountPYOut);
                  function redeemPY(address receiver) external returns (uint256 amountSyOut);
                  function redeemPYMulti(
                      address[] calldata receivers,
                      uint256[] calldata amountPYToRedeems
                  ) external returns (uint256[] memory amountSyOuts);
                  function redeemDueInterestAndRewards(
                      address user,
                      bool redeemInterest,
                      bool redeemRewards
                  ) external returns (uint256 interestOut, uint256[] memory rewardsOut);
                  function rewardIndexesCurrent() external returns (uint256[] memory);
                  function pyIndexCurrent() external returns (uint256);
                  function pyIndexStored() external view returns (uint256);
                  function getRewardTokens() external view returns (address[] memory);
                  function SY() external view returns (address);
                  function PT() external view returns (address);
                  function factory() external view returns (address);
                  function expiry() external view returns (uint256);
                  function isExpired() external view returns (bool);
                  function doCacheIndexSameBlock() external view returns (bool);
                  function pyIndexLastUpdatedBlock() external view returns (uint128);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              pragma solidity ^0.8.0;
              interface IRewardManager {
                  function userReward(address token, address user) external view returns (uint128 index, uint128 accrued);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              /*
               * MIT License
               * ===========
               *
               * Permission is hereby granted, free of charge, to any person obtaining a copy
               * of this software and associated documentation files (the "Software"), to deal
               * in the Software without restriction, including without limitation the rights
               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
               * copies of the Software, and to permit persons to whom the Software is
               * furnished to do so, subject to the following conditions:
               *
               * The above copyright notice and this permission notice shall be included in all
               * copies or substantial portions of the Software.
               *
               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
               */
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
              interface IStandardizedYield is IERC20Metadata {
                  /// @dev Emitted when any base tokens is deposited to mint shares
                  event Deposit(
                      address indexed caller,
                      address indexed receiver,
                      address indexed tokenIn,
                      uint256 amountDeposited,
                      uint256 amountSyOut
                  );
                  /// @dev Emitted when any shares are redeemed for base tokens
                  event Redeem(
                      address indexed caller,
                      address indexed receiver,
                      address indexed tokenOut,
                      uint256 amountSyToRedeem,
                      uint256 amountTokenOut
                  );
                  /// @dev check `assetInfo()` for more information
                  enum AssetType {
                      TOKEN,
                      LIQUIDITY
                  }
                  /// @dev Emitted when (`user`) claims their rewards
                  event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts);
                  /**
                   * @notice mints an amount of shares by depositing a base token.
                   * @param receiver shares recipient address
                   * @param tokenIn address of the base tokens to mint shares
                   * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`)
                   * @param minSharesOut reverts if amount of shares minted is lower than this
                   * @return amountSharesOut amount of shares minted
                   * @dev Emits a {Deposit} event
                   *
                   * Requirements:
                   * - (`tokenIn`) must be a valid base token.
                   */
                  function deposit(
                      address receiver,
                      address tokenIn,
                      uint256 amountTokenToDeposit,
                      uint256 minSharesOut
                  ) external payable returns (uint256 amountSharesOut);
                  /**
                   * @notice redeems an amount of base tokens by burning some shares
                   * @param receiver recipient address
                   * @param amountSharesToRedeem amount of shares to be burned
                   * @param tokenOut address of the base token to be redeemed
                   * @param minTokenOut reverts if amount of base token redeemed is lower than this
                   * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender`
                   * @return amountTokenOut amount of base tokens redeemed
                   * @dev Emits a {Redeem} event
                   *
                   * Requirements:
                   * - (`tokenOut`) must be a valid base token.
                   */
                  function redeem(
                      address receiver,
                      uint256 amountSharesToRedeem,
                      address tokenOut,
                      uint256 minTokenOut,
                      bool burnFromInternalBalance
                  ) external returns (uint256 amountTokenOut);
                  /**
                   * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account
                   * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy
                   he can mint must be X * exchangeRate / 1e18
                   * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication
                   & division
                   */
                  function exchangeRate() external view returns (uint256 res);
                  /**
                   * @notice claims reward for (`user`)
                   * @param user the user receiving their rewards
                   * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
                   * @dev
                   * Emits a `ClaimRewards` event
                   * See {getRewardTokens} for list of reward tokens
                   */
                  function claimRewards(address user) external returns (uint256[] memory rewardAmounts);
                  /**
                   * @notice get the amount of unclaimed rewards for (`user`)
                   * @param user the user to check for
                   * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
                   */
                  function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts);
                  function rewardIndexesCurrent() external returns (uint256[] memory indexes);
                  function rewardIndexesStored() external view returns (uint256[] memory indexes);
                  /**
                   * @notice returns the list of reward token addresses
                   */
                  function getRewardTokens() external view returns (address[] memory);
                  /**
                   * @notice returns the address of the underlying yield token
                   */
                  function yieldToken() external view returns (address);
                  /**
                   * @notice returns all tokens that can mint this SY
                   */
                  function getTokensIn() external view returns (address[] memory res);
                  /**
                   * @notice returns all tokens that can be redeemed by this SY
                   */
                  function getTokensOut() external view returns (address[] memory res);
                  function isValidTokenIn(address token) external view returns (bool);
                  function isValidTokenOut(address token) external view returns (bool);
                  function previewDeposit(
                      address tokenIn,
                      uint256 amountTokenToDeposit
                  ) external view returns (uint256 amountSharesOut);
                  function previewRedeem(
                      address tokenOut,
                      uint256 amountSharesToRedeem
                  ) external view returns (uint256 amountTokenOut);
                  /**
                   * @notice This function contains information to interpret what the asset is
                   * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens,
                      2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the chain)
                   * @return assetAddress the address of the asset
                   * @return assetDecimals the decimals of the asset
                   */
                  function assetInfo() external view returns (AssetType assetType, address assetAddress, uint8 assetDecimals);
              }
              // SPDX-License-Identifier: GPL-3.0-or-later
              /*
               * MIT License
               * ===========
               *
               * Permission is hereby granted, free of charge, to any person obtaining a copy
               * of this software and associated documentation files (the "Software"), to deal
               * in the Software without restriction, including without limitation the rights
               * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
               * copies of the Software, and to permit persons to whom the Software is
               * furnished to do so, subject to the following conditions:
               *
               * The above copyright notice and this permission notice shall be included in all
               * copies or substantial portions of the Software.
               *
               * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
               * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
               * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
               * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
               * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
               * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
               */
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
              interface IWETH is IERC20 {
                  event Deposit(address indexed dst, uint256 wad);
                  event Withdrawal(address indexed src, uint256 wad);
                  function deposit() external payable;
                  function withdraw(uint256 wad) external;
              }
              

              File 18 of 30: Proxy
              pragma solidity ^0.5.3;
              
              /// @title Proxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
              /// @author Stefan George - <stefan@gnosis.io>
              /// @author Richard Meissner - <richard@gnosis.io>
              contract Proxy {
              
                  // masterCopy always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                  // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                  address internal masterCopy;
              
                  /// @dev Constructor function sets address of master copy contract.
                  /// @param _masterCopy Master copy address.
                  constructor(address _masterCopy)
                      public
                  {
                      require(_masterCopy != address(0), "Invalid master copy address provided");
                      masterCopy = _masterCopy;
                  }
              
                  /// @dev Fallback function forwards all transactions and returns all received return data.
                  function ()
                      external
                      payable
                  {
                      // solium-disable-next-line security/no-inline-assembly
                      assembly {
                          let masterCopy := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                          // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                          if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                              mstore(0, masterCopy)
                              return(0, 0x20)
                          }
                          calldatacopy(0, 0, calldatasize())
                          let success := delegatecall(gas, masterCopy, 0, calldatasize(), 0, 0)
                          returndatacopy(0, 0, returndatasize())
                          if eq(success, 0) { revert(0, returndatasize()) }
                          return(0, returndatasize())
                      }
                  }
              }

              File 19 of 30: 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 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.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _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 Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev 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.7.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
                              /// @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.7.0) (utils/StorageSlot.sol)
              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:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @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
                      }
                  }
              }
              

              File 20 of 30: 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 21 of 30: TransparentUpgradeableProxy
              // 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 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.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _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 Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev 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.7.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(
                      address _logic,
                      address admin_,
                      bytes memory _data
                  ) payable ERC1967Proxy(_logic, _data) {
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.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
                              /// @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.7.0) (utils/StorageSlot.sol)
              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:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @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
                      }
                  }
              }
              

              File 22 of 30: TransparentUpgradeableProxy
              // 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 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.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _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 Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev 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.7.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(
                      address _logic,
                      address admin_,
                      bytes memory _data
                  ) payable ERC1967Proxy(_logic, _data) {
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.7.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
                              /// @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.7.0) (utils/StorageSlot.sol)
              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:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      /// @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
                      }
                  }
              }
              

              File 23 of 30: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
              import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
              import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
              // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
              contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                  constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IBeacon.sol";
              import "../Proxy.sol";
              import "../ERC1967/ERC1967Upgrade.sol";
              /**
               * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}.
               *
               * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
               * conflict with the storage layout of the implementation behind the proxy.
               *
               * _Available since v3.4._
               */
              contract BeaconProxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the proxy with `beacon`.
                   *
                   * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                   * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity
                   * constructor.
                   *
                   * Requirements:
                   *
                   * - `beacon` must be a contract with the interface {IBeacon}.
                   */
                  constructor(address beacon, bytes memory data) payable {
                      assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1));
                      _upgradeBeaconToAndCall(beacon, data, false);
                  }
                  /**
                   * @dev Returns the current beacon address.
                   */
                  function _beacon() internal view virtual returns (address) {
                      return _getBeacon();
                  }
                  /**
                   * @dev Returns the current implementation address of the associated beacon.
                   */
                  function _implementation() internal view virtual override returns (address) {
                      return IBeacon(_getBeacon()).implementation();
                  }
                  /**
                   * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                   *
                   * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                   *
                   * Requirements:
                   *
                   * - `beacon` must be a contract.
                   * - The implementation returned by `beacon` must be a contract.
                   */
                  function _setBeacon(address beacon, bytes memory data) internal virtual {
                      _upgradeBeaconToAndCall(beacon, data, false);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IBeacon.sol";
              import "../../access/Ownable.sol";
              import "../../utils/Address.sol";
              /**
               * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
               * implementation contract, which is where they will delegate all function calls.
               *
               * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
               */
              contract UpgradeableBeacon is IBeacon, Ownable {
                  address private _implementation;
                  /**
                   * @dev Emitted when the implementation returned by the beacon is changed.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
                   * beacon.
                   */
                  constructor(address implementation_) {
                      _setImplementation(implementation_);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function implementation() public view virtual override returns (address) {
                      return _implementation;
                  }
                  /**
                   * @dev Upgrades the beacon to a new implementation.
                   *
                   * Emits an {Upgraded} event.
                   *
                   * Requirements:
                   *
                   * - msg.sender must be the owner of the contract.
                   * - `newImplementation` must be a contract.
                   */
                  function upgradeTo(address newImplementation) public virtual onlyOwner {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Sets the implementation contract address for this beacon
                   *
                   * Requirements:
                   *
                   * - `newImplementation` must be a contract.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
                      _implementation = newImplementation;
                  }
              }
              // SPDX-License-Identifier: MIT
              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 initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./TransparentUpgradeableProxy.sol";
              import "../../access/Ownable.sol";
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
                  /**
                   * @dev Returns the current implementation of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("implementation()")) == 0x5c60da1b
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Returns the current admin of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("admin()")) == 0xf851a440
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Changes the admin of `proxy` to `newAdmin`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the current admin of `proxy`.
                   */
                  function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                      proxy.changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                      proxy.upgradeTo(implementation);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                   * {TransparentUpgradeableProxy-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              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
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                      address oldImplementation = _getImplementation();
                      // Initial upgrade and setup call
                      _setImplementation(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                      // Perform rollback test if not already in progress
                      StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                      if (!rollbackTesting.value) {
                          // Trigger rollback using upgradeTo from the new implementation
                          rollbackTesting.value = true;
                          Address.functionDelegateCall(
                              newImplementation,
                              abi.encodeWithSignature(
                                  "upgradeTo(address)",
                                  oldImplementation
                              )
                          );
                          rollbackTesting.value = false;
                          // Check rollback was effective
                          require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                          // Finally reset to the new implementation and log the upgrade
                          _setImplementation(newImplementation);
                          emit Upgraded(newImplementation);
                      }
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(
                          Address.isContract(newBeacon),
                          "ERC1967: new beacon is not a contract"
                      );
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{ value: amount }("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain`call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      require(isContract(target), "Address: call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.call{ value: value }(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                      require(isContract(target), "Address: static call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      require(isContract(target), "Address: delegate call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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 () {
                      address msgSender = _msgSender();
                      _owner = msgSender;
                      emit OwnershipTransferred(address(0), msgSender);
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                      _;
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              // SPDX-License-Identifier: MIT
              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) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                  }
              }
              

              File 24 of 30: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
              import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
              import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
              // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
              contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                  constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IBeacon.sol";
              import "../Proxy.sol";
              import "../ERC1967/ERC1967Upgrade.sol";
              /**
               * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}.
               *
               * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
               * conflict with the storage layout of the implementation behind the proxy.
               *
               * _Available since v3.4._
               */
              contract BeaconProxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the proxy with `beacon`.
                   *
                   * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                   * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity
                   * constructor.
                   *
                   * Requirements:
                   *
                   * - `beacon` must be a contract with the interface {IBeacon}.
                   */
                  constructor(address beacon, bytes memory data) payable {
                      assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1));
                      _upgradeBeaconToAndCall(beacon, data, false);
                  }
                  /**
                   * @dev Returns the current beacon address.
                   */
                  function _beacon() internal view virtual returns (address) {
                      return _getBeacon();
                  }
                  /**
                   * @dev Returns the current implementation address of the associated beacon.
                   */
                  function _implementation() internal view virtual override returns (address) {
                      return IBeacon(_getBeacon()).implementation();
                  }
                  /**
                   * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                   *
                   * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                   *
                   * Requirements:
                   *
                   * - `beacon` must be a contract.
                   * - The implementation returned by `beacon` must be a contract.
                   */
                  function _setBeacon(address beacon, bytes memory data) internal virtual {
                      _upgradeBeaconToAndCall(beacon, data, false);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IBeacon.sol";
              import "../../access/Ownable.sol";
              import "../../utils/Address.sol";
              /**
               * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
               * implementation contract, which is where they will delegate all function calls.
               *
               * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
               */
              contract UpgradeableBeacon is IBeacon, Ownable {
                  address private _implementation;
                  /**
                   * @dev Emitted when the implementation returned by the beacon is changed.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
                   * beacon.
                   */
                  constructor(address implementation_) {
                      _setImplementation(implementation_);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function implementation() public view virtual override returns (address) {
                      return _implementation;
                  }
                  /**
                   * @dev Upgrades the beacon to a new implementation.
                   *
                   * Emits an {Upgraded} event.
                   *
                   * Requirements:
                   *
                   * - msg.sender must be the owner of the contract.
                   * - `newImplementation` must be a contract.
                   */
                  function upgradeTo(address newImplementation) public virtual onlyOwner {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Sets the implementation contract address for this beacon
                   *
                   * Requirements:
                   *
                   * - `newImplementation` must be a contract.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
                      _implementation = newImplementation;
                  }
              }
              // SPDX-License-Identifier: MIT
              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 initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./TransparentUpgradeableProxy.sol";
              import "../../access/Ownable.sol";
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
                  /**
                   * @dev Returns the current implementation of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("implementation()")) == 0x5c60da1b
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Returns the current admin of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("admin()")) == 0xf851a440
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Changes the admin of `proxy` to `newAdmin`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the current admin of `proxy`.
                   */
                  function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                      proxy.changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                      proxy.upgradeTo(implementation);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                   * {TransparentUpgradeableProxy-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              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
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                      address oldImplementation = _getImplementation();
                      // Initial upgrade and setup call
                      _setImplementation(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                      // Perform rollback test if not already in progress
                      StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                      if (!rollbackTesting.value) {
                          // Trigger rollback using upgradeTo from the new implementation
                          rollbackTesting.value = true;
                          Address.functionDelegateCall(
                              newImplementation,
                              abi.encodeWithSignature(
                                  "upgradeTo(address)",
                                  oldImplementation
                              )
                          );
                          rollbackTesting.value = false;
                          // Check rollback was effective
                          require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                          // Finally reset to the new implementation and log the upgrade
                          _setImplementation(newImplementation);
                          emit Upgraded(newImplementation);
                      }
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(
                          Address.isContract(newBeacon),
                          "ERC1967: new beacon is not a contract"
                      );
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{ value: amount }("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain`call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      require(isContract(target), "Address: call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.call{ value: value }(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                      require(isContract(target), "Address: static call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      require(isContract(target), "Address: delegate call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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 () {
                      address msgSender = _msgSender();
                      _owner = msgSender;
                      emit OwnershipTransferred(address(0), msgSender);
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                      _;
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              // SPDX-License-Identifier: MIT
              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) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                  }
              }
              

              File 25 of 30: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
              import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
              import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
              // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
              contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                  constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IBeacon.sol";
              import "../Proxy.sol";
              import "../ERC1967/ERC1967Upgrade.sol";
              /**
               * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}.
               *
               * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
               * conflict with the storage layout of the implementation behind the proxy.
               *
               * _Available since v3.4._
               */
              contract BeaconProxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the proxy with `beacon`.
                   *
                   * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                   * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity
                   * constructor.
                   *
                   * Requirements:
                   *
                   * - `beacon` must be a contract with the interface {IBeacon}.
                   */
                  constructor(address beacon, bytes memory data) payable {
                      assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1));
                      _upgradeBeaconToAndCall(beacon, data, false);
                  }
                  /**
                   * @dev Returns the current beacon address.
                   */
                  function _beacon() internal view virtual returns (address) {
                      return _getBeacon();
                  }
                  /**
                   * @dev Returns the current implementation address of the associated beacon.
                   */
                  function _implementation() internal view virtual override returns (address) {
                      return IBeacon(_getBeacon()).implementation();
                  }
                  /**
                   * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                   *
                   * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                   *
                   * Requirements:
                   *
                   * - `beacon` must be a contract.
                   * - The implementation returned by `beacon` must be a contract.
                   */
                  function _setBeacon(address beacon, bytes memory data) internal virtual {
                      _upgradeBeaconToAndCall(beacon, data, false);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IBeacon.sol";
              import "../../access/Ownable.sol";
              import "../../utils/Address.sol";
              /**
               * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
               * implementation contract, which is where they will delegate all function calls.
               *
               * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
               */
              contract UpgradeableBeacon is IBeacon, Ownable {
                  address private _implementation;
                  /**
                   * @dev Emitted when the implementation returned by the beacon is changed.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
                   * beacon.
                   */
                  constructor(address implementation_) {
                      _setImplementation(implementation_);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function implementation() public view virtual override returns (address) {
                      return _implementation;
                  }
                  /**
                   * @dev Upgrades the beacon to a new implementation.
                   *
                   * Emits an {Upgraded} event.
                   *
                   * Requirements:
                   *
                   * - msg.sender must be the owner of the contract.
                   * - `newImplementation` must be a contract.
                   */
                  function upgradeTo(address newImplementation) public virtual onlyOwner {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Sets the implementation contract address for this beacon
                   *
                   * Requirements:
                   *
                   * - `newImplementation` must be a contract.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
                      _implementation = newImplementation;
                  }
              }
              // SPDX-License-Identifier: MIT
              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 initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./TransparentUpgradeableProxy.sol";
              import "../../access/Ownable.sol";
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
                  /**
                   * @dev Returns the current implementation of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("implementation()")) == 0x5c60da1b
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Returns the current admin of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("admin()")) == 0xf851a440
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Changes the admin of `proxy` to `newAdmin`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the current admin of `proxy`.
                   */
                  function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                      proxy.changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                      proxy.upgradeTo(implementation);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                   * {TransparentUpgradeableProxy-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              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
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                      address oldImplementation = _getImplementation();
                      // Initial upgrade and setup call
                      _setImplementation(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                      // Perform rollback test if not already in progress
                      StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                      if (!rollbackTesting.value) {
                          // Trigger rollback using upgradeTo from the new implementation
                          rollbackTesting.value = true;
                          Address.functionDelegateCall(
                              newImplementation,
                              abi.encodeWithSignature(
                                  "upgradeTo(address)",
                                  oldImplementation
                              )
                          );
                          rollbackTesting.value = false;
                          // Check rollback was effective
                          require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                          // Finally reset to the new implementation and log the upgrade
                          _setImplementation(newImplementation);
                          emit Upgraded(newImplementation);
                      }
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(
                          Address.isContract(newBeacon),
                          "ERC1967: new beacon is not a contract"
                      );
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{ value: amount }("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain`call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      require(isContract(target), "Address: call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.call{ value: value }(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                      require(isContract(target), "Address: static call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      require(isContract(target), "Address: delegate call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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 () {
                      address msgSender = _msgSender();
                      _owner = msgSender;
                      emit OwnershipTransferred(address(0), msgSender);
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                      _;
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              // SPDX-License-Identifier: MIT
              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) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                  }
              }
              

              File 26 of 30: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
              import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
              import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
              import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
              // Kept for backwards compatibility with older versions of Hardhat and Truffle plugins.
              contract AdminUpgradeabilityProxy is TransparentUpgradeableProxy {
                  constructor(address logic, address admin, bytes memory data) payable TransparentUpgradeableProxy(logic, admin, data) {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IBeacon.sol";
              import "../Proxy.sol";
              import "../ERC1967/ERC1967Upgrade.sol";
              /**
               * @dev This contract implements a proxy that gets the implementation address for each call from a {UpgradeableBeacon}.
               *
               * The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
               * conflict with the storage layout of the implementation behind the proxy.
               *
               * _Available since v3.4._
               */
              contract BeaconProxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the proxy with `beacon`.
                   *
                   * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
                   * will typically be an encoded function call, and allows initializating the storage of the proxy like a Solidity
                   * constructor.
                   *
                   * Requirements:
                   *
                   * - `beacon` must be a contract with the interface {IBeacon}.
                   */
                  constructor(address beacon, bytes memory data) payable {
                      assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1));
                      _upgradeBeaconToAndCall(beacon, data, false);
                  }
                  /**
                   * @dev Returns the current beacon address.
                   */
                  function _beacon() internal view virtual returns (address) {
                      return _getBeacon();
                  }
                  /**
                   * @dev Returns the current implementation address of the associated beacon.
                   */
                  function _implementation() internal view virtual override returns (address) {
                      return IBeacon(_getBeacon()).implementation();
                  }
                  /**
                   * @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
                   *
                   * If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
                   *
                   * Requirements:
                   *
                   * - `beacon` must be a contract.
                   * - The implementation returned by `beacon` must be a contract.
                   */
                  function _setBeacon(address beacon, bytes memory data) internal virtual {
                      _upgradeBeaconToAndCall(beacon, data, false);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./IBeacon.sol";
              import "../../access/Ownable.sol";
              import "../../utils/Address.sol";
              /**
               * @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
               * implementation contract, which is where they will delegate all function calls.
               *
               * An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
               */
              contract UpgradeableBeacon is IBeacon, Ownable {
                  address private _implementation;
                  /**
                   * @dev Emitted when the implementation returned by the beacon is changed.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
                   * beacon.
                   */
                  constructor(address implementation_) {
                      _setImplementation(implementation_);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function implementation() public view virtual override returns (address) {
                      return _implementation;
                  }
                  /**
                   * @dev Upgrades the beacon to a new implementation.
                   *
                   * Emits an {Upgraded} event.
                   *
                   * Requirements:
                   *
                   * - msg.sender must be the owner of the contract.
                   * - `newImplementation` must be a contract.
                   */
                  function upgradeTo(address newImplementation) public virtual onlyOwner {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Sets the implementation contract address for this beacon
                   *
                   * Requirements:
                   *
                   * - `newImplementation` must be a contract.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
                      _implementation = newImplementation;
                  }
              }
              // SPDX-License-Identifier: MIT
              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 initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import "./TransparentUpgradeableProxy.sol";
              import "../../access/Ownable.sol";
              /**
               * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
               * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
               */
              contract ProxyAdmin is Ownable {
                  /**
                   * @dev Returns the current implementation of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("implementation()")) == 0x5c60da1b
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Returns the current admin of `proxy`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
                      // We need to manually run the static call since the getter cannot be flagged as view
                      // bytes4(keccak256("admin()")) == 0xf851a440
                      (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
                      require(success);
                      return abi.decode(returndata, (address));
                  }
                  /**
                   * @dev Changes the admin of `proxy` to `newAdmin`.
                   *
                   * Requirements:
                   *
                   * - This contract must be the current admin of `proxy`.
                   */
                  function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
                      proxy.changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
                      proxy.upgradeTo(implementation);
                  }
                  /**
                   * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See
                   * {TransparentUpgradeableProxy-upgradeToAndCall}.
                   *
                   * Requirements:
                   *
                   * - This contract must be the admin of `proxy`.
                   */
                  function upgradeAndCall(TransparentUpgradeableProxy proxy, address implementation, bytes memory data) public payable virtual onlyOwner {
                      proxy.upgradeToAndCall{value: msg.value}(implementation, data);
                  }
              }
              // SPDX-License-Identifier: MIT
              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
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive () external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.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._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallSecure(address newImplementation, bytes memory data, bool forceCall) internal {
                      address oldImplementation = _getImplementation();
                      // Initial upgrade and setup call
                      _setImplementation(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                      // Perform rollback test if not already in progress
                      StorageSlot.BooleanSlot storage rollbackTesting = StorageSlot.getBooleanSlot(_ROLLBACK_SLOT);
                      if (!rollbackTesting.value) {
                          // Trigger rollback using upgradeTo from the new implementation
                          rollbackTesting.value = true;
                          Address.functionDelegateCall(
                              newImplementation,
                              abi.encodeWithSignature(
                                  "upgradeTo(address)",
                                  oldImplementation
                              )
                          );
                          rollbackTesting.value = false;
                          // Check rollback was effective
                          require(oldImplementation == _getImplementation(), "ERC1967Upgrade: upgrade breaks further upgrades");
                          // Finally reset to the new implementation and log the upgrade
                          _setImplementation(newImplementation);
                          emit Upgraded(newImplementation);
                      }
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(
                          Address.isContract(newBeacon),
                          "ERC1967: new beacon is not a contract"
                      );
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize, which returns 0 for contracts in
                      // construction, since the code is only stored at the end of the
                      // constructor execution.
                      uint256 size;
                      // solhint-disable-next-line no-inline-assembly
                      assembly { size := extcodesize(account) }
                      return size > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{ value: amount }("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain`call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionCall(target, data, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      require(isContract(target), "Address: call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.call{ value: value }(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
                      require(isContract(target), "Address: static call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                      require(isContract(target), "Address: delegate call to non-contract");
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return _verifyCallResult(success, returndata, errorMessage);
                  }
                  function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              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 () {
                      address msgSender = _msgSender();
                      _owner = msgSender;
                      emit OwnershipTransferred(address(0), msgSender);
                  }
                  /**
                   * @dev Returns the address of the current owner.
                   */
                  function owner() public view virtual returns (address) {
                      return _owner;
                  }
                  /**
                   * @dev Throws if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      require(owner() == _msgSender(), "Ownable: caller is not the owner");
                      _;
                  }
                  /**
                   * @dev Leaves the contract without owner. It will not be possible to call
                   * `onlyOwner` functions anymore. Can only be called by the current owner.
                   *
                   * NOTE: Renouncing ownership will leave the contract without an owner,
                   * thereby removing any functionality that is only available to the owner.
                   */
                  function renounceOwnership() public virtual onlyOwner {
                      emit OwnershipTransferred(_owner, address(0));
                      _owner = address(0);
                  }
                  /**
                   * @dev Transfers ownership of the contract to a new account (`newOwner`).
                   * Can only be called by the current owner.
                   */
                  function transferOwnership(address newOwner) public virtual onlyOwner {
                      require(newOwner != address(0), "Ownable: new owner is the zero address");
                      emit OwnershipTransferred(_owner, newOwner);
                      _owner = newOwner;
                  }
              }
              // SPDX-License-Identifier: MIT
              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) {
                      this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                      return msg.data;
                  }
              }
              

              File 27 of 30: BeefyMultiStratHarvester
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              
              interface IStrategy {
                  function harvest(address _to) external; 
              }
              
              interface IERC20 {
                  function balanceOf(address _user) external view returns (uint256);
              }
              
              contract BeefyMultiStratHarvester {
              
                  struct Config {
                      uint256 lowerWaitForExec;
                      uint256 upperWaitForExec;
                      uint256 gasPriceLimit;
                      uint256 lowerTvlLimit;
                      uint256 upperTvlLimit;
                  }
              
                  Config public config;
              
                  address[] public problemStrats;
              
                  address public beefyTreasury; 
                  address public owner;
                  address public weth;
              
                  event Harvest(address[] indexed strats, uint256 time);
              
                  constructor(
                      address _treasury,
                      address _weth
                  ) {
                      beefyTreasury = _treasury;
                      weth = _weth;
                      owner = msg.sender;
              
                      problemStrats.push(0xE31e2e731DE87FfD0eCE2cE2f486E9095C8eBE63);
                  }
              
                  modifier onlyOwner {
                       require(msg.sender == owner, "!Owner");
                       _;
                  }
              
                  function encodeData(address one, address two, address three, uint num) external pure returns (bytes memory) {
                      if (num == 1) {
                          return abi.encode(one);
                      } else if (num == 2) {
                          return abi.encode(one, two);
                      } else {
                          return abi.encode(one, two, three);
                      }
                  }
              
                  function decodeData(bytes memory _data, uint num) internal pure returns (address[] memory) {
                      if (num == 1) {
                          address[] memory strats = new address[](num);
                          address decodedAddress = abi.decode(_data, (address));
                          strats[0] = decodedAddress;
                          return strats;
                      } else if (num == 2) {
                          address[] memory strats = new address[](num);
                          (address one, address two) = abi.decode(_data, (address, address));
                          strats[0] = one;
                          strats[1] = two;
                          return strats;
                      } else {
                          (address one, address two, address three) = abi.decode(_data, (address, address, address));
                          address[] memory strats = new address[](num);
                          strats[0] = one;
                          strats[1] = two;
                          strats[2] = three;
                          return strats;
                      }
                  }
              
                  function harvestMultiple(bytes memory _data, uint num) external {
                      address[] memory strats = decodeData(_data, num);
                      
                      for (uint i; i < strats.length;) {
                          uint256 before = IERC20(weth).balanceOf(beefyTreasury);
                          try IStrategy(strats[i]).harvest(beefyTreasury) {
                              uint256 diff = IERC20(weth).balanceOf(beefyTreasury) - before;
                              if (diff == 0) problemStrats.push(strats[i]);
                          } catch {
                              problemStrats.push(strats[i]);
                          }
                          unchecked { ++i; }
                      }
              
                      emit Harvest(strats, block.timestamp);
                  }
              
                  function cleanProblemStrats() external onlyOwner {
                      delete problemStrats;
                  }
              
                  function problems() external view returns (address[] memory) {
                      return problemStrats;
                  }
              
                  function transferOwnership(address _newOwner) external onlyOwner {
                      owner = _newOwner;
                  }
              
                  function setTreasury(address _treasury) external onlyOwner {
                      beefyTreasury = _treasury;
                  }
              
                  // Sets harvester configuration
                  function setConfig(Config calldata _config) external onlyOwner {
                       config = _config;
                  }
              }

              File 28 of 30: EIP173Proxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.7.0;
              import "./Proxy.sol";
              interface ERC165 {
                  function supportsInterface(bytes4 id) external view returns (bool);
              }
              ///@notice Proxy implementing EIP173 for ownership management
              contract EIP173Proxy is Proxy {
                  // ////////////////////////// EVENTS ///////////////////////////////////////////////////////////////////////
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  // /////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////
                  constructor(
                      address implementationAddress,
                      address ownerAddress,
                      bytes memory data
                  ) payable {
                      _setImplementation(implementationAddress, data);
                      _setOwner(ownerAddress);
                  }
                  // ///////////////////// EXTERNAL ///////////////////////////////////////////////////////////////////////////
                  function owner() external view returns (address) {
                      return _owner();
                  }
                  function supportsInterface(bytes4 id) external view returns (bool) {
                      if (id == 0x01ffc9a7 || id == 0x7f5828d0) {
                          return true;
                      }
                      if (id == 0xFFFFFFFF) {
                          return false;
                      }
                      ERC165 implementation;
                      // solhint-disable-next-line security/no-inline-assembly
                      assembly {
                          implementation := sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
                      }
                      // Technically this is not standard compliant as ERC-165 require 30,000 gas which that call cannot ensure
                      // because it is itself inside `supportsInterface` that might only get 30,000 gas.
                      // In practise this is unlikely to be an issue.
                      try implementation.supportsInterface(id) returns (bool support) {
                          return support;
                      } catch {
                          return false;
                      }
                  }
                  function transferOwnership(address newOwner) external onlyOwner {
                      _setOwner(newOwner);
                  }
                  function upgradeTo(address newImplementation) external onlyOwner {
                      _setImplementation(newImplementation, "");
                  }
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable onlyOwner {
                      _setImplementation(newImplementation, data);
                  }
                  // /////////////////////// MODIFIERS ////////////////////////////////////////////////////////////////////////
                  modifier onlyOwner() {
                      require(msg.sender == _owner(), "NOT_AUTHORIZED");
                      _;
                  }
                  // ///////////////////////// INTERNAL //////////////////////////////////////////////////////////////////////
                  function _owner() internal view returns (address adminAddress) {
                      // solhint-disable-next-line security/no-inline-assembly
                      assembly {
                          adminAddress := sload(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103)
                      }
                  }
                  function _setOwner(address newOwner) internal {
                      address previousOwner = _owner();
                      // solhint-disable-next-line security/no-inline-assembly
                      assembly {
                          sstore(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103, newOwner)
                      }
                      emit OwnershipTransferred(previousOwner, newOwner);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.7.0;
              // EIP-1967
              abstract contract Proxy {
                  // /////////////////////// EVENTS ///////////////////////////////////////////////////////////////////////////
                  event ProxyImplementationUpdated(address indexed previousImplementation, address indexed newImplementation);
                  // ///////////////////// EXTERNAL ///////////////////////////////////////////////////////////////////////////
                  receive() external payable virtual {
                      revert("ETHER_REJECTED"); // explicit reject by default
                  }
                  fallback() external payable {
                      _fallback();
                  }
                  // ///////////////////////// INTERNAL //////////////////////////////////////////////////////////////////////
                  function _fallback() internal {
                      // solhint-disable-next-line security/no-inline-assembly
                      assembly {
                          let implementationAddress := sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
                          calldatacopy(0x0, 0x0, calldatasize())
                          let success := delegatecall(gas(), implementationAddress, 0x0, calldatasize(), 0, 0)
                          let retSz := returndatasize()
                          returndatacopy(0, 0, retSz)
                          switch success
                              case 0 {
                                  revert(0, retSz)
                              }
                              default {
                                  return(0, retSz)
                              }
                      }
                  }
                  function _setImplementation(address newImplementation, bytes memory data) internal {
                      address previousImplementation;
                      // solhint-disable-next-line security/no-inline-assembly
                      assembly {
                          previousImplementation := sload(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)
                      }
                      // solhint-disable-next-line security/no-inline-assembly
                      assembly {
                          sstore(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, newImplementation)
                      }
                      emit ProxyImplementationUpdated(previousImplementation, newImplementation);
                      if (data.length > 0) {
                          (bool success, ) = newImplementation.delegatecall(data);
                          if (!success) {
                              assembly {
                                  // This assembly ensure the revert contains the exact string data
                                  let returnDataSize := returndatasize()
                                  returndatacopy(0, 0, returnDataSize)
                                  revert(0, returnDataSize)
                              }
                          }
                      }
                  }
              }
              

              File 29 of 30: Automate
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  /**
                   * @dev Returns the amount of tokens in existence.
                   */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @dev Returns the amount of tokens owned by `account`.
                   */
                  function balanceOf(address account) external view returns (uint256);
                  /**
                   * @dev Moves `amount` tokens from the caller's account to `to`.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transfer(address to, uint256 amount) external returns (bool);
                  /**
                   * @dev Returns the remaining number of tokens that `spender` will be
                   * allowed to spend on behalf of `owner` through {transferFrom}. This is
                   * zero by default.
                   *
                   * This value changes when {approve} or {transferFrom} are called.
                   */
                  function allowance(address owner, address spender) external view returns (uint256);
                  /**
                   * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * IMPORTANT: Beware that changing an allowance with this method brings the risk
                   * that someone may use both the old and the new allowance by unfortunate
                   * transaction ordering. One possible solution to mitigate this race
                   * condition is to first reduce the spender's allowance to 0 and set the
                   * desired value afterwards:
                   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                   *
                   * Emits an {Approval} event.
                   */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                   * @dev Moves `amount` tokens from `from` to `to` using the
                   * allowance mechanism. `amount` is then deducted from the caller's
                   * allowance.
                   *
                   * Returns a boolean value indicating whether the operation succeeded.
                   *
                   * Emits a {Transfer} event.
                   */
                  function transferFrom(
                      address from,
                      address to,
                      uint256 amount
                  ) external returns (bool);
                  /**
                   * @dev Emitted when `value` tokens are moved from one account (`from`) to
                   * another (`to`).
                   *
                   * Note that `value` may be zero.
                   */
                  event Transfer(address indexed from, address indexed to, uint256 value);
                  /**
                   * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                   * a call to {approve}. `value` is the new allowance.
                   */
                  event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)
              pragma solidity ^0.8.0;
              import "../IERC20.sol";
              import "../../../utils/Address.sol";
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                  using Address for address;
                  function safeTransfer(
                      IERC20 token,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 value
                  ) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                  }
                  /**
                   * @dev Deprecated. This function has issues similar to the ones found in
                   * {IERC20-approve}, and its usage is discouraged.
                   *
                   * Whenever possible, use {safeIncreaseAllowance} and
                   * {safeDecreaseAllowance} instead.
                   */
                  function safeApprove(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      // safeApprove should only be called when setting an initial allowance,
                      // or when resetting it to zero. To increase and decrease it, use
                      // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                      require(
                          (value == 0) || (token.allowance(address(this), spender) == 0),
                          "SafeERC20: approve from non-zero to non-zero allowance"
                      );
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                  }
                  function safeIncreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      uint256 newAllowance = token.allowance(address(this), spender) + value;
                      _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                  }
                  function safeDecreaseAllowance(
                      IERC20 token,
                      address spender,
                      uint256 value
                  ) internal {
                      unchecked {
                          uint256 oldAllowance = token.allowance(address(this), spender);
                          require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
                          uint256 newAllowance = oldAllowance - value;
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                      }
                  }
                  /**
                   * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                   * on the return value: the return value is optional (but if data is returned, it must not be false).
                   * @param token The token targeted by the call.
                   * @param data The call data (encoded using abi.encode or one of its variants).
                   */
                  function _callOptionalReturn(IERC20 token, bytes memory data) private {
                      // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                      // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                      // the target address contains contract code and also asserts for success in the low-level call.
                      bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                      if (returndata.length > 0) {
                          // Return data is optional
                          require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 0;
                  }
                  /**
                   * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                   * `recipient`, forwarding all available gas and reverting on errors.
                   *
                   * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                   * of certain opcodes, possibly making contracts go over the 2300 gas limit
                   * imposed by `transfer`, making them unable to receive funds via
                   * `transfer`. {sendValue} removes this limitation.
                   *
                   * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                   *
                   * IMPORTANT: because control is transferred to `recipient`, care must be
                   * taken to not create reentrancy vulnerabilities. Consider using
                   * {ReentrancyGuard} or the
                   * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                   */
                  function sendValue(address payable recipient, uint256 amount) internal {
                      require(address(this).balance >= amount, "Address: insufficient balance");
                      (bool success, ) = recipient.call{value: amount}("");
                      require(success, "Address: unable to send value, recipient may have reverted");
                  }
                  /**
                   * @dev Performs a Solidity function call using a low level `call`. A
                   * plain `call` is an unsafe replacement for a function call: use this
                   * function instead.
                   *
                   * If `target` reverts with a revert reason, it is bubbled up by this
                   * function (like regular Solidity function calls).
                   *
                   * Returns the raw returned data. To convert to the expected return value,
                   * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                   *
                   * Requirements:
                   *
                   * - `target` must be a contract.
                   * - calling `target` with `data` must not revert.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionCall(target, data, "Address: low-level call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                   * `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, 0, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but also transferring `value` wei to `target`.
                   *
                   * Requirements:
                   *
                   * - the calling contract must have an ETH balance of at least `value`.
                   * - the called Solidity function must be `payable`.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value
                  ) internal returns (bytes memory) {
                      return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                   * with `errorMessage` as a fallback revert reason when `target` reverts.
                   *
                   * _Available since v3.1._
                   */
                  function functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 value,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(address(this).balance >= value, "Address: insufficient balance for call");
                      require(isContract(target), "Address: call to non-contract");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                      return functionStaticCall(target, data, "Address: low-level static call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a static call.
                   *
                   * _Available since v3.3._
                   */
                  function functionStaticCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal view returns (bytes memory) {
                      require(isContract(target), "Address: static call to non-contract");
                      (bool success, bytes memory returndata) = target.staticcall(data);
                      return verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                      return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                  }
                  /**
                   * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
                   * but performing a delegate call.
                   *
                   * _Available since v3.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(isContract(target), "Address: delegate call to non-contract");
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal pure returns (bytes memory) {
                      if (success) {
                          return returndata;
                      } else {
                          // Look for revert reason and bubble it up if present
                          if (returndata.length > 0) {
                              // The easiest way to bubble the revert reason is using memory via assembly
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Library for managing
               * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
               * types.
               *
               * Sets have the following properties:
               *
               * - Elements are added, removed, and checked for existence in constant time
               * (O(1)).
               * - Elements are enumerated in O(n). No guarantees are made on the ordering.
               *
               * ```
               * contract Example {
               *     // Add the library methods
               *     using EnumerableSet for EnumerableSet.AddressSet;
               *
               *     // Declare a set state variable
               *     EnumerableSet.AddressSet private mySet;
               * }
               * ```
               *
               * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
               * and `uint256` (`UintSet`) are supported.
               */
              library EnumerableSet {
                  // To implement this library for multiple types with as little code
                  // repetition as possible, we write it in terms of a generic Set type with
                  // bytes32 values.
                  // The Set implementation uses private functions, and user-facing
                  // implementations (such as AddressSet) are just wrappers around the
                  // underlying Set.
                  // This means that we can only create new EnumerableSets for types that fit
                  // in bytes32.
                  struct Set {
                      // Storage of set values
                      bytes32[] _values;
                      // Position of the value in the `values` array, plus 1 because index 0
                      // means a value is not in the set.
                      mapping(bytes32 => uint256) _indexes;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function _add(Set storage set, bytes32 value) private returns (bool) {
                      if (!_contains(set, value)) {
                          set._values.push(value);
                          // The value is stored at length-1, but we add 1 to all indexes
                          // and use 0 as a sentinel value
                          set._indexes[value] = set._values.length;
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function _remove(Set storage set, bytes32 value) private returns (bool) {
                      // We read and store the value's index to prevent multiple reads from the same storage slot
                      uint256 valueIndex = set._indexes[value];
                      if (valueIndex != 0) {
                          // Equivalent to contains(set, value)
                          // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
                          // the array, and then remove the last element (sometimes called as 'swap and pop').
                          // This modifies the order of the array, as noted in {at}.
                          uint256 toDeleteIndex = valueIndex - 1;
                          uint256 lastIndex = set._values.length - 1;
                          if (lastIndex != toDeleteIndex) {
                              bytes32 lastvalue = set._values[lastIndex];
                              // Move the last value to the index where the value to delete is
                              set._values[toDeleteIndex] = lastvalue;
                              // Update the index for the moved value
                              set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
                          }
                          // Delete the slot where the moved value was stored
                          set._values.pop();
                          // Delete the index for the deleted slot
                          delete set._indexes[value];
                          return true;
                      } else {
                          return false;
                      }
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function _contains(Set storage set, bytes32 value) private view returns (bool) {
                      return set._indexes[value] != 0;
                  }
                  /**
                   * @dev Returns the number of values on the set. O(1).
                   */
                  function _length(Set storage set) private view returns (uint256) {
                      return set._values.length;
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function _at(Set storage set, uint256 index) private view returns (bytes32) {
                      return set._values[index];
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function _values(Set storage set) private view returns (bytes32[] memory) {
                      return set._values;
                  }
                  // Bytes32Set
                  struct Bytes32Set {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                      return _add(set._inner, value);
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
                      return _remove(set._inner, value);
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
                      return _contains(set._inner, value);
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(Bytes32Set storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
                      return _at(set._inner, index);
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
                      return _values(set._inner);
                  }
                  // AddressSet
                  struct AddressSet {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(AddressSet storage set, address value) internal returns (bool) {
                      return _add(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(AddressSet storage set, address value) internal returns (bool) {
                      return _remove(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(AddressSet storage set, address value) internal view returns (bool) {
                      return _contains(set._inner, bytes32(uint256(uint160(value))));
                  }
                  /**
                   * @dev Returns the number of values in the set. O(1).
                   */
                  function length(AddressSet storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(AddressSet storage set, uint256 index) internal view returns (address) {
                      return address(uint160(uint256(_at(set._inner, index))));
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(AddressSet storage set) internal view returns (address[] memory) {
                      bytes32[] memory store = _values(set._inner);
                      address[] memory result;
                      assembly {
                          result := store
                      }
                      return result;
                  }
                  // UintSet
                  struct UintSet {
                      Set _inner;
                  }
                  /**
                   * @dev Add a value to a set. O(1).
                   *
                   * Returns true if the value was added to the set, that is if it was not
                   * already present.
                   */
                  function add(UintSet storage set, uint256 value) internal returns (bool) {
                      return _add(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Removes a value from a set. O(1).
                   *
                   * Returns true if the value was removed from the set, that is if it was
                   * present.
                   */
                  function remove(UintSet storage set, uint256 value) internal returns (bool) {
                      return _remove(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Returns true if the value is in the set. O(1).
                   */
                  function contains(UintSet storage set, uint256 value) internal view returns (bool) {
                      return _contains(set._inner, bytes32(value));
                  }
                  /**
                   * @dev Returns the number of values on the set. O(1).
                   */
                  function length(UintSet storage set) internal view returns (uint256) {
                      return _length(set._inner);
                  }
                  /**
                   * @dev Returns the value stored at position `index` in the set. O(1).
                   *
                   * Note that there are no guarantees on the ordering of values inside the
                   * array, and it may change when more values are added or removed.
                   *
                   * Requirements:
                   *
                   * - `index` must be strictly less than {length}.
                   */
                  function at(UintSet storage set, uint256 index) internal view returns (uint256) {
                      return uint256(_at(set._inner, index));
                  }
                  /**
                   * @dev Return the entire set in an array
                   *
                   * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
                   * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
                   * this function has an unbounded cost, and using it as part of a state-changing function may render the function
                   * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
                   */
                  function values(UintSet storage set) internal view returns (uint256[] memory) {
                      bytes32[] memory store = _values(set._inner);
                      uint256[] memory result;
                      assembly {
                          result := store
                      }
                      return result;
                  }
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.14;
              import {
                  EnumerableSet
              } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
              import {Gelatofied} from "./vendor/gelato/Gelatofied.sol";
              import {GelatoBytes} from "./vendor/gelato/GelatoBytes.sol";
              import {Proxied} from "./vendor/proxy/EIP173/Proxied.sol";
              import {AutomateStorage} from "./AutomateStorage.sol";
              import {LibDataTypes} from "./libraries/LibDataTypes.sol";
              import {LibEvents} from "./libraries/LibEvents.sol";
              import {LibTaskId} from "./libraries/LibTaskId.sol";
              import {LibTaskModule} from "./libraries/LibTaskModule.sol";
              import {LibBypassModule} from "./libraries/LibBypassModule.sol";
              import {IAutomate} from "./interfaces/IAutomate.sol";
              /**
               * @notice Automate enables everyone to have Gelato monitor and execute transactions.
               * @notice ExecAddress refers to the contract that has the function which Gelato will call.
               * @notice Modules allow users to customise conditions and specifications when creating a task.
               */
              //solhint-disable function-max-lines
              //solhint-disable no-empty-blocks
              contract Automate is Gelatofied, Proxied, AutomateStorage, IAutomate {
                  using GelatoBytes for bytes;
                  using EnumerableSet for EnumerableSet.Bytes32Set;
                  // solhint-disable const-name-snakecase
                  string public constant version = "7";
                  constructor(address payable _gelato) Gelatofied(_gelato) {}
                  ///@inheritdoc IAutomate
                  function createTask(
                      address _execAddress,
                      bytes calldata _execDataOrSelector,
                      LibDataTypes.ModuleData calldata _moduleData,
                      address _feeToken
                  ) external override returns (bytes32 taskId) {
                      address taskCreator;
                      (taskCreator, _execAddress) = LibTaskModule.preCreateTask(
                          msg.sender,
                          _execAddress,
                          taskModuleAddresses
                      );
                      taskId = _createTask(
                          taskCreator,
                          _execAddress,
                          _execDataOrSelector,
                          _moduleData,
                          _feeToken
                      );
                  }
                  ///@inheritdoc IAutomate
                  function cancelTask(bytes32 _taskId) external {
                      address _taskCreator = LibTaskModule.preCancelTask(
                          _taskId,
                          msg.sender,
                          taskModuleAddresses
                      );
                      _cancelTask(_taskCreator, _taskId);
                  }
                  ///@inheritdoc IAutomate
                  function exec(
                      address _taskCreator,
                      address _execAddress,
                      bytes memory _execData,
                      LibDataTypes.ModuleData calldata _moduleData,
                      uint256 _txFee,
                      address _feeToken,
                      bool _revertOnFailure
                  ) external onlyGelato {
                      bytes32 taskId = LibTaskId.getTaskId(
                          _taskCreator,
                          _execAddress,
                          _execData.memorySliceSelector(),
                          _moduleData,
                          _feeToken
                      );
                      require(
                          _createdTasks[_taskCreator].contains(taskId),
                          "Automate.exec: Task not found"
                      );
                      fee = _txFee;
                      feeToken = _feeToken;
                      bool success = LibTaskModule.onExecTask(
                          taskId,
                          _taskCreator,
                          _execAddress,
                          _execData,
                          _moduleData.modules,
                          _revertOnFailure,
                          taskModuleAddresses
                      );
                      delete fee;
                      delete feeToken;
                      emit LibEvents.ExecSuccess(
                          _txFee,
                          _feeToken,
                          _execAddress,
                          _execData,
                          taskId,
                          success
                      );
                  }
                  ///@inheritdoc IAutomate
                  function exec1Balance(
                      address _taskCreator,
                      address _execAddress,
                      bytes memory _execData,
                      LibDataTypes.ModuleData calldata _moduleData,
                      Gelato1BalanceParam calldata _oneBalanceParam,
                      bool _revertOnFailure
                  ) external onlyGelato {
                      bytes32 taskId = LibTaskId.getTaskId(
                          _taskCreator,
                          _execAddress,
                          _execData.memorySliceSelector(),
                          _moduleData,
                          address(0)
                      );
                      require(
                          _createdTasks[_taskCreator].contains(taskId),
                          "Automate.exec: Task not found"
                      );
                      bool success = LibTaskModule.onExecTask(
                          taskId,
                          _taskCreator,
                          _execAddress,
                          _execData,
                          _moduleData.modules,
                          _revertOnFailure,
                          taskModuleAddresses
                      );
                      emit LibEvents.ExecSuccess(
                          0,
                          address(0),
                          _execAddress,
                          _execData,
                          taskId,
                          success
                      );
                      emit LogUseGelato1Balance(
                          _oneBalanceParam.sponsor,
                          _execAddress,
                          _oneBalanceParam.feeToken,
                          _oneBalanceParam.oneBalanceChainId,
                          _oneBalanceParam.nativeToFeeTokenXRateNumerator,
                          _oneBalanceParam.nativeToFeeTokenXRateDenominator,
                          _oneBalanceParam.correlationId
                      );
                  }
                  function execBypassModuleSyncFee(
                      address _taskCreator,
                      address _execAddress,
                      bytes32 _taskId,
                      uint256 _txFee,
                      address _feeToken,
                      bytes memory _execData,
                      bool _revertOnFailure,
                      bool _singleExec
                  ) external onlyGelato {
                      require(
                          _createdTasks[_taskCreator].contains(_taskId),
                          "Automate.exec: Task not found"
                      );
                      fee = _txFee;
                      feeToken = _feeToken;
                      bool success = LibBypassModule.onExecTask(
                          _taskId,
                          _taskCreator,
                          _execAddress,
                          _execData,
                          _revertOnFailure,
                          _singleExec,
                          _createdTasks
                      );
                      delete fee;
                      delete feeToken;
                      emit LibEvents.ExecBypassModuleSyncFeeSuccess(
                          _taskId,
                          _txFee,
                          _feeToken,
                          success
                      );
                  }
                  ///@inheritdoc IAutomate
                  function execBypassModule(
                      address _taskCreator,
                      address _execAddress,
                      bytes32 _taskId,
                      bytes32 _correlationId,
                      bytes memory _execData,
                      bool _revertOnFailure,
                      bool _singleExec
                  ) external onlyGelato {
                      require(
                          _createdTasks[_taskCreator].contains(_taskId),
                          "Automate.exec: Task not found"
                      );
                      bool success = LibBypassModule.onExecTask(
                          _taskId,
                          _taskCreator,
                          _execAddress,
                          _execData,
                          _revertOnFailure,
                          _singleExec,
                          _createdTasks
                      );
                      emit LibEvents.ExecBypassModuleSuccess(
                          _taskId,
                          _correlationId,
                          success
                      );
                  }
                  ///@inheritdoc IAutomate
                  function setModule(
                      LibDataTypes.Module[] calldata _modules,
                      address[] calldata _moduleAddresses
                  ) external onlyProxyAdmin {
                      uint256 length = _modules.length;
                      for (uint256 i; i < length; i++) {
                          taskModuleAddresses[_modules[i]] = _moduleAddresses[i];
                      }
                  }
                  ///@inheritdoc IAutomate
                  function getFeeDetails() external view returns (uint256, address) {
                      return (fee, feeToken);
                  }
                  ///@inheritdoc IAutomate
                  function getTaskIdsByUser(address _taskCreator)
                      external
                      view
                      returns (bytes32[] memory)
                  {
                      bytes32[] memory taskIds = _createdTasks[_taskCreator].values();
                      return taskIds;
                  }
                  ///@inheritdoc IAutomate
                  function getTaskId(
                      address taskCreator,
                      address execAddress,
                      bytes4 execSelector,
                      LibDataTypes.ModuleData memory moduleData,
                      address feeToken
                  ) external pure returns (bytes32 taskId) {
                      taskId = LibTaskId.getTaskId(
                          taskCreator,
                          execAddress,
                          execSelector,
                          moduleData,
                          feeToken
                      );
                  }
                  function _createTask(
                      address _taskCreator,
                      address _execAddress,
                      bytes memory _execDataOrSelector,
                      LibDataTypes.ModuleData memory _moduleData,
                      address _feeToken
                  ) private returns (bytes32 taskId) {
                      taskId = LibTaskId.getTaskId(
                          _taskCreator,
                          _execAddress,
                          _execDataOrSelector.memorySliceSelector(),
                          _moduleData,
                          _feeToken
                      );
                      require(
                          !_createdTasks[_taskCreator].contains(taskId),
                          "Automate.createTask: Duplicate task"
                      );
                      LibTaskModule.onCreateTask(
                          taskId,
                          _taskCreator,
                          _execAddress,
                          _execDataOrSelector,
                          _moduleData,
                          taskModuleAddresses
                      );
                      _createdTasks[_taskCreator].add(taskId);
                      emit LibEvents.TaskCreated(
                          _taskCreator,
                          _execAddress,
                          _execDataOrSelector,
                          _moduleData,
                          _feeToken,
                          taskId
                      );
                  }
                  function _cancelTask(address _taskCreator, bytes32 _taskId) private {
                      require(
                          _createdTasks[_taskCreator].contains(_taskId),
                          "Automate.cancelTask: Task not found"
                      );
                      _createdTasks[_taskCreator].remove(_taskId);
                      emit LibEvents.TaskCancelled(_taskId, _taskCreator);
                  }
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.12;
              import {
                  EnumerableSet
              } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
              import {LibDataTypes} from "./libraries/LibDataTypes.sol";
              /**
               * @notice Storage layout of Automate smart contract.
               */
              // solhint-disable max-states-count
              abstract contract AutomateStorage {
                  mapping(bytes32 => address) public taskCreator; ///@dev Deprecated
                  mapping(bytes32 => address) public execAddresses; ///@dev Deprecated
                  mapping(address => EnumerableSet.Bytes32Set) internal _createdTasks;
                  uint256 public fee;
                  address public feeToken;
                  ///@dev Appended State
                  mapping(bytes32 => LibDataTypes.Time) public timedTask; ///@dev Deprecated
                  mapping(LibDataTypes.Module => address) public taskModuleAddresses;
                  mapping(bytes32 => uint256) public nonce1Balance; ///@dev Deprecated
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              import {GelatoBytes} from "../vendor/gelato/GelatoBytes.sol";
              // solhint-disable private-vars-leading-underscore
              // solhint-disable func-visibility
              function _call(
                  address _add,
                  bytes memory _data,
                  uint256 _value,
                  bool _revertOnFailure,
                  string memory _tracingInfo
              ) returns (bool success, bytes memory returnData) {
                  (success, returnData) = _add.call{value: _value}(_data);
                  if (!success && _revertOnFailure)
                      GelatoBytes.revertWithError(returnData, _tracingInfo);
              }
              function _delegateCall(
                  address _add,
                  bytes memory _data,
                  string memory _tracingInfo
              ) returns (bool success, bytes memory returnData) {
                  (success, returnData) = _add.delegatecall(_data);
                  if (!success) GelatoBytes.revertWithError(returnData, _tracingInfo);
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.12;
              import {
                  SafeERC20,
                  IERC20
              } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
              address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
              // solhint-disable private-vars-leading-underscore
              // solhint-disable func-visibility
              function _transfer(
                  address payable _to,
                  address _paymentToken,
                  uint256 _amount
              ) {
                  if (_paymentToken == ETH) {
                      (bool success, ) = _to.call{value: _amount}("");
                      require(success, "_transfer: ETH transfer failed");
                  } else {
                      SafeERC20.safeTransfer(IERC20(_paymentToken), _to, _amount);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import {LibDataTypes} from "../libraries/LibDataTypes.sol";
              import {IGelato1Balance} from "./IGelato1Balance.sol";
              // solhint-disable max-line-length
              interface IAutomate is IGelato1Balance {
                  /**
                   * @notice Initiates a task with conditions which Gelato will monitor and execute when conditions are met.
                   *
                   * @param execAddress Address of contract that should be called by Gelato.
                   * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
                   * @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
                   * @param feeToken Address of token to be used as payment. Use address(0) if Gelato 1Balance is being used, 0xeeeeee... for ETH or native tokens.
                   *
                   * @return taskId Unique hash of the task created.
                   */
                  function createTask(
                      address execAddress,
                      bytes calldata execData,
                      LibDataTypes.ModuleData calldata moduleData,
                      address feeToken
                  ) external returns (bytes32 taskId);
                  /**
                   * @notice Terminates a task that was created and Gelato can no longer execute it.
                   *
                   * @param taskId Unique hash of the task that is being cancelled. {See LibTaskId-getTaskId}
                   */
                  function cancelTask(bytes32 taskId) external;
                  /**
                   * @notice Execution API called by Gelato, using Sync Fee as fee payment method
                   *
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that should be called by Gelato.
                   * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
                   * @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
                   * @param txFee Fee paid to Gelato for execution, transfered to Gelato.feeCollector().
                   * @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
                   * @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
                   */
                  function exec(
                      address taskCreator,
                      address execAddress,
                      bytes memory execData,
                      LibDataTypes.ModuleData calldata moduleData,
                      uint256 txFee,
                      address feeToken,
                      bool revertOnFailure
                  ) external;
                  /**
                   * @notice Execution API called by Gelato, using Gelato 1Balance as fee payment method.
                   *
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that should be called by Gelato.
                   * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
                   * @param moduleData Conditional modules that will be used. {See LibDataTypes-ModuleData}
                   * @param oneBalanceParam Parameters required for fee payment with Gelato 1Balance.
                   * @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
                   */
                  function exec1Balance(
                      address taskCreator,
                      address execAddress,
                      bytes memory execData,
                      LibDataTypes.ModuleData calldata moduleData,
                      Gelato1BalanceParam calldata oneBalanceParam,
                      bool revertOnFailure
                  ) external;
                  /**
                   * @notice Execution API called by Gelato, using Gelato 1Balance as fee payment method.
                   *
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that should be called by Gelato.
                   * @param taskId Unique hash of the task.
                   * @param correlationId Id of the execution to be used for 1Balance settlement.
                   * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
                   * @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
                   * @param singleExec If the task is a SingleExec task. If true, task will be cancelled after execution.
                   */
                  function execBypassModule(
                      address taskCreator,
                      address execAddress,
                      bytes32 taskId,
                      bytes32 correlationId,
                      bytes memory execData,
                      bool revertOnFailure,
                      bool singleExec
                  ) external;
                  /**
                   * @notice Execution API called by Gelato, using Gelato Sync fee as fee payment method.
                   *
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that should be called by Gelato.
                   * @param taskId Unique hash of the task.
                   * @param txFee Fee paid to Gelato for execution, transfered to Gelato.feeCollector().
                   * @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
                   * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
                   * @param revertOnFailure To revert or not if call to execAddress fails. (Used for off-chain simulations)
                   * @param singleExec If the task is a SingleExec task. If true, task will be cancelled after execution.
                   */
                  function execBypassModuleSyncFee(
                      address taskCreator,
                      address execAddress,
                      bytes32 taskId,
                      uint256 txFee,
                      address feeToken,
                      bytes memory execData,
                      bool revertOnFailure,
                      bool singleExec
                  ) external;
                  /**
                   * @notice Sets the address of task modules. Only callable by proxy admin.
                   *
                   * @param modules List of modules to be set
                   * @param moduleAddresses List of addresses for respective modules.
                   */
                  function setModule(
                      LibDataTypes.Module[] calldata modules,
                      address[] calldata moduleAddresses
                  ) external;
                  /**
                   * @notice Helper function to query fee and feeToken to be used for payment. (For executions which pays itself)
                   *
                   * @return uint256 Fee amount to be paid.
                   * @return address Token to be paid. (Determined and passed by taskCreator during createTask)
                   */
                  function getFeeDetails() external view returns (uint256, address);
                  /**
                   * @notice Helper func to query all open tasks by a task creator.
                   *
                   * @param taskCreator Address of task creator to query.
                   *
                   * @return bytes32[] List of taskIds created.
                   */
                  function getTaskIdsByUser(address taskCreator)
                      external
                      view
                      returns (bytes32[] memory);
                  /**
                   * @notice Helper function to compute task id with module arguments
                   *
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that will be called by Gelato.
                   * @param execSelector Signature of the function which will be called by Gelato.
                   * @param moduleData  Conditional modules that will be used. {See LibDataTypes-ModuleData}
                   * @param feeToken Address of token to be used as payment. Use address(0) if Gelato 1Balance is being used, 0xeeeeee... for ETH or native tokens.
                   */
                  function getTaskId(
                      address taskCreator,
                      address execAddress,
                      bytes4 execSelector,
                      LibDataTypes.ModuleData memory moduleData,
                      address feeToken
                  ) external pure returns (bytes32 taskId);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              interface IGelato1Balance {
                  struct Gelato1BalanceParam {
                      address sponsor;
                      address feeToken;
                      uint256 oneBalanceChainId;
                      uint256 nativeToFeeTokenXRateNumerator;
                      uint256 nativeToFeeTokenXRateDenominator;
                      bytes32 correlationId;
                  }
                  event LogUseGelato1Balance(
                      address indexed sponsor,
                      address indexed target,
                      address indexed feeToken,
                      uint256 oneBalanceChainId,
                      uint256 nativeToFeeTokenXRateNumerator,
                      uint256 nativeToFeeTokenXRateDenominator,
                      bytes32 correlationId
                  );
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              // solhint-disable max-line-length
              interface ITaskModule {
                  /**
                   * @notice Called before generating taskId.
                   * @dev Modules can override execAddress or taskCreator. {See ProxyModule-preCreateTask}
                   *
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that should be called.
                   *
                   * @return address Overriden or original taskCreator.
                   * @return address Overriden or original execAddress.
                   */
                  function preCreateTask(address taskCreator, address execAddress)
                      external
                      returns (address, address);
                  /**
                   * @notice Initiates task module whenever `createTask` is being called.
                   *
                   * @param taskId Unique hash of the task created.
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that should be called.
                   * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
                   * @param initModuleArg Encoded arguments for module if any.
                   */
                  function onCreateTask(
                      bytes32 taskId,
                      address taskCreator,
                      address execAddress,
                      bytes calldata execData,
                      bytes calldata initModuleArg
                  ) external;
                  /**
                   * @notice Called before taskId is removed from _createdTasks[].
                   * @dev Modules can override taskCreator.
                   *
                   * @param taskId Unique hash of the task created.
                   * @param taskCreator The address which created the task.
                   *
                   * @return address Overriden or original taskCreator.
                   */
                  function preCancelTask(bytes32 taskId, address taskCreator)
                      external
                      returns (address);
                  /**
                   * @notice Called during `exec` and before execAddress is called.
                   *
                   * @param taskId Unique hash of the task created.
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that should be called.
                   * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
                   *
                   * @return address Overriden or original execution address.
                   * @return bytes Overriden or original execution data.
                   */
                  function preExecCall(
                      bytes32 taskId,
                      address taskCreator,
                      address execAddress,
                      bytes calldata execData
                  ) external returns (address, bytes memory);
                  /**
                   * @notice Called during `exec` and after execAddress is called.
                   *
                   * @param taskId Unique hash of the task created.
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that should be called.
                   * @param execData Execution data to be called with / function selector if execution data is yet to be determined.
                   */
                  function postExecCall(
                      bytes32 taskId,
                      address taskCreator,
                      address execAddress,
                      bytes calldata execData
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              import {
                  EnumerableSet
              } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
              import {_call, _delegateCall} from "../functions/FExec.sol";
              import {LibDataTypes} from "./LibDataTypes.sol";
              import {LibEvents} from "./LibEvents.sol";
              import {LibTaskModule} from "./LibTaskModule.sol";
              import {LibTaskModuleConfig} from "./LibTaskModuleConfig.sol";
              import {ITaskModule} from "../interfaces/ITaskModule.sol";
              // solhint-disable function-max-lines
              /// @notice Simplified library for task executions
              library LibBypassModule {
                  using EnumerableSet for EnumerableSet.Bytes32Set;
                  using LibTaskModuleConfig for LibDataTypes.Module;
                  /**
                   * @notice Delegate calls SingleExecModule on exec for single exec tasks.
                   *
                   * @param _taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   * @param _taskCreator Address which created the task.
                   * @param _execAddress Address of contract that will be called by Gelato.
                   * @param _execData Execution data to be called with / function selector.
                   * @param _revertOnFailure To revert or not if call to execAddress fails.
                   * @param _singleExec If task is a single exec task.
                   * @param _createdTasks The storage reference of owner to the taskIds created mapping.
                   */
                  function onExecTask(
                      bytes32 _taskId,
                      address _taskCreator,
                      address _execAddress,
                      bytes memory _execData,
                      bool _revertOnFailure,
                      bool _singleExec,
                      mapping(address => EnumerableSet.Bytes32Set) storage _createdTasks
                  ) internal returns (bool callSuccess) {
                      (callSuccess, ) = _call(
                          _execAddress,
                          abi.encodePacked(_execData, _taskCreator),
                          0,
                          _revertOnFailure,
                          "Automate.exec: "
                      );
                      if (_singleExec) {
                          _createdTasks[_taskCreator].remove(_taskId);
                          emit LibEvents.TaskCancelled(_taskId, _taskCreator);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              // solhint-disable max-line-length
              library LibDataTypes {
                  /**
                   * @notice Whitelisted modules that are available for users to customise conditions and specifications of their tasks.
                   *
                   * @param RESOLVER Use dynamic condition & input data for execution. {See ResolverModule.sol}
                   * @param DEPRECATED_TIME deprecated
                   * @param PROXY Creates a dedicated caller (msg.sender) to be used when executing the task. {See ProxyModule.sol}
                   * @param SINGLE_EXEC Task is cancelled after one execution. {See SingleExecModule.sol}
                   * @param WEB3_FUNCTION Use off-chain condition & input data for execution. {See Web3FunctionModule.sol}
                   * @param TRIGGER Repeated execution of task ata a specified timing and interval or cron. {See TriggerModule.sol}
                   */
                  enum Module {
                      RESOLVER,
                      DEPRECATED_TIME, // @deprecated
                      PROXY,
                      SINGLE_EXEC,
                      WEB3_FUNCTION,
                      TRIGGER
                  }
                  /**
                   * @notice Struct to contain modules and their relative arguments that are used for task creation.
                   *
                   * @param modules List of selected modules.
                   * @param args Arguments of modules if any. Pass "0x" for modules which does not require args {See encodeModuleArg}
                   */
                  struct ModuleData {
                      Module[] modules;
                      bytes[] args;
                  }
                  /**
                   * @notice Struct for time module.
                   *
                   * @param nextExec Time when the next execution should occur.
                   * @param interval Time interval between each execution.
                   */
                  struct Time {
                      uint128 nextExec;
                      uint128 interval;
                  }
                  /**
                   * @notice Types of trigger
                   *
                   * @param TIME Time triggered tasks, starting at a specific time and triggered intervally
                   * @param CRON Cron triggered tasks, triggered according to the cron conditions
                   */
                  enum TriggerType {
                      TIME,
                      CRON,
                      EVENT,
                      BLOCK
                  }
                  /**
                   * @notice Struct for trigger module
                   *
                   * @param triggerType Type of the trigger
                   * @param triggerConfig Trigger configuration that shuold be parsed according to triggerType
                   */
                  struct TriggerModuleData {
                      TriggerType triggerType;
                      bytes triggerConfig;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              import {LibDataTypes} from "./LibDataTypes.sol";
              library LibEvents {
                  /**
                   * @notice Emitted when `createTask` is called.
                   *
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that is called by Gelato.
                   * @param execDataOrSelector Execution data / function selector.
                   * @param moduleData Conditional modules used. {See LibDataTypes-ModuleData}
                   * @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
                   * @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   */
                  event TaskCreated(
                      address indexed taskCreator,
                      address indexed execAddress,
                      bytes execDataOrSelector,
                      LibDataTypes.ModuleData moduleData,
                      address feeToken,
                      bytes32 indexed taskId
                  );
                  /**
                   * @notice Emitted when `cancelTask` is called.
                   *
                   * @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   * @param taskCreator The address which owned the task.
                   */
                  event TaskCancelled(bytes32 taskId, address taskCreator);
                  /**
                   * @notice Emitted when `exec` is called.
                   *
                   * @param txFee Fee paid to Gelato for execution
                   * @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
                   * @param execAddress Address of contract that will be called by Gelato.
                   * @param execData Execution data / function selector.
                   * @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   * @param callSuccess Status of the call to execAddress.
                   */
                  event ExecSuccess(
                      uint256 indexed txFee,
                      address indexed feeToken,
                      address indexed execAddress,
                      bytes execData,
                      bytes32 taskId,
                      bool callSuccess
                  );
                  /**
                   * @notice Emitted when `execBypassModule` is called.
                   *
                   * @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   * @param correlationId Id of the execution to be used for 1Balance settlement.
                   * @param callSuccess Status of the call to execAddress.
                   */
                  event ExecBypassModuleSuccess(
                      bytes32 taskId,
                      bytes32 correlationId,
                      bool callSuccess
                  );
                  /**
                   * @notice Emitted when `execBypassModuleSyncFee` is called.
                   *
                   * @param taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   * @param txFee Fee paid to Gelato for execution
                   * @param feeToken Token used to pay for the execution. ETH = 0xeeeeee...
                   * @param callSuccess Status of the call to execAddress.
                   */
                  event ExecBypassModuleSyncFeeSuccess(
                      bytes32 taskId,
                      uint256 txFee,
                      address feeToken,
                      bool callSuccess
                  );
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.0;
              import {LibDataTypes} from "./LibDataTypes.sol";
              /**
               * @notice Library to compute taskId of tasks.
               */
              // solhint-disable max-line-length
              library LibTaskId {
                  /**
                   * @notice Returns taskId of taskCreator.
                   *
                   * @param taskCreator The address which created the task.
                   * @param execAddress Address of contract that will be called by Gelato.
                   * @param execSelector Signature of the function which will be called by Gelato.
                   * @param moduleData  Conditional modules that will be used. {See LibDataTypes-ModuleData}
                   * @param feeToken Address of token to be used as payment. Use address(0) if Gelato 1Balance is being used, 0xeeeeee... for ETH or native tokens.
                   */
                  function getTaskId(
                      address taskCreator,
                      address execAddress,
                      bytes4 execSelector,
                      LibDataTypes.ModuleData memory moduleData,
                      address feeToken
                  ) internal pure returns (bytes32 taskId) {
                      taskId = keccak256(
                          abi.encode(
                              taskCreator,
                              execAddress,
                              execSelector,
                              moduleData,
                              feeToken
                          )
                      );
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              import {_call, _delegateCall} from "../functions/FExec.sol";
              import {LibDataTypes} from "./LibDataTypes.sol";
              import {LibTaskModuleConfig} from "./LibTaskModuleConfig.sol";
              import {ITaskModule} from "../interfaces/ITaskModule.sol";
              // solhint-disable function-max-lines
              /// @notice Library to call task modules on task creation and execution.
              library LibTaskModule {
                  using LibTaskModuleConfig for LibDataTypes.Module;
                  /**
                   * @notice Delegate calls task modules before generating taskId.
                   *
                   * @param _execAddress Address of contract that will be called by Gelato.
                   * @param _taskCreator The address which created the task.
                   * @param taskModuleAddresses The storage reference to the mapping of modules to their address.
                   */
                  function preCreateTask(
                      address _taskCreator,
                      address _execAddress,
                      mapping(LibDataTypes.Module => address) storage taskModuleAddresses
                  ) internal returns (address, address) {
                      uint256 length = uint256(type(LibDataTypes.Module).max) + 1;
                      for (uint256 i; i < length; i++) {
                          LibDataTypes.Module module = LibDataTypes.Module(i);
                          if (!module.requirePreCreate()) continue;
                          address moduleAddress = taskModuleAddresses[module];
                          _moduleInitialised(moduleAddress);
                          bytes memory delegatecallData = abi.encodeWithSelector(
                              ITaskModule.preCreateTask.selector,
                              _taskCreator,
                              _execAddress
                          );
                          (, bytes memory returnData) = _delegateCall(
                              moduleAddress,
                              delegatecallData,
                              "Automate.preCreateTask: "
                          );
                          (_taskCreator, _execAddress) = abi.decode(
                              returnData,
                              (address, address)
                          );
                      }
                      return (_taskCreator, _execAddress);
                  }
                  /**
                   * @notice Delegate calls task modules on create task to initialise them.
                   *
                   * @param _taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   * @param _taskCreator The address which created the task.
                   * @param _execAddress Address of contract that will be called by Gelato.
                   * @param _execData Execution data to be called with / function selector.
                   * @param _moduleData Modules that will be used for the task. {See LibDataTypes-ModuleData}
                   * @param taskModuleAddresses The storage reference to the mapping of modules to their address.
                   */
                  function onCreateTask(
                      bytes32 _taskId,
                      address _taskCreator,
                      address _execAddress,
                      bytes memory _execData,
                      LibDataTypes.ModuleData memory _moduleData,
                      mapping(LibDataTypes.Module => address) storage taskModuleAddresses
                  ) internal {
                      uint256 length = _moduleData.modules.length;
                      _validModules(_moduleData.modules);
                      for (uint256 i; i < length; i++) {
                          LibDataTypes.Module module = _moduleData.modules[i];
                          if (!module.requireOnCreate()) continue;
                          address moduleAddress = taskModuleAddresses[module];
                          _moduleInitialised(moduleAddress);
                          bytes memory delegatecallData = abi.encodeWithSelector(
                              ITaskModule.onCreateTask.selector,
                              _taskId,
                              _taskCreator,
                              _execAddress,
                              _execData,
                              _moduleData.args[i]
                          );
                          _delegateCall(
                              moduleAddress,
                              delegatecallData,
                              "Automate.onCreateTask: "
                          );
                      }
                  }
                  /**
                   * @notice Delegate calls task modules before removing task.
                   *
                   * @param _taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   * @param _taskCreator The address which created the task.
                   * @param taskModuleAddresses The storage reference to the mapping of modules to their address.
                   */
                  function preCancelTask(
                      bytes32 _taskId,
                      address _taskCreator,
                      mapping(LibDataTypes.Module => address) storage taskModuleAddresses
                  ) internal returns (address) {
                      uint256 length = uint256(type(LibDataTypes.Module).max);
                      for (uint256 i; i <= length; i++) {
                          LibDataTypes.Module module = LibDataTypes.Module(i);
                          if (!module.requirePreCancel()) continue;
                          address moduleAddress = taskModuleAddresses[module];
                          _moduleInitialised(moduleAddress);
                          bytes memory delegatecallData = abi.encodeWithSelector(
                              ITaskModule.preCancelTask.selector,
                              _taskId,
                              _taskCreator
                          );
                          (, bytes memory returnData) = _delegateCall(
                              moduleAddress,
                              delegatecallData,
                              "Automate.preCancelTask: "
                          );
                          (_taskCreator) = abi.decode(returnData, (address));
                      }
                      return _taskCreator;
                  }
                  /**
                   * @notice Delegate calls task modules on exec.
                   *
                   * @param _taskId Unique hash of the task. {See LibTaskId-getTaskId}
                   * @param _taskCreator Address which created the task.
                   * @param _execAddress Address of contract that will be called by Gelato.
                   * @param _execData Execution data to be called with / function selector.
                   * @param _modules Modules that is used for the task. {See LibDataTypes-Module}
                   * @param _revertOnFailure To revert or not if call to execAddress fails.
                   * @param taskModuleAddresses The storage reference to the mapping of modules to their address.
                   */
                  function onExecTask(
                      bytes32 _taskId,
                      address _taskCreator,
                      address _execAddress,
                      bytes memory _execData,
                      LibDataTypes.Module[] memory _modules,
                      bool _revertOnFailure,
                      mapping(LibDataTypes.Module => address) storage taskModuleAddresses
                  ) internal returns (bool callSuccess) {
                      address[] memory moduleAddresses = _getModuleAddresses(
                          _modules,
                          taskModuleAddresses
                      );
                      (_execAddress, _execData) = preExecCall(
                          _taskId,
                          _taskCreator,
                          _execAddress,
                          _execData,
                          _modules,
                          moduleAddresses
                      );
                      (callSuccess, ) = _call(
                          _execAddress,
                          abi.encodePacked(_execData, _taskCreator),
                          0,
                          _revertOnFailure,
                          "Automate.exec: "
                      );
                      postExecCall(
                          _taskId,
                          _taskCreator,
                          _execAddress,
                          _execData,
                          _modules,
                          moduleAddresses
                      );
                  }
                  function preExecCall(
                      bytes32 _taskId,
                      address _taskCreator,
                      address _execAddress,
                      bytes memory _execData,
                      LibDataTypes.Module[] memory _modules,
                      address[] memory _moduleAddresses
                  ) internal returns (address, bytes memory) {
                      uint256 length = _modules.length;
                      for (uint256 i; i < length; i++) {
                          if (!_modules[i].requirePreExec()) continue;
                          bytes memory delegatecallData = abi.encodeWithSelector(
                              ITaskModule.preExecCall.selector,
                              _taskId,
                              _taskCreator,
                              _execAddress,
                              _execData
                          );
                          (, bytes memory returnData) = _delegateCall(
                              _moduleAddresses[i],
                              delegatecallData,
                              "Automate.preExecCall: "
                          );
                          (_execAddress, _execData) = abi.decode(
                              returnData,
                              (address, bytes)
                          );
                      }
                      return (_execAddress, _execData);
                  }
                  function postExecCall(
                      bytes32 _taskId,
                      address _taskCreator,
                      address _execAddress,
                      bytes memory _execData,
                      LibDataTypes.Module[] memory _modules,
                      address[] memory _moduleAddresses
                  ) internal {
                      uint256 length = _moduleAddresses.length;
                      for (uint256 i; i < length; i++) {
                          if (!_modules[i].requirePostExec()) continue;
                          bytes memory delegatecallData = abi.encodeWithSelector(
                              ITaskModule.postExecCall.selector,
                              _taskId,
                              _taskCreator,
                              _execAddress,
                              _execData
                          );
                          _delegateCall(
                              _moduleAddresses[i],
                              delegatecallData,
                              "Automate.postExecCall: "
                          );
                      }
                  }
                  function _getModuleAddresses(
                      LibDataTypes.Module[] memory _modules,
                      mapping(LibDataTypes.Module => address) storage taskModuleAddresses
                  ) private view returns (address[] memory) {
                      uint256 length = _modules.length;
                      address[] memory moduleAddresses = new address[](length);
                      for (uint256 i; i < length; i++) {
                          moduleAddresses[i] = taskModuleAddresses[_modules[i]];
                      }
                      return moduleAddresses;
                  }
                  function _moduleInitialised(address _moduleAddress) private pure {
                      require(
                          _moduleAddress != address(0),
                          "Automate._moduleInitialised: Not init"
                      );
                  }
                  /**
                   * @dev
                   * - No duplicate modules
                   * - No deprecated TIME
                   * - No RESOLVER && WEB3_FUNCTION
                   * - PROXY is required
                   */
                  function _validModules(LibDataTypes.Module[] memory _modules) private pure {
                      uint256 length = _modules.length;
                      uint256 existsLength = uint256(type(LibDataTypes.Module).max) + 1;
                      bool[] memory exists = new bool[](existsLength);
                      for (uint256 i = 0; i < length; i++) {
                          if (i > 0) {
                              require(
                                  _modules[i] > _modules[i - 1],
                                  "Automate._validModules: Asc only"
                              );
                          }
                          exists[uint256(_modules[i])] = true;
                      }
                      require(
                          !exists[uint256(LibDataTypes.Module.DEPRECATED_TIME)],
                          "Automate._validModules: TIME is deprecated"
                      );
                      require(
                          !(exists[uint256(LibDataTypes.Module.RESOLVER)] &&
                              exists[uint256(LibDataTypes.Module.WEB3_FUNCTION)]),
                          "Automate._validModules: Only RESOLVER or WEB3_FUNCTION"
                      );
                      require(
                          exists[uint256(LibDataTypes.Module.PROXY)],
                          "Automate._validModules: PROXY is required"
                      );
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              import {LibDataTypes} from "./LibDataTypes.sol";
              /**
               * @notice Library to determine wether to call task modules to reduce unnecessary calls.
               */
              library LibTaskModuleConfig {
                  function requirePreCreate(LibDataTypes.Module _module)
                      internal
                      pure
                      returns (bool)
                  {
                      if (_module == LibDataTypes.Module.PROXY) return true;
                      return false;
                  }
                  function requirePreCancel(LibDataTypes.Module _module)
                      internal
                      pure
                      returns (bool)
                  {
                      if (_module == LibDataTypes.Module.PROXY) return true;
                      return false;
                  }
                  function requireOnCreate(LibDataTypes.Module _module)
                      internal
                      pure
                      returns (bool)
                  {
                      if (_module == LibDataTypes.Module.PROXY) return true;
                      return false;
                  }
                  function requirePreExec(LibDataTypes.Module _module)
                      internal
                      pure
                      returns (bool)
                  {
                      if (_module == LibDataTypes.Module.PROXY) return true;
                      return false;
                  }
                  function requirePostExec(LibDataTypes.Module _module)
                      internal
                      pure
                      returns (bool)
                  {
                      if (_module == LibDataTypes.Module.SINGLE_EXEC) return true;
                      return false;
                  }
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.12;
              library GelatoBytes {
                  function calldataSliceSelector(bytes calldata _bytes)
                      internal
                      pure
                      returns (bytes4 selector)
                  {
                      selector =
                          _bytes[0] |
                          (bytes4(_bytes[1]) >> 8) |
                          (bytes4(_bytes[2]) >> 16) |
                          (bytes4(_bytes[3]) >> 24);
                  }
                  function memorySliceSelector(bytes memory _bytes)
                      internal
                      pure
                      returns (bytes4 selector)
                  {
                      selector =
                          _bytes[0] |
                          (bytes4(_bytes[1]) >> 8) |
                          (bytes4(_bytes[2]) >> 16) |
                          (bytes4(_bytes[3]) >> 24);
                  }
                  function revertWithError(bytes memory _bytes, string memory _tracingInfo)
                      internal
                      pure
                  {
                      // 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
                      if (_bytes.length % 32 == 4) {
                          bytes4 selector;
                          assembly {
                              selector := mload(add(0x20, _bytes))
                          }
                          if (selector == 0x08c379a0) {
                              // Function selector for Error(string)
                              assembly {
                                  _bytes := add(_bytes, 68)
                              }
                              revert(string(abi.encodePacked(_tracingInfo, string(_bytes))));
                          } else {
                              revert(
                                  string(abi.encodePacked(_tracingInfo, "NoErrorSelector"))
                              );
                          }
                      } else {
                          revert(
                              string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"))
                          );
                      }
                  }
                  function returnError(bytes memory _bytes, string memory _tracingInfo)
                      internal
                      pure
                      returns (string memory)
                  {
                      // 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
                      if (_bytes.length % 32 == 4) {
                          bytes4 selector;
                          assembly {
                              selector := mload(add(0x20, _bytes))
                          }
                          if (selector == 0x08c379a0) {
                              // Function selector for Error(string)
                              assembly {
                                  _bytes := add(_bytes, 68)
                              }
                              return string(abi.encodePacked(_tracingInfo, string(_bytes)));
                          } else {
                              return
                                  string(abi.encodePacked(_tracingInfo, "NoErrorSelector"));
                          }
                      } else {
                          return
                              string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"));
                      }
                  }
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.12;
              import {
                  SafeERC20,
                  IERC20
              } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
              import {_transfer, ETH} from "../../functions/FUtils.sol";
              abstract contract Gelatofied {
                  address payable public immutable gelato;
                  modifier gelatofy(uint256 _amount, address _paymentToken) {
                      require(msg.sender == gelato, "Gelatofied: Only gelato");
                      _;
                      _transfer(gelato, _paymentToken, _amount);
                  }
                  modifier onlyGelato() {
                      require(msg.sender == gelato, "Gelatofied: Only gelato");
                      _;
                  }
                  constructor(address payable _gelato) {
                      gelato = _gelato;
                  }
              }
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity ^0.8.12;
              abstract contract Proxied {
                  /// @notice to be used by initialisation / postUpgrade function so that only the proxy's admin can execute them
                  /// It also allows these functions to be called inside a contructor
                  /// even if the contract is meant to be used without proxy
                  modifier proxied() {
                      address proxyAdminAddress = _proxyAdmin();
                      // With hardhat-deploy proxies
                      // the proxyAdminAddress is zero only for the implementation contract
                      // if the implementation contract want to be used as a standalone/immutable contract
                      // it simply has to execute the `proxied` function
                      // This ensure the proxyAdminAddress is never zero post deployment
                      // And allow you to keep the same code for both proxied contract and immutable contract
                      if (proxyAdminAddress == address(0)) {
                          // ensure can not be called twice when used outside of proxy : no admin
                          // solhint-disable-next-line security/no-inline-assembly
                          assembly {
                              sstore(
                                  0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103,
                                  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
                              )
                          }
                      } else {
                          require(msg.sender == proxyAdminAddress);
                      }
                      _;
                  }
                  modifier onlyProxyAdmin() {
                      require(msg.sender == _proxyAdmin(), "NOT_AUTHORIZED");
                      _;
                  }
                  function _proxyAdmin() internal view returns (address adminAddress) {
                      // solhint-disable-next-line security/no-inline-assembly
                      assembly {
                          adminAddress := sload(
                              0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
                          )
                      }
                  }
              }
              

              File 30 of 30: OpsProxy
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              import {GelatoBytes} from "../vendor/gelato/GelatoBytes.sol";
              // solhint-disable private-vars-leading-underscore
              // solhint-disable func-visibility
              function _call(
                  address _add,
                  bytes memory _data,
                  uint256 _value,
                  bool _revertOnFailure,
                  string memory _tracingInfo
              ) returns (bool success, bytes memory returnData) {
                  (success, returnData) = _add.call{value: _value}(_data);
                  if (!success && _revertOnFailure)
                      GelatoBytes.revertWithError(returnData, _tracingInfo);
              }
              function _delegateCall(
                  address _add,
                  bytes memory _data,
                  string memory _tracingInfo
              ) returns (bool success, bytes memory returnData) {
                  (success, returnData) = _add.delegatecall(_data);
                  if (!success) GelatoBytes.revertWithError(returnData, _tracingInfo);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.12;
              interface IOpsProxy {
                  /**
                   * @notice Emitted when proxy calls a contract successfully in `executeCall`
                   *
                   * @param target Address of contract that is called
                   * @param data Data used in the call.
                   * @param value Native token value used in the call.
                   * @param returnData Data returned by the call.
                   */
                  event ExecuteCall(
                      address indexed target,
                      bytes data,
                      uint256 value,
                      bytes returnData
                  );
                  /**
                   * @notice Multicall to different contracts with different datas.
                   *
                   * @param targets Addresses of contracts to be called.
                   * @param datas Datas for each contract call.
                   * @param values Native token value for each contract call.
                   */
                  function batchExecuteCall(
                      address[] calldata targets,
                      bytes[] calldata datas,
                      uint256[] calldata values
                  ) external payable;
                  /**
                   * @notice Call to a single contract.
                   *
                   * @param target Address of contracts to be called.
                   * @param data Data for contract call.
                   * @param value Native token value for contract call.
                   */
                  function executeCall(
                      address target,
                      bytes calldata data,
                      uint256 value
                  ) external payable;
                  /**
                   * @return address Ops smart contract address
                   */
                  function ops() external view returns (address);
                  /**
                   * @return address Owner of the proxy
                   */
                  function owner() external view returns (address);
                  /**
                   * @return uint256 version of OpsProxy.
                   */
                  function version() external view returns (uint256);
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.12;
              import {Proxied} from "../vendor/proxy/EIP173/Proxied.sol";
              import {_call} from "../functions/FExec.sol";
              import {IOpsProxy} from "../interfaces/IOpsProxy.sol";
              contract OpsProxy is Proxied, IOpsProxy {
                  // solhint-disable const-name-snakecase
                  uint256 public constant override version = 1;
                  address public immutable override ops;
                  modifier onlyAuth() {
                      address proxyOwner = owner();
                      if (msg.sender != proxyOwner) {
                          require(msg.sender == ops, "OpsProxy: Not authorised");
                          require(
                              _getTaskCreator() == proxyOwner,
                              "OpsProxy: Only tasks created by owner"
                          );
                      } // else msg.sender == proxyOwner
                      _;
                  }
                  // solhint-disable no-empty-blocks
                  constructor(address _ops) {
                      ops = _ops;
                  }
                  receive() external payable {}
                  ///@inheritdoc IOpsProxy
                  function batchExecuteCall(
                      address[] calldata _targets,
                      bytes[] calldata _datas,
                      uint256[] calldata _values
                  ) external payable override onlyAuth {
                      uint256 length = _targets.length;
                      require(
                          length == _datas.length && length == _values.length,
                          "OpsProxy: Length mismatch"
                      );
                      for (uint256 i; i < length; i++)
                          _executeCall(_targets[i], _datas[i], _values[i]);
                  }
                  ///@inheritdoc IOpsProxy
                  function executeCall(
                      address _target,
                      bytes calldata _data,
                      uint256 _value
                  ) external payable override onlyAuth {
                      _executeCall(_target, _data, _value);
                  }
                  function owner() public view returns (address) {
                      return _proxyAdmin();
                  }
                  function _executeCall(
                      address _target,
                      bytes calldata _data,
                      uint256 _value
                  ) private {
                      (, bytes memory returnData) = _call(
                          _target,
                          _data,
                          _value,
                          true,
                          "OpsProxy.executeCall: "
                      );
                      emit ExecuteCall(_target, _data, _value, returnData);
                  }
                  function _getTaskCreator() private pure returns (address taskCreator) {
                      assembly {
                          taskCreator := shr(96, calldataload(sub(calldatasize(), 20)))
                      }
                  }
              }
              // SPDX-License-Identifier: UNLICENSED
              pragma solidity ^0.8.12;
              library GelatoBytes {
                  function calldataSliceSelector(bytes calldata _bytes)
                      internal
                      pure
                      returns (bytes4 selector)
                  {
                      selector =
                          _bytes[0] |
                          (bytes4(_bytes[1]) >> 8) |
                          (bytes4(_bytes[2]) >> 16) |
                          (bytes4(_bytes[3]) >> 24);
                  }
                  function memorySliceSelector(bytes memory _bytes)
                      internal
                      pure
                      returns (bytes4 selector)
                  {
                      selector =
                          _bytes[0] |
                          (bytes4(_bytes[1]) >> 8) |
                          (bytes4(_bytes[2]) >> 16) |
                          (bytes4(_bytes[3]) >> 24);
                  }
                  function revertWithError(bytes memory _bytes, string memory _tracingInfo)
                      internal
                      pure
                  {
                      // 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
                      if (_bytes.length % 32 == 4) {
                          bytes4 selector;
                          assembly {
                              selector := mload(add(0x20, _bytes))
                          }
                          if (selector == 0x08c379a0) {
                              // Function selector for Error(string)
                              assembly {
                                  _bytes := add(_bytes, 68)
                              }
                              revert(string(abi.encodePacked(_tracingInfo, string(_bytes))));
                          } else {
                              revert(
                                  string(abi.encodePacked(_tracingInfo, "NoErrorSelector"))
                              );
                          }
                      } else {
                          revert(
                              string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"))
                          );
                      }
                  }
                  function returnError(bytes memory _bytes, string memory _tracingInfo)
                      internal
                      pure
                      returns (string memory)
                  {
                      // 68: 32-location, 32-length, 4-ErrorSelector, UTF-8 err
                      if (_bytes.length % 32 == 4) {
                          bytes4 selector;
                          assembly {
                              selector := mload(add(0x20, _bytes))
                          }
                          if (selector == 0x08c379a0) {
                              // Function selector for Error(string)
                              assembly {
                                  _bytes := add(_bytes, 68)
                              }
                              return string(abi.encodePacked(_tracingInfo, string(_bytes)));
                          } else {
                              return
                                  string(abi.encodePacked(_tracingInfo, "NoErrorSelector"));
                          }
                      } else {
                          return
                              string(abi.encodePacked(_tracingInfo, "UnexpectedReturndata"));
                      }
                  }
              }
              // SPDX-License-Identifier: GPL-3.0
              pragma solidity ^0.8.12;
              abstract contract Proxied {
                  /// @notice to be used by initialisation / postUpgrade function so that only the proxy's admin can execute them
                  /// It also allows these functions to be called inside a contructor
                  /// even if the contract is meant to be used without proxy
                  modifier proxied() {
                      address proxyAdminAddress = _proxyAdmin();
                      // With hardhat-deploy proxies
                      // the proxyAdminAddress is zero only for the implementation contract
                      // if the implementation contract want to be used as a standalone/immutable contract
                      // it simply has to execute the `proxied` function
                      // This ensure the proxyAdminAddress is never zero post deployment
                      // And allow you to keep the same code for both proxied contract and immutable contract
                      if (proxyAdminAddress == address(0)) {
                          // ensure can not be called twice when used outside of proxy : no admin
                          // solhint-disable-next-line security/no-inline-assembly
                          assembly {
                              sstore(
                                  0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103,
                                  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
                              )
                          }
                      } else {
                          require(msg.sender == proxyAdminAddress);
                      }
                      _;
                  }
                  modifier onlyProxyAdmin() {
                      require(msg.sender == _proxyAdmin(), "NOT_AUTHORIZED");
                      _;
                  }
                  function _proxyAdmin() internal view returns (address adminAddress) {
                      // solhint-disable-next-line security/no-inline-assembly
                      assembly {
                          adminAddress := sload(
                              0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
                          )
                      }
                  }
              }