20M+ users trust MoonPay worldwide. Checkout with your preferred payment method.
Ready to onboard to Ethereum? With MetaMask Portfolio, you're in control.
Don’t invest unless you’re prepared to lose all the money you invest.
Everyday giveaways up to 100 ETH, Lucky Spins. Deposit BONUS 300% and Cashbacks!
5000+ Slots & Live Casino Games, 50+cryptos. Register with Etherscan and get 760% deposit bonus. Win Big$, withdraw it fast.
Slots, Roulette, Poker & more - Proud sponsors of UFC, Everton & StakeF1 team!
5000+ Slots & Live Casino Games, 50+cryptos. Register with Etherscan and get 760% deposit bonus. Win Big$, withdraw it fast.
Anonymous play on awesome games - sign up now for 25 free jackpot spins - worth $100s!
100s of games, generous bonuses, 20+ years of trusted gaming. Join CryptoWins & start winning today!
Overview
ETH Balance
Eth Value
Less Than $0.01 (@ $3,317.38/ETH)Token Holdings
Could not find any matches!
- ERC-20 Tokens (>200)0 AEGAether Games (AEG)$0.00@0.00620 alETHAlchemix ETH (alETH)$0.00@3,041.700 ALICEAlice AI (ALICE)0 AmericaAmerica (Americ...)$0.00@0.00040 ABONDApeBond (ABOND)$0.00@0.00460 BTCBitcoin6900 (BTC)0 CLEVCLever Token (CLEV)$0.00@4.820 CTRConcentrator... (CTR)$0.00@0.42270 PUNKCryptoPunks (PUNK)$0.00@119,527.000 EMRLDEMERALD (EMRLD)$0.00@0.00320 ETEEtherEmpires (ETE)$0.00@0.00730 FNFakeNews (FN)0 fluffyFluffy (fluffy)0 GASGas DAO (GAS)$0.00@0.000 FlorkHeyFlork (Flork)$0.00@0.00010 ICSAIcosa (ICSA)$0.00@0.1350 JJJJMOJI (JJ)$0.00@0.03740 HERRISKumala Herri... (HERRIS)0 LCRLaunchR (LCR)$0.00@0.0010 MAZZEMAZZE (MAZZE)$0.00@0.00180 MILEIMILEI Token (MILEI)$0.00@0.02660 NGCNAGA Coin (NGC)$0.00@0.01740 NCCNeuroChain C... (NCC)0 PIKAPikachu (PIKA)$0.00@0.000 PCPlayable Coi... (PC)$0.00@0.00010 $ROARRoaring Kitt... ($ROAR)$0.00@0.00550 SHIBAShiba (SHIBA)$0.00@0.000.00000001 SPXSPX6900 (SPX)$0.00@0.51690 STRDYSturdy Token (STRDY)$0.00@2.430 TAONUTAO Inu (TAONU)$0.00@0.00460 SPARKLETUpland (SPARKL...)$0.00@0.04140 VECVector (VEC)$0.00@1.550 WOJAKWojak Coin (WOJAK)$0.00@0.00190 0XL0x Leverage (0XL)$0.00@0.00220.00081405 0x00x0 Token (0x0)$0.00@0.0050.00000001 0xBTC0xBitcoin To... (0xBTC)$0.00@0.07920 0XC0xCalls (0XC)$0.00@0.01810.00000007 OxN0xNumber (OxN)$0.00@0.01680 SCANS0xScans (SCANS)$0.00@0.00510 1INCH1INCH Token (1INCH)$0.00@0.3480.00000001 1WO1World (1WO)0 TRUMP4747th Preside... (TRUMP4...)$0.00@0.00020 STZ99Starz (STZ)$0.00@0.01070 CAWA Hunters Dr... (CAW)$0.00@0.000.000001 aEthUSDCAave Ethereu... (aEthUS...)$0.00@0.99990.000001 aEthUSDTAave Ethereu... (aEthUS...)$0.00@1.0010.00000001 aEthWBTCAave Ethereu... (aEthWB...)$0.00@98,951.000.000001 aUSDCAave interes... (aUSDC)$0.00@0.99820 aWETHAave interes... (aWETH)$0.00@3,316.860 AAVEAave Token (AAVE)$0.00@167.800 GHSTAavegotchi G... (GHST)$0.00@0.94640 TOADAcid Toad (TOAD)$0.00@0.000 ATHAethir Token (ATH)$0.00@0.0580 AEVOAevo (AEVO)$0.00@0.35680 AGXAGIX (AGX)$0.00@0.10250 AGAAgora DEX To... (AGA)$0.00@0.00970.00000001 AGRSAgoras Token (AGRS)$0.00@1.680 AGURIAguri-Chan (AGURI)$0.00@0.000 AIXAIgentX (AIX)$0.00@0.10250 AIOZAIOZ Network (AIOZ)$0.00@0.82460.0001 ASTAirSwap (AST)$0.00@0.08770 aisigAISignal (aisig)$0.00@0.00050 AITAXAITaxBot (AITAX)$0.00@0.0020 AKITAAkita Inu (AKITA)$0.00@0.000 arUSDAladdin rUSD (arUSD)$0.00@1.0720 ALDAladdin Toke... (ALD)$0.00@0.02920 ALCXAlchemix (ALCX)$0.00@20.270.00000001 ACHAlchemy (ACH)$0.00@0.02280 ALEPHaleph.im v2 (ALEPH)$0.00@0.15240 ALPHAlephium (Al... (ALPH)$0.00@1.510 AllInAll In (AllIn)$0.00@0.48370 NXRAAllianceBloc... (NXRA)$0.00@0.03150 AMKTAlongside Cr... (AMKT)$0.00@266.560 ALTAltLayer Tok... (ALT)$0.00@0.11320 ASIAltSignals (ASI)$0.00@0.00140 OMIKAMIAMATERASU OM... (OMIKAM...)$0.00@0.02020 AMCAMC (AMC)$0.00@0.003,308,126.7577956 USHIBAAmerican Shi... (USHIBA)0 AMOAmino (AMO)$0.00@0.00020 AMPAmp (AMP)$0.00@0.00470 AMPLAmpleforth (AMPL)$0.00@1.200 ANDYMANAndyman (ANDYMA...)$0.00@0.00130 ANGLEANGLE (ANGLE)$0.00@0.0230 ANMLAnimal Conce... (ANML)$0.00@0.00010 ANKRAnkr Network (ANKR)$0.00@0.03410 ankrETHAnkr Staked ... (ankrET...)$0.00@3,881.860 APEApeCoin (APE)$0.00@1.190.000001 NFTAPENFT (NFT)$0.00@0.000 APESApeScreener (APES)$0.00@0.08550 APEXApeX Token (APEX)$0.00@1.970 API3API3 (API3)$0.00@1.890 APUGURLApu Gurl (APUGUR...)$0.00@0.000 APYSAPYSwap (APYS)$0.00@0.0060 AQTISAQTIS Token (AQTIS)$0.00@0.00080 ANTAragon Netwo... (ANT)$0.00@1.750 RBISArbismart To... (RBIS)$0.00@0.00010 AIUSArbius (AIUS)$0.00@33.64530 ARCARC (ARC)$0.00@0.06660 ABTArcBlock (ABT)$0.00@1.360 ARCXArchitex (ARCX)$0.00@0.45850 ARKMArkham (ARKM)$0.00@2.00980 ARKIArkiTech (ARKI)$0.00@0.03020 ALIArtificial L... (ALI)$0.00@0.01260 ASIAASIA COIN (ASIA)$0.00@0.10840 ASTXAsterix (ASTX)$0.00@625.65740 ASTRAAstraAI (ASTRA)$0.00@1.28120 ASFAsymmetry Fi... (ASF)0 AUDIOAudius (AUDIO)$0.00@0.1460 AURAAura (AURA)$0.00@0.31960 AURORAAurora (AURORA)$0.00@0.15660 URUSAurox Token (URUS)$0.00@2.510.000001 AUSDAUSD (AUSD)0.000001 aUSD₮aUSD₮ (aUSD₮)$0.00@0.99660.0001 NIOXAutonio (NIOX)$0.00@0.00110 AVAILAvail (AVAIL)$0.00@0.11130 AVENTAventa (AVENT)$0.00@0.00050 AVGAvocado DAO ... (AVG)$0.00@0.01370.000001 AXLAxelar (AXL)$0.00@0.71550 AXSAxie Infinit... (AXS)$0.00@6.200 AZURAzuro (AZUR)$0.00@0.06360 BMoneyB-money (BMoney)$0.00@0.000 BXXBaanx (BXX)$0.00@0.04250 BAXBABB BAX (BAX)$0.00@0.00010 BABYPEPEBaby Pepe (BABYPE...)$0.00@0.000 BADGERBadger (BADGER)$0.00@3.360 BALBalancer (BAL)$0.00@2.380 bALPHAbAlpha (bALPHA)$0.00@10.76650 BANANABanana (BANANA)$0.00@0.25710 BANANABanana (BANANA)$0.00@65.430 BCATBananaCat (BCAT)$0.00@0.00910 BANKBank Ai (BANK)$0.00@0.00020 BANKBankless Tok... (BANK)$0.00@0.0010 BAOBao Token V2 (BAO)$0.00@0.00080 BAOBaoToken (BAO)$0.00@0.000 SOLANABarbieCrashB... (SOLANA)$0.00@0.000 BONDBarnBridge G... (BOND)$0.00@1.600 BASEBase Protoco... (BASE)$0.00@1.0240 BAZEDBazed Games (BAZED)$0.00@0.04540 BDPBDPToken (BDP)$0.00@0.11190 BEAMBeam (BEAM)$0.00@0.02120 BEARDYBearded Drag... (BEARDY)$0.00@0.00020.00000001 BEBEBEBE (BEBE)$0.00@0.000 FROGBeer Frog (FROG)$0.00@0.00080 EYEBehodler.io (EYE)$0.00@0.03270 BelugaBeluga (Beluga)0 BENDBend Token (BEND)$0.00@0.00070 BENTBent Token (BENT)$0.00@0.01720 BEPROBetProtocolT... (BEPRO)$0.00@0.00050 BIDBidao (BID)$0.00@0.00070 BiFiBiFi (BiFi)$0.00@0.00290 BUDBig Bud (BUD)$0.00@0.000.00000001 BBTCBinance Wrap... (BBTC)$0.00@97,837.000 BIRDDOGBird Dog (BIRDDO...)$0.00@0.000 BIRDDOGBird Dog (BIRDDO...)$0.00@0.000.00000001 btcbitcoin (btc)$0.00@0.10940 BITBitDAO (BIT)$0.00@0.83640 LEOBitfinex LEO... (LEO)$0.00@8.410 BITGBitGate (BITG)$0.00@0.00020 BITROCKBitrock (BITROC...)$0.00@0.04580 BTTBitTorrent (BTT)$0.00@0.000 BHSc$BlackHoleSwa... (BHSc$)0 BPTBlackPool To... (BPT)$0.00@0.04650 BSBlacksmith T... (BS)$0.00@0.00060 BLESBlind Boxes ... (BLES)$0.00@0.00260 BJBlocjerk (BJ)$0.00@0.25450 BBLBlockBlend (BBL)$0.00@0.00530 BCDTBlockchain C... (BCDT)$0.00@0.04060 DEFENDBlockDefend ... (DEFEND)$0.00@0.00490 BSTBlocksquareT... (BST)$0.00@0.23260 VEEBLOCKv (VEE)$0.00@0.00870 BLOODBloodboy (BLOOD)$0.00@0.00050 BLBBlueberry (BLB)$0.00@0.00140 BLZBluzelle (BLZ)$0.00@0.11910 BOBBOB (BOB)$0.00@0.000 BOBOBOBO (BOBO)$0.00@0.000 BOGBog (BOG)$0.00@0.000 BONDLYBondly (BONDLY)$0.00@0.0020 BONKBonk on ETH (BONK)$0.00@0.000 BODBook Of Dona... (BOD)$0.00@0.000 BOOEBook of Ethe... (BOOE)$0.00@0.25020 BOPPYBoppy The Ba... (BOPPY)$0.00@0.000 BORINGBoringDAO (BORING)$0.00@0.00020 BORKBork (BORK)$0.00@0.01290 BOSONBoson Token (BOSON)$0.00@0.30820 BOTTOBotto (BOTTO)$0.00@0.61120 BXBTBoxBet (BXBT)$0.00@0.0730 BrAInBrAIngent (BrAIn)$0.00@0.01710 BREEDBreederDAO (BREED)$0.00@0.01960 BRETTBrett (BRETT)$0.00@0.03170 BRAIBribeAI (BRAI)$0.00@0.07540 BKNBrickken (BKN)$0.00@0.30660 BTUBTU Protocol (BTU)$0.00@0.39530 BuddyBuddyAI (Buddy)$0.00@0.04230 BUILDBuildAI (BUILD)$0.00@0.08370 BULLABulladotfun (BULLA)$0.00@0.01070 BTCBullish Trum... (BTC)$0.00@0.000 BYTEByteAI (BYTE)$0.00@0.00020 BYTESBYTES (BYTES)$0.00@5.25610 BZZBZZ (BZZ)$0.00@0.49730 CADCAD (CAD)$0.00@0.0018NFT Tokens (29)claim rewards on agixcoin.netagixcoin.netERC-1155claim rewards on apyclink.comapyclink.comERC-1155claim rewards on beamprotocol.netbeamprotocol.netERC-1155claim rewards on dydxnetwork.orgdydxnetwork.orgERC-1155claim rewards on ensfoundation.orgensfoundation.orgERC-1155claim rewards on fetchevent.netfetchevent.netERC-1155claim rewards on fraxprotocol.comfraxprotocol.comERC-1155GNUSGenius Token & NFT CollectionsERC-1155claim rewards illuviumnetwork.netilluviumnetwork.netERC-1155claim rewards on jasmyprotocol.comjasmyprotocol.comERC-1155claim rewards on ondonetwork.orgondonetwork.orgERC-1155claim rewards on paxgnetwork.compaxgnetwork.comERC-1155claim rewards on poolstaked.orgpoolstaked.orgERC-1155Puffer Mystery Box NFT pufether.orgPuffer Mystery Box NFT pufether.orgERC-1155claim rewards qntnetwork.orgqntnetwork.orgERC-1155claim rewards on renderpool.orgrenderpool.orgERC-1155claim rewards riotoken.orgriotoken.orgERC-1155t.me/shibarmy_botShiba Inu Token AirdropERC-1155claim rewards on snxtoken.comsnxtoken.comERC-1155claim rewards on swapshiba.orgswapshiba.orgERC-1155claim rewards on woonetwork.netwoonetwork.netERC-1155claim rewards on wrappedbtc.netwrappedbtc.netERC-1155
More Info
Private Name Tags
ContractCreator
- Transactions
- Internal Transactions
- Token Transfers (ERC-20)
- NFT Transfers
- Contract
- Events
- Analytics
- Multichain Portfolio
- Cards New
- Filter by Tx Type:
- Tx
- Internal Tx
- ERC-20
- NFTs
Latest 25 from a total of 106,521 transactions (+21 Pending)
Transaction Hash |
Method
|
Block
|
From
|
To
|
||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
0x4392c7bc82ebc8994c92bcee0806875c743502fa95dfddd5830240967cc48426 | Swap Exact Amoun... | (pending) | 2024-11-22 15:24:45 | 8 hrs ago | 1732289085 | IN | 0.0025 ETH$8.29 | (Pending) | (Pending) | |||
0xcb23b8f3781bcbfc46f1abb65680f5f555234e596a957e8056cc21483f0992d9 | Swap Exact Amoun... | (pending) | 2024-11-22 12:32:44 | 10 hrs ago | 1732278764 | IN | 0.0062 ETH$20.57 | (Pending) | (Pending) | |||
0x4c95256aeafc95bb74e32ad252be96ed19c30a5eb3b48171a7170dfd4206f900 | Swap Exact Amoun... | (pending) | 2024-11-21 1:01:06 | 46 hrs ago | 1732150866 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x1c6a456268a12bb48e9801c292e6e0e960f4588ea7514d5700e51fb4109e950d | Swap Exact Amoun... | (pending) | 2024-11-20 13:26:17 | 2 days ago | 1732109177 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x929883e8c22bf84e7f4108a75923a38ebecee7917610bd98f64e27c97d26de68 | Swap Exact Amoun... | (pending) | 2024-11-20 13:04:41 | 2 days ago | 1732107881 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x0463d718697bab2fc01679b4c527f16b14b0a7b005e62072bb2aa0d5f601e922 | Swap Exact Amoun... | (pending) | 2024-11-20 13:03:30 | 2 days ago | 1732107810 | IN | 0.008 ETH$26.54 | (Pending) | (Pending) | |||
0x0dffb2d11ab0aa7c7381ee2864b89cba2b3f5a3e2cf3b5f56720cae0978cb757 | Swap Exact Amoun... | (pending) | 2024-11-20 13:02:31 | 2 days ago | 1732107751 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0xda286ecc55b1beeb46da1b5b32a2b4f03479bab7737977a9cc14ef38690e663a | Swap Exact Amoun... | (pending) | 2024-11-20 13:01:57 | 2 days ago | 1732107717 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x780d4a181a489ac6fe6325ffad1d0eba6257b51e08e9594d1f88276ecb52bc9d | Swap Exact Amoun... | (pending) | 2024-11-17 23:55:32 | 4 days ago | 1731887732 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0xd62350e39b4c511d01c406baa7be721728b5fa7bbdd87f3cc1d3b4168fa7d3fe | Swap Exact Amoun... | (pending) | 2024-11-08 14:23:29 | 14 days ago | 1731075809 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0xe06c88e02a019a9de449edd1658c38855822f4b59181f62e98a2d5c65d35d856 | Swap Exact Amoun... | (pending) | 2024-11-08 14:23:29 | 14 days ago | 1731075809 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x92b010f6ffdbc68afa47879c30e45f4b438d116ef0608ef60296a7e3a34d1180 | Swap Exact Amoun... | (pending) | 2024-11-06 14:02:11 | 16 days ago | 1730901731 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x372bef439b071452c4ae0b122d3c7eb2da016eb3dcd0cbdec3ce1ba1e948d68a | Swap Exact Amoun... | (pending) | 2024-11-06 1:02:36 | 16 days ago | 1730854956 | IN | 0.0013573925 ETH$4.50 | (Pending) | (Pending) | |||
0xd35c74a909cc52ff53d766f8333c59ad4eead097a2dbc8de150d33ba67aea30f | Swap Exact Amoun... | (pending) | 2024-11-05 16:01:30 | 17 days ago | 1730822490 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0xc9dba40e7cf161f3ef1d2cf211868d0d86c1e09f4343c3bd4bb75472a6782869 | Swap Exact Amoun... | (pending) | 2024-11-05 13:02:31 | 17 days ago | 1730811751 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x424ac6d28aad36afafdff96b87acdbf842f807c8b86e4c510b4d0a7a0a266a6f | Swap Exact Amoun... | (pending) | 2024-11-05 13:01:51 | 17 days ago | 1730811711 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0xe0c97fcf7876467cdb9bfc8af6e79e78540b9ff21c4d50883c9caf655904ee73 | Swap Exact Amoun... | (pending) | 2024-11-04 2:22:00 | 18 days ago | 1730686920 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x9a29feca4d913fed5c3b5f6c3da47c642383896a2a69847951e76798a5c9b217 | Swap Exact Amoun... | (pending) | 2024-11-03 22:15:38 | 19 days ago | 1730672138 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0x1c62a7565240964e20c49a6db675bede28f305062722c24d572c3fbb875eecab | Swap Exact Amoun... | (pending) | 2024-11-03 14:56:29 | 19 days ago | 1730645789 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0xf5854e036ffb6484d72db97dc06c537d2a9f36c2257305eb2a777823442dcee4 | Swap Exact Amoun... | (pending) | 2024-11-03 14:11:23 | 19 days ago | 1730643083 | IN | 0 ETH$0.00 | (Pending) | (Pending) | |||
0xd6455b1190e200d1a81754e1738ce54cab6828c2e312773520ec3ade35803169 | Swap Exact Amoun... | (pending) | 2024-11-03 4:28:40 | 19 days ago | 1730608120 | IN | 0.0038 ETH$12.61 | (Pending) | (Pending) | |||
Swap Exact Amoun... | 21246537 | 2024-11-22 23:25:11 | 1 min ago | 1732317911 | IN | 1 ETH$3,317.38 | 0.00339882 | 12.09104728 | ||||
Swap Exact Amoun... | 21246498 | 2024-11-22 23:17:11 | 9 mins ago | 1732317431 | IN | 0 ETH$0.00 | 0.00157785 | 12.76647221 | ||||
Swap Exact Amoun... | 21246494 | 2024-11-22 23:16:23 | 10 mins ago | 1732317383 | IN | 0.075 ETH$248.80 | 0.00235677 | 14.43405679 | ||||
Swap Exact Amoun... | 21246473 | 2024-11-22 23:12:11 | 14 mins ago | 1732317131 | IN | 0 ETH$0.00 | 0.00265168 | 13.69453227 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||||
---|---|---|---|---|---|---|---|---|
21246537 | 2024-11-22 23:25:11 | 1 min ago | 1732317911 | 1 ETH$3,317.38 | ||||
21246494 | 2024-11-22 23:16:23 | 10 mins ago | 1732317383 | 0.075 ETH$248.80 | ||||
21246473 | 2024-11-22 23:12:11 | 14 mins ago | 1732317131 | 0.0318355 ETH$105.61 | ||||
21246473 | 2024-11-22 23:12:11 | 14 mins ago | 1732317131 | 0.00008142 ETH$0.27 | ||||
21246473 | 2024-11-22 23:12:11 | 14 mins ago | 1732317131 | 0.00001436 ETH$0.05 | ||||
21246473 | 2024-11-22 23:12:11 | 14 mins ago | 1732317131 | 0.03193129 ETH$105.93 | ||||
21246468 | 2024-11-22 23:11:11 | 15 mins ago | 1732317071 | 0.19079386 ETH$632.94 | ||||
21246441 | 2024-11-22 23:05:47 | 21 mins ago | 1732316747 | 8.53651133 ETH$28,318.89 | ||||
21246441 | 2024-11-22 23:05:47 | 21 mins ago | 1732316747 | 0.0218336 ETH$72.43 | ||||
21246441 | 2024-11-22 23:05:47 | 21 mins ago | 1732316747 | 0.00385298 ETH$12.78 | ||||
21246441 | 2024-11-22 23:05:47 | 21 mins ago | 1732316747 | 8.56219792 ETH$28,404.11 | ||||
21246436 | 2024-11-22 23:04:47 | 22 mins ago | 1732316687 | 0.31474493 ETH$1,044.13 | ||||
21246426 | 2024-11-22 23:02:47 | 24 mins ago | 1732316567 | 0.10547971 ETH$349.92 | ||||
21246426 | 2024-11-22 23:02:47 | 24 mins ago | 1732316567 | 0.00182975 ETH$6.07 | ||||
21246426 | 2024-11-22 23:02:47 | 24 mins ago | 1732316567 | 0.00032289 ETH$1.07 | ||||
21246426 | 2024-11-22 23:02:47 | 24 mins ago | 1732316567 | 0.10763236 ETH$357.06 | ||||
21246423 | 2024-11-22 23:02:11 | 24 mins ago | 1732316531 | 1 ETH$3,317.38 | ||||
21246415 | 2024-11-22 23:00:35 | 26 mins ago | 1732316435 | 7.28901695 ETH$24,180.48 | ||||
21246415 | 2024-11-22 23:00:35 | 26 mins ago | 1732316435 | 0.12644213 ETH$419.46 | ||||
21246415 | 2024-11-22 23:00:35 | 26 mins ago | 1732316435 | 0.02231331 ETH$74.02 | ||||
21246415 | 2024-11-22 23:00:35 | 26 mins ago | 1732316435 | 7.4377724 ETH$24,673.95 | ||||
21246407 | 2024-11-22 22:58:59 | 27 mins ago | 1732316339 | 0.61 ETH$2,023.60 | ||||
21246386 | 2024-11-22 22:54:47 | 32 mins ago | 1732316087 | 18.74885605 ETH$62,197.17 | ||||
21246386 | 2024-11-22 22:54:47 | 32 mins ago | 1732316087 | 0.04795344 ETH$159.08 | ||||
21246386 | 2024-11-22 22:54:47 | 32 mins ago | 1732316087 | 0.00846237 ETH$28.07 |
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Vendor import { Diamond } from "./vendor/Diamond.sol"; // Routers import { Routers } from "./routers/Routers.sol"; // ______ __ __ __ ____ // /\ _ \ /\ \__ /\ \/\ \ /'___\ // \ \ \L\ \ __ __ __ __ __ ____\ \ ,_\ __ __ ____\ \ \ \ \/\ \__/ // \ \ __ \/\ \/\ \ /'_ `\/\ \/\ \ /',__\\ \ \/ /\ \/\ \ /',__\\ \ \ \ \ \ _``\ // \ \ \/\ \ \ \_\ \/\ \L\ \ \ \_\ \/\__, `\\ \ \_\ \ \_\ \/\__, `\\ \ \_/ \ \ \L\ \ // \ \_\ \_\ \____/\ \____ \ \____/\/\____/ \ \__\\ \____/\/\____/ \ `\___/\ \____/ // \/_/\/_/\/___/ \/___L\ \/___/ \/___/ \/__/ \/___/ \/___/ `\/__/ \/___/ // /\____/ // \_/__/ /// @title AugustusV6 /// @notice The V6 implementation of the ParaSwap onchain aggregation protocol contract AugustusV6 is Diamond, Routers { /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( /// @dev Diamond address _owner, address _diamondCutFacet, /// @dev Direct Routers address _weth, address payable _balancerVault, uint256 _uniV3FactoryAndFF, uint256 _uniswapV3PoolInitCodeHash, uint256 _uniswapV2FactoryAndFF, uint256 _uniswapV2PoolInitCodeHash, address _rfq, /// @dev Fees address payable _feeVault, /// @dev Permit2 address _permit2 ) Diamond(_owner, _diamondCutFacet) Routers( _weth, _uniV3FactoryAndFF, _uniswapV3PoolInitCodeHash, _uniswapV2FactoryAndFF, _uniswapV2PoolInitCodeHash, _balancerVault, _permit2, _rfq, _feeVault ) { } /*////////////////////////////////////////////////////////////// EXTERNAL //////////////////////////////////////////////////////////////*/ /// @notice Reverts if the caller is one of the following: // - 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 receive() external payable override(Diamond) { address addr = msg.sender; // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { if iszero(extcodesize(addr)) { revert(0, 0) } } } }
// SPDX-License-Identifier: MIT /** * Vendored on October 12, 2023 from: * https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/Diamond.sol */ pragma solidity ^0.8.0; /** * \ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 * * Implementation of a diamond. * /***************************************************************************** */ import { LibDiamond } from "./libraries/LibDiamond.sol"; import { IDiamondCut } from "./interfaces/IDiamondCut.sol"; contract Diamond { error DiamondFunctionDoesNotExist(); constructor(address _contractOwner, address _diamondCutFacet) payable { LibDiamond.setContractOwner(_contractOwner); // Add the diamondCut external function from the diamondCutFacet IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1); bytes4[] memory functionSelectors = new bytes4[](1); functionSelectors[0] = IDiamondCut.diamondCut.selector; cut[0] = IDiamondCut.FacetCut({ facetAddress: _diamondCutFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); LibDiamond.diamondCut(cut, address(0), ""); } // Find facet for function that is called and execute the // function if a facet is found and return any value. fallback() external payable { LibDiamond.DiamondStorage storage ds; bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION; // get diamond storage assembly { ds.slot := position } // get facet from function selector address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress; // revert if function does not exist if (facet == address(0)) { revert DiamondFunctionDoesNotExist(); } // Execute external function from facet using delegatecall and return any value. assembly { // copy function selector and any arguments calldatacopy(0, 0, calldatasize()) // execute function call using the facet let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) // get any return value returndatacopy(0, 0, returndatasize()) // return any return value or error back to the caller switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } receive() external payable virtual { } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // DirectSwapExactAmountIn import { BalancerV2SwapExactAmountIn } from "./swapExactAmountIn/direct/BalancerV2SwapExactAmountIn.sol"; import { CurveV1SwapExactAmountIn } from "./swapExactAmountIn/direct/CurveV1SwapExactAmountIn.sol"; import { CurveV2SwapExactAmountIn } from "./swapExactAmountIn/direct/CurveV2SwapExactAmountIn.sol"; import { UniswapV2SwapExactAmountIn } from "./swapExactAmountIn/direct/UniswapV2SwapExactAmountIn.sol"; import { UniswapV3SwapExactAmountIn } from "./swapExactAmountIn/direct/UniswapV3SwapExactAmountIn.sol"; // DirectSwapExactAmountOut import { BalancerV2SwapExactAmountOut } from "./swapExactAmountOut/direct/BalancerV2SwapExactAmountOut.sol"; import { UniswapV2SwapExactAmountOut } from "./swapExactAmountOut/direct/UniswapV2SwapExactAmountOut.sol"; import { UniswapV3SwapExactAmountOut } from "./swapExactAmountOut/direct/UniswapV3SwapExactAmountOut.sol"; // Fees import { AugustusFees } from "../fees/AugustusFees.sol"; // GenericSwapExactAmountIn import { GenericSwapExactAmountIn } from "./swapExactAmountIn/GenericSwapExactAmountIn.sol"; // GenericSwapExactAmountOut import { GenericSwapExactAmountOut } from "./swapExactAmountOut/GenericSwapExactAmountOut.sol"; // General import { AugustusRFQRouter } from "./general/AugustusRFQRouter.sol"; // Utils import { AugustusRFQUtils } from "../util/AugustusRFQUtils.sol"; import { BalancerV2Utils } from "../util/BalancerV2Utils.sol"; import { UniswapV2Utils } from "../util/UniswapV2Utils.sol"; import { UniswapV3Utils } from "../util/UniswapV3Utils.sol"; import { WETHUtils } from "../util/WETHUtils.sol"; import { Permit2Utils } from "../util/Permit2Utils.sol"; /// @title Routers /// @notice A wrapper for all router contracts contract Routers is AugustusFees, AugustusRFQRouter, BalancerV2SwapExactAmountOut, BalancerV2SwapExactAmountIn, CurveV1SwapExactAmountIn, CurveV2SwapExactAmountIn, GenericSwapExactAmountOut, GenericSwapExactAmountIn, UniswapV2SwapExactAmountOut, UniswapV2SwapExactAmountIn, UniswapV3SwapExactAmountOut, UniswapV3SwapExactAmountIn { /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( address _weth, uint256 _uniswapV3FactoryAndFF, uint256 _uniswapV3PoolInitCodeHash, uint256 _uniswapV2FactoryAndFF, uint256 _uniswapV2PoolInitCodeHash, address payable _balancerVault, address _permit2, address _rfq, address payable _feeVault ) AugustusFees(_feeVault) AugustusRFQUtils(_rfq) BalancerV2Utils(_balancerVault) Permit2Utils(_permit2) UniswapV2Utils(_uniswapV2FactoryAndFF, _uniswapV2PoolInitCodeHash) UniswapV3Utils(_uniswapV3FactoryAndFF, _uniswapV3PoolInitCodeHash) WETHUtils(_weth) { } }
// SPDX-License-Identifier: MIT /** * Vendored on October 12, 2023 from: * https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/libraries/LibDiamond.sol */ pragma solidity ^0.8.0; /** * \ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 * /***************************************************************************** */ import { IDiamondCut } from "../interfaces/IDiamondCut.sol"; // Remember to add the loupe functions from DiamondLoupeFacet to the diamond. // The loupe functions are required by the EIP2535 Diamonds standard error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); library LibDiamond { bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); struct FacetAddressAndPosition { address facetAddress; uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array } struct FacetFunctionSelectors { bytes4[] functionSelectors; uint256 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 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(); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _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"); addFunction(ds, selector, selectorPosition, _facetAddress); 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)"); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _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(ds, oldFacetAddress, selector); addFunction(ds, selector, selectorPosition, _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(ds, oldFacetAddress, selector); } } function addFacet(DiamondStorage storage ds, address _facetAddress) internal { enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code"); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length; ds.facetAddresses.push(_facetAddress); } function addFunction( DiamondStorage storage ds, bytes4 _selector, uint96 _selectorPosition, address _facetAddress ) internal { ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition; ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector); ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress; } function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal { 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 = uint96(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 = facetAddressPosition; } ds.facetAddresses.pop(); delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { return; } enforceHasContractCode(_init, "LibDiamondCut: _init address has no code"); (bool success, bytes memory error) = _init.delegatecall(_calldata); if (!success) { if (error.length > 0) { // bubble up error /// @solidity memory-safe-assembly assembly { let returndata_size := mload(error) revert(add(32, error), returndata_size) } } else { revert InitializationFunctionReverted(_init, _calldata); } } } function enforceHasContractCode(address _contract, string memory _errorMessage) internal view { uint256 contractSize; assembly { contractSize := extcodesize(_contract) } require(contractSize > 0, _errorMessage); } }
// SPDX-License-Identifier: MIT /** * Vendored on October 12, 2023 from: * https://github.com/mudgen/diamond-3-hardhat/blob/main/contracts/interfaces/IDiamondCut.sol */ pragma solidity ^0.8.0; /** * \ * Author: Nick Mudge (https://twitter.com/mudgen) * EIP-2535 Diamonds: 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; } /// @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; event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IBalancerV2SwapExactAmountIn } from "../../../interfaces/IBalancerV2SwapExactAmountIn.sol"; // Libraries import { ERC20Utils } from "../../../libraries/ERC20Utils.sol"; // Types import { BalancerV2Data } from "../../../AugustusV6Types.sol"; // Utils import { BalancerV2Utils } from "../../../util/BalancerV2Utils.sol"; /// @title BalancerV2SwapExactAmountIn /// @notice A contract for executing direct swapExactAmountIn on Balancer V2 abstract contract BalancerV2SwapExactAmountIn is IBalancerV2SwapExactAmountIn, BalancerV2Utils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @inheritdoc IBalancerV2SwapExactAmountIn function swapExactAmountInOnBalancerV2( BalancerV2Data calldata balancerData, uint256 partnerAndFee, bytes calldata permit, bytes calldata data ) external payable whenNotPaused returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference balancerData uint256 quotedAmountOut = balancerData.quotedAmount; uint256 beneficiaryAndApproveFlag = balancerData.beneficiaryAndApproveFlag; uint256 amountIn = balancerData.fromAmount; uint256 minAmountOut = balancerData.toAmount; // Decode params (IERC20 srcToken, IERC20 destToken, address payable beneficiary, bool approve) = _decodeBalancerV2Params(beneficiaryAndApproveFlag, data); // Check if toAmount is valid if (minAmountOut == 0) { revert InvalidToAmount(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Check if srcToken is ETH if (srcToken.isETH(amountIn) == 0) { // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } srcToken.safeTransferFrom(msg.sender, address(this), amountIn); } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, address(this), amountIn); } // Check if approve is needed if (approve) { // Approve BALANCER_VAULT to spend srcToken srcToken.approve(BALANCER_VAULT); } } // Execute swap _callBalancerV2(data); // Check balance after swap receivedAmount = destToken.getBalance(address(this)); // Check if swap succeeded if (receivedAmount < minAmountOut) { revert InsufficientReturnAmount(); } // Process fees and transfer destToken to beneficiary return processSwapExactAmountInFeesAndTransfer( beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { ICurveV1SwapExactAmountIn } from "../../../interfaces/ICurveV1SwapExactAmountIn.sol"; // Libraries import { ERC20Utils } from "../../../libraries/ERC20Utils.sol"; // Types import { CurveV1Data } from "../../../AugustusV6Types.sol"; // Utils import { AugustusFees } from "../../../fees/AugustusFees.sol"; import { WETHUtils } from "../../../util/WETHUtils.sol"; import { Permit2Utils } from "../../../util/Permit2Utils.sol"; import { PauseUtils } from "../../../util/PauseUtils.sol"; /// @title CurveV1SwapExactAmountIn /// @notice A contract for executing direct CurveV1 swaps abstract contract CurveV1SwapExactAmountIn is ICurveV1SwapExactAmountIn, AugustusFees, WETHUtils, Permit2Utils, PauseUtils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @inheritdoc ICurveV1SwapExactAmountIn function swapExactAmountInOnCurveV1( CurveV1Data calldata curveV1Data, uint256 partnerAndFee, bytes calldata permit ) external payable whenNotPaused returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference curveV1Data IERC20 srcToken = curveV1Data.srcToken; IERC20 destToken = curveV1Data.destToken; uint256 amountIn = curveV1Data.fromAmount; uint256 minAmountOut = curveV1Data.toAmount; uint256 quotedAmountOut = curveV1Data.quotedAmount; address payable beneficiary = curveV1Data.beneficiary; uint256 curveAssets = curveV1Data.curveAssets; uint256 curveData = curveV1Data.curveData; // Check if toAmount is valid if (minAmountOut == 0) { revert InvalidToAmount(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Decode curveData // 160 bits for curve exchange address // 1 bit for approve flag // 2 bits for wrap flag // 2 bits for swap type flag address exchange; bool approveFlag; uint256 wrapFlag; uint256 swapType; // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { exchange := and(curveData, 0xffffffffffffffffffffffffffffffffffffffff) approveFlag := and(shr(160, curveData), 1) wrapFlag := and(shr(161, curveData), 3) swapType := and(shr(163, curveData), 3) } // Check if srcToken is ETH // Transfer srcToken to augustus if not ETH if (srcToken.isETH(amountIn) == 0) { // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } srcToken.safeTransferFrom(msg.sender, address(this), amountIn); } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, address(this), amountIn); } // Check if approve flag is set if (approveFlag) { // Approve exchange srcToken.approve(exchange); } } else { // Check if approve flag is set if (approveFlag) { // Approve exchange IERC20(WETH).approve(exchange); } } // Execute swap _executeSwapOnCurveV1(exchange, wrapFlag, swapType, curveAssets, amountIn); // Check balance after swap and unwrap if needed if (wrapFlag == 2) { // Received amount is WETH balance receivedAmount = IERC20(WETH).getBalance(address(this)); // Unwrap WETH WETH.withdraw(receivedAmount - 1); // Set receivedAmount to this contract's balance receivedAmount = address(this).balance; } else { // Received amount is destToken balance receivedAmount = destToken.getBalance(address(this)); } // Check if swap succeeded if (receivedAmount < minAmountOut) { revert InsufficientReturnAmount(); } // Process fees and transfer destToken to beneficiary return processSwapExactAmountInFeesAndTransfer( beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut ); } /*////////////////////////////////////////////////////////////// PRIVATE //////////////////////////////////////////////////////////////*/ function _executeSwapOnCurveV1( address exchange, uint256 wrapFlag, uint256 swapType, uint256 curveAssets, uint256 fromAmount ) private { // Load WETH address address weth = address(WETH); // solhint-disable-next-line no-inline-assembly assembly { // Load free memory pointer let ptr := mload(64) //----------------------------------------------------------------------------------- // Wrap ETH if needed //----------------------------------------------------------------------------------- // Check if wrap src flag is set if eq(wrapFlag, 1) { // Prepare call data for WETH.deposit() // Store function selector and mstore(ptr, 0xd0e30db000000000000000000000000000000000000000000000000000000000) // deposit() // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), weth, callvalue(), ptr, 4, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } //----------------------------------------------------------------------------------- // Execute swap //----------------------------------------------------------------------------------- // Prepare call data for external call // Check swap type switch swapType // 0x01 for EXCHANGE_UNDERLYING case 0x01 { // Store function selector for function exchange_underlying(int128,int128,uint256,uint256) mstore(ptr, 0xa6417ed600000000000000000000000000000000000000000000000000000000) // store selector mstore(add(ptr, 4), shr(128, curveAssets)) // store index i mstore(add(ptr, 36), and(curveAssets, 0xffffffffffffffffffffffffffffffff)) // store index j mstore(add(ptr, 68), fromAmount) // store fromAmount mstore(add(ptr, 100), 1) // store 1 // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), exchange, 0, ptr, 132, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } // 0x00(default) for EXCHANGE default { // check send eth wrap flag switch eq(wrapFlag, 0x03) // if it is not set, store selector for function exchange(int128,int128,uint256,uint256) case 1 { mstore(ptr, 0x3df0212400000000000000000000000000000000000000000000000000000000) // store selector mstore(add(ptr, 4), shr(128, curveAssets)) // store index i mstore(add(ptr, 36), and(curveAssets, 0xffffffffffffffffffffffffffffffff)) // store index j mstore(add(ptr, 68), fromAmount) // store fromAmount mstore(add(ptr, 100), 1) // store 1 // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), exchange, callvalue(), ptr, 132, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } // if it is set, store selector for function exchange(int128,int128,uint256,uint256) default { mstore(ptr, 0x3df0212400000000000000000000000000000000000000000000000000000000) // store selector mstore(add(ptr, 4), shr(128, curveAssets)) // store index i mstore(add(ptr, 36), and(curveAssets, 0xffffffffffffffffffffffffffffffff)) // store index j mstore(add(ptr, 68), fromAmount) // store fromAmount mstore(add(ptr, 100), 1) // store 1 // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), exchange, 0, ptr, 132, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { ICurveV2SwapExactAmountIn } from "../../../interfaces/ICurveV2SwapExactAmountIn.sol"; // Libraries import { ERC20Utils } from "../../../libraries/ERC20Utils.sol"; // Types import { CurveV2Data } from "../../../AugustusV6Types.sol"; // Utils import { AugustusFees } from "../../../fees/AugustusFees.sol"; import { WETHUtils } from "../../../util/WETHUtils.sol"; import { Permit2Utils } from "../../../util/Permit2Utils.sol"; import { PauseUtils } from "../../../util/PauseUtils.sol"; /// @title CurveV2SwapExactAmountIn /// @notice A contract for executing direct CurveV2 swaps abstract contract CurveV2SwapExactAmountIn is ICurveV2SwapExactAmountIn, AugustusFees, WETHUtils, Permit2Utils, PauseUtils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @inheritdoc ICurveV2SwapExactAmountIn function swapExactAmountInOnCurveV2( CurveV2Data calldata curveV2Data, uint256 partnerAndFee, bytes calldata permit ) external payable whenNotPaused returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference curveData IERC20 srcToken = curveV2Data.srcToken; IERC20 destToken = curveV2Data.destToken; uint256 amountIn = curveV2Data.fromAmount; uint256 minAmountOut = curveV2Data.toAmount; uint256 quotedAmountOut = curveV2Data.quotedAmount; address payable beneficiary = curveV2Data.beneficiary; uint256 i = curveV2Data.i; uint256 j = curveV2Data.j; address poolAddress = curveV2Data.poolAddress; uint256 curveData = curveV2Data.curveData; // Check if toAmount is valid if (minAmountOut == 0) { revert InvalidToAmount(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Decode curveData // 160 bits for curve exchange address // 1 bit for approve flag // 2 bits for wrap flag // 2 bits for swap type flag address exchange; bool approveFlag; uint256 wrapFlag; uint256 swapType; // solhint-disable-next-line no-inline-assembly assembly { exchange := and(curveData, 0xffffffffffffffffffffffffffffffffffffffff) approveFlag := and(shr(160, curveData), 1) wrapFlag := and(shr(161, curveData), 3) swapType := and(shr(163, curveData), 3) } // Check if srcToken is ETH // Transfer srcToken to augustus if not ETH if (srcToken.isETH(amountIn) == 0) { // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } srcToken.safeTransferFrom(msg.sender, address(this), amountIn); } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, address(this), amountIn); } // Check if approve flag is set if (approveFlag) { // Approve exchange srcToken.approve(exchange); } } else { // Check if approve flag is set if (approveFlag) { // Approve exchange IERC20(WETH).approve(exchange); } } // Execute swap _executeSwapOnCurveV2(exchange, wrapFlag, swapType, i, j, amountIn, poolAddress); // Check balance after swap and unwrap if needed if (wrapFlag == 2) { // Received amount is WETH balance receivedAmount = IERC20(WETH).getBalance(address(this)); // Unwrap WETH WETH.withdraw(receivedAmount - 1); // Set receivedAmount to this contract's balance receivedAmount = address(this).balance; } else { // Received amount is destToken balance receivedAmount = destToken.getBalance(address(this)); } // Check if swap succeeded if (receivedAmount < minAmountOut) { revert InsufficientReturnAmount(); } // Process fees and transfer destToken to beneficiary return processSwapExactAmountInFeesAndTransfer( beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut ); } /*////////////////////////////////////////////////////////////// PRIVATE //////////////////////////////////////////////////////////////*/ function _executeSwapOnCurveV2( address exchange, uint256 wrapFlag, uint256 swapType, uint256 i, uint256 j, uint256 fromAmount, address poolAddress ) private { // Load WETH address address weth = address(WETH); // solhint-disable-next-line no-inline-assembly assembly { // Load free memory pointer let ptr := mload(64) //----------------------------------------------------------------------------------- // Wrap ETH if needed //----------------------------------------------------------------------------------- // Check if wrap src flag is set if eq(wrapFlag, 1) { // Prepare call data for WETH.deposit() // Store function selector and mstore(ptr, 0xd0e30db000000000000000000000000000000000000000000000000000000000) // deposit() // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), weth, callvalue(), ptr, 4, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } //----------------------------------------------------------------------------------- // Execute swap //----------------------------------------------------------------------------------- // Prepare call data for external call // Check swap type switch swapType // 0x01 for EXCHANGE_UNDERLYING case 0x01 { // Store function selector for function exchange_underlying(uint256,uint256,uint256,uint256) mstore(ptr, 0x65b2489b00000000000000000000000000000000000000000000000000000000) // store selector mstore(add(ptr, 4), i) // store index i mstore(add(ptr, 36), j) // store index j mstore(add(ptr, 68), fromAmount) // store fromAmount mstore(add(ptr, 100), 1) // store 1 // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), exchange, 0, ptr, 132, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } // 0x02 for EXCHANGE_GENERIC_FACTORY_ZAP case 0x02 { // Store function selector for function exchange(address,uint256,uint256,uint256,uint256) mstore(ptr, 0x64a1455800000000000000000000000000000000000000000000000000000000) mstore(add(ptr, 4), poolAddress) // store poolAddress mstore(add(ptr, 36), i) // store index i mstore(add(ptr, 68), j) // store index j mstore(add(ptr, 100), fromAmount) // store fromAmount mstore(add(ptr, 132), 1) // store 1 // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), exchange, 0, ptr, 164, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } // 0x00(default) for EXCHANGE default { // check send eth wrap flag switch eq(wrapFlag, 0x03) // if it is not set, store selector for function exchange(uint256,uint256,uint256,uint256,bool) case 1 { mstore(ptr, 0x394747c500000000000000000000000000000000000000000000000000000000) // store selector mstore(add(ptr, 4), i) // store index i mstore(add(ptr, 36), j) // store index j mstore(add(ptr, 68), fromAmount) // store fromAmount mstore(add(ptr, 100), 1) // store 1 mstore(add(ptr, 132), 1) // store true // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), exchange, callvalue(), ptr, 164, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } // if it is set, store selector for function exchange(uint256,uint256,uint256,uint256) default { mstore(ptr, 0x5b41b90800000000000000000000000000000000000000000000000000000000) // store selector mstore(add(ptr, 4), i) // store index i mstore(add(ptr, 36), j) // store index j mstore(add(ptr, 68), fromAmount) // store fromAmount mstore(add(ptr, 100), 1) // store 1 // Perform the external call with the prepared calldata // Check the outcome of the call and handle failure if iszero(call(gas(), exchange, 0, ptr, 132, 0, 0)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } } } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IUniswapV2SwapExactAmountIn } from "../../../interfaces/IUniswapV2SwapExactAmountIn.sol"; // Libraries import { ERC20Utils } from "../../../libraries/ERC20Utils.sol"; // Types import { UniswapV2Data } from "../../../AugustusV6Types.sol"; // Utils import { UniswapV2Utils } from "../../../util/UniswapV2Utils.sol"; /// @title UniswapV2SwapExactAmountIn /// @notice A contract for executing direct swapExactAmountIn on UniswapV2 pools abstract contract UniswapV2SwapExactAmountIn is IUniswapV2SwapExactAmountIn, UniswapV2Utils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// SWAP //////////////////////////////////////////////////////////////*/ /// @inheritdoc IUniswapV2SwapExactAmountIn function swapExactAmountInOnUniswapV2( UniswapV2Data calldata uniData, uint256 partnerAndFee, bytes calldata permit ) external payable whenNotPaused returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference uniData IERC20 srcToken = uniData.srcToken; IERC20 destToken = uniData.destToken; uint256 amountIn = uniData.fromAmount; uint256 minAmountOut = uniData.toAmount; uint256 quotedAmountOut = uniData.quotedAmount; address payable beneficiary = uniData.beneficiary; bytes calldata pools = uniData.pools; // Initialize payer address payer = msg.sender; // Check if toAmount is valid if (minAmountOut == 0) { revert InvalidToAmount(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Check if we need to wrap or permit if (srcToken.isETH(amountIn) == 0) { // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } } } else { // If it is ETH. wrap it to WETH WETH.deposit{ value: amountIn }(); // Set srcToken to WETH srcToken = WETH; // Set payer to this contract payer = address(this); } // Execute swap _callUniswapV2PoolsSwapExactIn(amountIn, srcToken, pools, payer, permit); // Check if destToken is ETH and unwrap if (address(destToken) == address(ERC20Utils.ETH)) { // Check balance of WETH receivedAmount = IERC20(WETH).getBalance(address(this)); // Unwrap WETH WETH.withdraw(receivedAmount - 1); // Set receivedAmount to this contract's balance receivedAmount = address(this).balance; } else { // Othwerwise check balance of destToken receivedAmount = destToken.getBalance(address(this)); } // Check if swap succeeded if (receivedAmount < minAmountOut) { revert InsufficientReturnAmount(); } // Process fees and transfer destToken to beneficiary return processSwapExactAmountInFeesAndTransfer( beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IUniswapV3SwapExactAmountIn } from "../../../interfaces/IUniswapV3SwapExactAmountIn.sol"; // Libraries import { ERC20Utils } from "../../../libraries/ERC20Utils.sol"; import { SafeCastLib } from "@solady/utils/SafeCastLib.sol"; // Types import { UniswapV3Data } from "../../../AugustusV6Types.sol"; // Utils import { UniswapV3Utils } from "../../../util/UniswapV3Utils.sol"; /// @title UniswapV3SwapExactAmountIn /// @notice A contract for executing direct swapExactAmountIn on Uniswap V3 abstract contract UniswapV3SwapExactAmountIn is IUniswapV3SwapExactAmountIn, UniswapV3Utils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; using SafeCastLib for uint256; /*////////////////////////////////////////////////////////////// SWAP //////////////////////////////////////////////////////////////*/ /// @inheritdoc IUniswapV3SwapExactAmountIn function swapExactAmountInOnUniswapV3( UniswapV3Data calldata uniData, uint256 partnerAndFee, bytes calldata permit ) external payable whenNotPaused returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference uniData IERC20 srcToken = uniData.srcToken; IERC20 destToken = uniData.destToken; uint256 amountIn = uniData.fromAmount; uint256 minAmountOut = uniData.toAmount; uint256 quotedAmountOut = uniData.quotedAmount; address payable beneficiary = uniData.beneficiary; bytes calldata pools = uniData.pools; // Check if toAmount is valid if (minAmountOut == 0) { revert InvalidToAmount(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Address that will pay for the swap address fromAddress = msg.sender; // Check if we need to wrap or permit if (srcToken.isETH(amountIn) == 0) { // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } } } else { // If it is ETH. wrap it to WETH WETH.deposit{ value: amountIn }(); // Swap will be paid from this contract fromAddress = address(this); } // Execute swap receivedAmount = _callUniswapV3PoolsSwapExactAmountIn(amountIn.toInt256(), pools, fromAddress, permit); // Check if swap succeeded if (receivedAmount < minAmountOut) { revert InsufficientReturnAmount(); } // Check if destToken is ETH and unwrap if (address(destToken) == address(ERC20Utils.ETH)) { // Unwrap WETH WETH.withdraw(receivedAmount); } // Process fees and transfer destToken to beneficiary return processSwapExactAmountInFeesAndTransferUniV3( beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IBalancerV2SwapExactAmountOut } from "../../../interfaces/IBalancerV2SwapExactAmountOut.sol"; // Libraries import { ERC20Utils } from "../../../libraries/ERC20Utils.sol"; // Types import { BalancerV2Data } from "../../../AugustusV6Types.sol"; // Utils import { BalancerV2Utils } from "../../../util/BalancerV2Utils.sol"; /// @title BalancerV2SwapExactAmountOut /// @notice A contract for executing direct swapExactAmountOut on BalancerV2 pools abstract contract BalancerV2SwapExactAmountOut is IBalancerV2SwapExactAmountOut, BalancerV2Utils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT //////////////////////////////////////////////////////////////*/ /// @inheritdoc IBalancerV2SwapExactAmountOut function swapExactAmountOutOnBalancerV2( BalancerV2Data calldata balancerData, uint256 partnerAndFee, bytes calldata permit, bytes calldata data ) external payable whenNotPaused returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference balancerData uint256 quotedAmountIn = balancerData.quotedAmount; uint256 beneficiaryAndApproveFlag = balancerData.beneficiaryAndApproveFlag; uint256 maxAmountIn = balancerData.fromAmount; uint256 amountOut = balancerData.toAmount; // Decode params (IERC20 srcToken, IERC20 destToken, address payable beneficiary, bool approve) = _decodeBalancerV2Params(beneficiaryAndApproveFlag, data); // Make sure srcToken and destToken are different if (srcToken == destToken) { revert ArbitrageNotSupported(); } // Check if toAmount is valid if (amountOut == 0) { revert InvalidToAmount(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Check contract balance uint256 balanceBefore = srcToken.getBalance(address(this)); // Check if srcToken is ETH if (srcToken.isETH(maxAmountIn) == 0) { // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } srcToken.safeTransferFrom(msg.sender, address(this), maxAmountIn); } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, address(this), maxAmountIn); } // Check if approve is needed if (approve) { // Approve BALANCER_VAULT to spend srcToken srcToken.approve(BALANCER_VAULT); } } else { // If srcToken is ETH, we have to deduct msg.value from balanceBefore balanceBefore = balanceBefore - msg.value; } // Execute swap _callBalancerV2(data); // Check balance of destToken receivedAmount = destToken.getBalance(address(this)); // Check balance of srcToken, deducting the balance before the swap if it is greater than 1 uint256 remainingAmount = srcToken.getBalance(address(this)) - (balanceBefore > 1 ? balanceBefore : 0); // Check if swap succeeded if (receivedAmount < amountOut) { revert InsufficientReturnAmount(); } // Process fees and transfer destToken and srcToken to beneficiary return processSwapExactAmountOutFeesAndTransfer( beneficiary, srcToken, destToken, partnerAndFee, maxAmountIn, remainingAmount, receivedAmount, quotedAmountIn ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IUniswapV2SwapExactAmountOut } from "../../../interfaces/IUniswapV2SwapExactAmountOut.sol"; // Libraries import { ERC20Utils } from "../../../libraries/ERC20Utils.sol"; // Types import { UniswapV2Data } from "../../../AugustusV6Types.sol"; // Utils import { UniswapV2Utils } from "../../../util/UniswapV2Utils.sol"; /// @title UniswapV2SwapExactAmountOut /// @notice A contract for executing direct swapExactAmountOut on UniswapV2 pools abstract contract UniswapV2SwapExactAmountOut is IUniswapV2SwapExactAmountOut, UniswapV2Utils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT //////////////////////////////////////////////////////////////*/ /// @inheritdoc IUniswapV2SwapExactAmountOut function swapExactAmountOutOnUniswapV2( UniswapV2Data calldata uniData, uint256 partnerAndFee, bytes calldata permit ) external payable whenNotPaused returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference uniData IERC20 srcToken = uniData.srcToken; IERC20 destToken = uniData.destToken; uint256 maxAmountIn = uniData.fromAmount; uint256 amountOut = uniData.toAmount; uint256 quotedAmountIn = uniData.quotedAmount; address payable beneficiary = uniData.beneficiary; bytes calldata pools = uniData.pools; // Check if toAmount is valid if (amountOut == 0) { revert InvalidToAmount(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Init balanceBefore uint256 balanceBefore; // Check if srcToken is ETH bool isFromETH = srcToken.isETH(maxAmountIn) != 0; // Check if we need to wrap or permit if (isFromETH) { // Check WETH balance before balanceBefore = IERC20(WETH).getBalance(address(this)); // If it is ETH. wrap it to WETH WETH.deposit{ value: maxAmountIn }(); // Set srcToken to WETH srcToken = WETH; } else { // Check srcToken balance before balanceBefore = srcToken.getBalance(address(this)); // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } srcToken.safeTransferFrom(msg.sender, address(this), maxAmountIn); } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, address(this), maxAmountIn); } } // Make sure srcToken and destToken are different if (srcToken == destToken) { revert ArbitrageNotSupported(); } // Execute swap _callUniswapV2PoolsSwapExactOut(amountOut, srcToken, pools); // Check if destToken is ETH and unwrap if (address(destToken) == address(ERC20Utils.ETH)) { // Make sure srcToken was not WETH if (srcToken == WETH) { revert ArbitrageNotSupported(); } // Check balance of WETH receivedAmount = IERC20(WETH).getBalance(address(this)); // Leave dust if receivedAmount > amountOut if (receivedAmount > amountOut) { --receivedAmount; } // Unwrap WETH WETH.withdraw(receivedAmount); // Set receivedAmount to this contract's balance receivedAmount = address(this).balance; } else { // Othwerwise check balance of destToken receivedAmount = destToken.getBalance(address(this)); } // Check balance of srcToken uint256 remainingAmount = srcToken.getBalance(address(this)); // Check if swap succeeded if (receivedAmount < amountOut) { revert InsufficientReturnAmount(); } // Check if srcToken is ETH and unwrap if there is remaining amount if (isFromETH) { // Check native balance before uint256 nativeBalanceBefore = address(this).balance; // If balanceBefore is greater than 1, deduct it from remainingAmount remainingAmount = remainingAmount - (balanceBefore > 1 ? balanceBefore : 0); // Withdraw remaining WETH if any if (remainingAmount > 1) { WETH.withdraw(remainingAmount - 1); } srcToken = ERC20Utils.ETH; // If native balance before is greater than 1, deduct it from remainingAmount remainingAmount = address(this).balance - (nativeBalanceBefore > 1 ? nativeBalanceBefore : 0); } else { // Otherwise, if balanceBefore is greater than 1, deduct it from remainingAmount remainingAmount = remainingAmount - (balanceBefore > 1 ? balanceBefore : 0); } // Process fees and transfer destToken and srcToken to beneficiary return processSwapExactAmountOutFeesAndTransfer( beneficiary, srcToken, destToken, partnerAndFee, maxAmountIn, remainingAmount, receivedAmount, quotedAmountIn ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IUniswapV3SwapExactAmountOut } from "../../../interfaces/IUniswapV3SwapExactAmountOut.sol"; // Libraries import { ERC20Utils } from "../../../libraries/ERC20Utils.sol"; import { SafeCastLib } from "@solady/utils/SafeCastLib.sol"; // Types import { UniswapV3Data } from "../../../AugustusV6Types.sol"; // Utils import { UniswapV3Utils } from "../../../util/UniswapV3Utils.sol"; /// @title UniswapV3SwapExactAmountOut /// @notice A contract for executing direct swapExactAmountOut on UniswapV3 pools abstract contract UniswapV3SwapExactAmountOut is IUniswapV3SwapExactAmountOut, UniswapV3Utils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; using SafeCastLib for uint256; /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT //////////////////////////////////////////////////////////////*/ /// @inheritdoc IUniswapV3SwapExactAmountOut function swapExactAmountOutOnUniswapV3( UniswapV3Data calldata uniData, uint256 partnerAndFee, bytes calldata permit ) external payable whenNotPaused returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference uniData IERC20 srcToken = uniData.srcToken; IERC20 destToken = uniData.destToken; uint256 maxAmountIn = uniData.fromAmount; uint256 amountOut = uniData.toAmount; uint256 quotedAmountIn = uniData.quotedAmount; address payable beneficiary = uniData.beneficiary; bytes calldata pools = uniData.pools; // Check if toAmount is valid if (amountOut == 0) { revert InvalidToAmount(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Address that will pay for the swap address fromAddress = msg.sender; // Check if srcToken is ETH bool isFromETH = srcToken.isETH(maxAmountIn) != 0; // If pools.length > 96, we are going to do a multi-pool swap bool isMultiplePools = pools.length > 96; // Init balance before variables uint256 senderBalanceBefore; uint256 balanceBefore; // Check if we need to wrap or permit if (isFromETH) { // Check WETH balance before balanceBefore = IERC20(WETH).getBalance(address(this)); // If it is ETH. wrap it to WETH WETH.deposit{ value: maxAmountIn }(); // Swap will be paid from this contract fromAddress = address(this); // Set srcToken to WETH srcToken = WETH; } else { // Check srcToken balance before balanceBefore = srcToken.getBalance(address(this)); // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } // if we're using multiple pools, we need to store the pre-swap balance of srcToken if (isMultiplePools) { senderBalanceBefore = srcToken.getBalance(msg.sender); } } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, address(this), maxAmountIn); // Swap will be paid from this contract fromAddress = address(this); } } // Make sure srcToken and destToken are different if (srcToken == destToken) { revert ArbitrageNotSupported(); } // Execute swap (spentAmount, receivedAmount) = _callUniswapV3PoolsSwapExactAmountOut((-amountOut.toInt256()), pools, fromAddress); // Check if swap succeeded if (receivedAmount < amountOut) { revert InsufficientReturnAmount(); } // Check if destToken is ETH and unwrap if (address(destToken) == address(ERC20Utils.ETH)) { // Make sure srcToken was not WETH if (srcToken == WETH) { revert ArbitrageNotSupported(); } // Unwrap WETH WETH.withdraw(receivedAmount); } // Iniiialize remainingAmount uint256 remainingAmount; // Check if payer is this contract if (fromAddress == address(this)) { // If srcTokenwas ETH, we need to withdraw remaining WETH if any if (isFromETH) { // Check native balance before uint256 nativeBalanceBefore = address(this).balance; // Check balance of WETH, If balanceBefore is greater than 1, deduct it from remainingAmount remainingAmount = IERC20(WETH).getBalance(address(this)) - (balanceBefore > 1 ? balanceBefore : 0); // Withdraw remaining WETH if any if (remainingAmount > 1) { // Unwrap WETH WETH.withdraw(remainingAmount - 1); // If native balance before is greater than 1, deduct it from remainingAmount remainingAmount = address(this).balance - (nativeBalanceBefore > 1 ? nativeBalanceBefore : 0); } // Set srcToken to ETH srcToken = ERC20Utils.ETH; } else { // If we have executed multi-pool swap, we need to fetch the remaining amount from balance if (isMultiplePools) { // Calculate spent amount and remaining amount, If balanceBefore is greater than 1, deduct it from // remainingAmount remainingAmount = srcToken.getBalance(address(this)) - (balanceBefore > 1 ? balanceBefore : 0); } else { // Otherwise, remaining amount is the difference between the spent amount and the remaining balance remainingAmount = maxAmountIn - spentAmount; } } // Process fees using processSwapExactAmountOutFeesAndTransfer return processSwapExactAmountOutFeesAndTransfer( beneficiary, srcToken, destToken, partnerAndFee, maxAmountIn, remainingAmount, receivedAmount, quotedAmountIn ); } else { // If we have executed multi-pool swap, we need to re-calculate the remaining amount and spent amount if (isMultiplePools) { // Calculate spent amount and remaining amount remainingAmount = srcToken.getBalance(msg.sender); spentAmount = senderBalanceBefore - remainingAmount; } // Process fees and transfer destToken and srcToken to feeVault or partner and // feeWallet if needed return processSwapExactAmountOutFeesAndTransferUniV3( beneficiary, srcToken, destToken, partnerAndFee, maxAmountIn, receivedAmount, spentAmount, quotedAmountIn ); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IAugustusFeeVault } from "../interfaces/IAugustusFeeVault.sol"; import { IAugustusFees } from "../interfaces/IAugustusFees.sol"; // Libraries import { ERC20Utils } from "../libraries/ERC20Utils.sol"; // Storage import { AugustusStorage } from "../storage/AugustusStorage.sol"; /// @title AugustusFees /// @notice Contract for handling fees contract AugustusFees is AugustusStorage, IAugustusFees { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @dev Fee share constants uint256 public constant PARTNER_SHARE_PERCENT = 8500; uint256 public constant MAX_FEE_PERCENT = 200; uint256 public constant SURPLUS_PERCENT = 100; uint256 public constant PARASWAP_REFERRAL_SHARE = 5000; uint256 public constant PARTNER_REFERRAL_SHARE = 2500; uint256 public constant PARASWAP_SURPLUS_SHARE = 5000; uint256 public constant PARASWAP_SLIPPAGE_SHARE = 10_000; uint256 public constant MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI = 11; /// @dev Masks for unpacking feeData uint256 private constant FEE_PERCENT_IN_BASIS_POINTS_MASK = 0x3FFF; uint256 private constant IS_USER_SURPLUS_MASK = 1 << 90; uint256 private constant IS_DIRECT_TRANSFER_MASK = 1 << 91; uint256 private constant IS_CAP_SURPLUS_MASK = 1 << 92; uint256 private constant IS_SKIP_BLACKLIST_MASK = 1 << 93; uint256 private constant IS_REFERRAL_MASK = 1 << 94; uint256 private constant IS_TAKE_SURPLUS_MASK = 1 << 95; /// @dev A contact that stores fees collected by the protocol IAugustusFeeVault public immutable FEE_VAULT; // solhint-disable-line var-name-mixedcase /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _feeVault) { FEE_VAULT = IAugustusFeeVault(_feeVault); } /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN FEES //////////////////////////////////////////////////////////////*/ /// @notice Process swapExactAmountIn fees and transfer the received amount to the beneficiary /// @param destToken The received token from the swapExactAmountIn /// @param partnerAndFee Packed partner and fee data /// @param receivedAmount The amount of destToken received from the swapExactAmountIn /// @param quotedAmount The quoted expected amount of destToken /// @return returnAmount The amount of destToken transfered to the beneficiary /// @return paraswapFeeShare The share of the fees for Paraswap /// @return partnerFeeShare The share of the fees for the partner function processSwapExactAmountInFeesAndTransfer( address beneficiary, IERC20 destToken, uint256 partnerAndFee, uint256 receivedAmount, uint256 quotedAmount ) internal returns (uint256 returnAmount, uint256 paraswapFeeShare, uint256 partnerFeeShare) { // initialize the surplus uint256 surplus; // parse partner and fee data (address payable partner, uint256 feeData) = parsePartnerAndFeeData(partnerAndFee); // calculate the surplus, we expect there to be 1 wei dust left which we should // not take into account when determining if there is surplus, we only take the // surplus if it is greater than MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI if (receivedAmount > quotedAmount + MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI) { surplus = receivedAmount - quotedAmount; // if the cap surplus flag is passed, we cap the surplus to 1% of the quoted amount if (feeData & IS_CAP_SURPLUS_MASK != 0) { uint256 cappedSurplus = (SURPLUS_PERCENT * quotedAmount) / 10_000; surplus = surplus > cappedSurplus ? cappedSurplus : surplus; } } // calculate remainingAmount uint256 remainingAmount = receivedAmount - surplus; // if partner address is not 0x0 if (partner != address(0x0)) { // Check if skip blacklist flag is true bool skipBlacklist = feeData & IS_SKIP_BLACKLIST_MASK != 0; // Check if token is blacklisted bool isBlacklisted = blacklistedTokens[destToken]; // If the token is blacklisted and the skipBlacklist flag is false, // send the received amount to the beneficiary, we won't process fees if (!skipBlacklist && isBlacklisted) { // transfer the received amount to the beneficiary, keeping 1 wei dust _transferAndLeaveDust(destToken, beneficiary, receivedAmount); return (receivedAmount - 1, 0, 0); } // Check if direct transfer flag is true bool isDirectTransfer = feeData & IS_DIRECT_TRANSFER_MASK != 0; // partner takes fixed fees feePercent is greater than 0 uint256 feePercent = _getAdjustedFeePercent(feeData); if (feePercent > 0) { // fee base = min (receivedAmount, quotedAmount + surplus) uint256 feeBase = receivedAmount > quotedAmount + surplus ? quotedAmount + surplus : receivedAmount; // calculate fixed fees uint256 fee = (feeBase * feePercent) / 10_000; partnerFeeShare = (fee * PARTNER_SHARE_PERCENT) / 10_000; paraswapFeeShare = fee - partnerFeeShare; // distrubite fees from destToken returnAmount = _distributeFees( receivedAmount, destToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the return amount to the beneficiary, keeping 1 wei dust _transferAndLeaveDust(destToken, beneficiary, returnAmount); return (returnAmount - 1, paraswapFeeShare, partnerFeeShare); } // if slippage is postive and referral flag is true else if (feeData & IS_REFERRAL_MASK != 0) { if (surplus > 0) { // the split is 50% for paraswap, 25% for the referrer and 25% for the user paraswapFeeShare = (surplus * PARASWAP_REFERRAL_SHARE) / 10_000; partnerFeeShare = (surplus * PARTNER_REFERRAL_SHARE) / 10_000; // distribute fees from destToken returnAmount = _distributeFees( receivedAmount, destToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the return amount to the beneficiary, keeping 1 wei dust _transferAndLeaveDust(destToken, beneficiary, returnAmount); return (returnAmount - 1, paraswapFeeShare, partnerFeeShare); } } // if slippage is positive and takeSurplus flag is true else if (feeData & IS_TAKE_SURPLUS_MASK != 0) { if (surplus > 0) { // paraswap takes 50% of the surplus and partner takes the other 50% paraswapFeeShare = (surplus * PARASWAP_SURPLUS_SHARE) / 10_000; partnerFeeShare = surplus - paraswapFeeShare; // If user surplus flag is true, transfer the partner share to the user instead of the partner if (feeData & IS_USER_SURPLUS_MASK != 0) { partnerFeeShare = 0; // Transfer the paraswap share directly to the fee wallet isDirectTransfer = true; } // distrubite fees from destToken, partner takes 50% of the surplus // and paraswap takes the other 50% returnAmount = _distributeFees( receivedAmount, destToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the return amount to the beneficiary, keeping 1 wei dust _transferAndLeaveDust(destToken, beneficiary, returnAmount); return (returnAmount - 1, paraswapFeeShare, partnerFeeShare); } } } // if slippage is positive and partner address is 0x0 or fee percent is 0 // paraswap will take the surplus and transfer the rest to the beneficiary // if there is no positive slippage, transfer the received amount to the beneficiary if (surplus > 0) { // If the token is blacklisted, send the received amount to the beneficiary // we won't process fees if (blacklistedTokens[destToken]) { // transfer the received amount to the beneficiary, keeping 1 wei dust _transferAndLeaveDust(destToken, beneficiary, receivedAmount); return (receivedAmount - 1, 0, 0); } // transfer the remaining amount to the beneficiary, keeping 1 wei dust _transferAndLeaveDust(destToken, beneficiary, remainingAmount); // transfer the surplus to the fee wallet destToken.safeTransfer(feeWallet, surplus); return (remainingAmount - 1, surplus, 0); } else { // transfer the received amount to the beneficiary, keeping 1 wei dust _transferAndLeaveDust(destToken, beneficiary, receivedAmount); return (receivedAmount - 1, 0, 0); } } /// @notice Process swapExactAmountIn fees and transfer the received amount to the beneficiary /// @param destToken The received token from the swapExactAmountIn /// @param partnerAndFee Packed partner and fee data /// @param receivedAmount The amount of destToken received from the swapExactAmountIn /// @param quotedAmount The quoted expected amount of destToken /// @return returnAmount The amount of destToken transfered to the beneficiary /// @return paraswapFeeShare The share of the fees for Paraswap /// @return partnerFeeShare The share of the fees for the partner function processSwapExactAmountInFeesAndTransferUniV3( address beneficiary, IERC20 destToken, uint256 partnerAndFee, uint256 receivedAmount, uint256 quotedAmount ) internal returns (uint256 returnAmount, uint256 paraswapFeeShare, uint256 partnerFeeShare) { // initialize the surplus uint256 surplus; // parse partner and fee data (address payable partner, uint256 feeData) = parsePartnerAndFeeData(partnerAndFee); // calculate the surplus, we do not take the surplus into account if it is less than // MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI if (receivedAmount > quotedAmount + MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI) { surplus = receivedAmount - quotedAmount; // if the cap surplus flag is passed, we cap the surplus to 1% of the quoted amount if (feeData & IS_CAP_SURPLUS_MASK != 0) { uint256 cappedSurplus = (SURPLUS_PERCENT * quotedAmount) / 10_000; surplus = surplus > cappedSurplus ? cappedSurplus : surplus; } } // calculate remainingAmount uint256 remainingAmount = receivedAmount - surplus; // if partner address is not 0x0 if (partner != address(0x0)) { // Check if skip blacklist flag is true bool skipBlacklist = feeData & IS_SKIP_BLACKLIST_MASK != 0; // Check if token is blacklisted bool isBlacklisted = blacklistedTokens[destToken]; // If the token is blacklisted and the skipBlacklist flag is false, // send the received amount to the beneficiary, we won't process fees if (!skipBlacklist && isBlacklisted) { // transfer the received amount to the beneficiary destToken.safeTransfer(beneficiary, receivedAmount); return (receivedAmount, 0, 0); } // Check if direct transfer flag is true bool isDirectTransfer = feeData & IS_DIRECT_TRANSFER_MASK != 0; // partner takes fixed fees feePercent is greater than 0 uint256 feePercent = _getAdjustedFeePercent(feeData); if (feePercent > 0) { // fee base = min (receivedAmount, quotedAmount + surplus) uint256 feeBase = receivedAmount > quotedAmount + surplus ? quotedAmount + surplus : receivedAmount; // calculate fixed fees uint256 fee = (feeBase * feePercent) / 10_000; partnerFeeShare = (fee * PARTNER_SHARE_PERCENT) / 10_000; paraswapFeeShare = fee - partnerFeeShare; // distrubite fees from destToken returnAmount = _distributeFees( receivedAmount, destToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the return amount to the beneficiary destToken.safeTransfer(beneficiary, returnAmount); return (returnAmount, paraswapFeeShare, partnerFeeShare); } // if slippage is postive and referral flag is true else if (feeData & IS_REFERRAL_MASK != 0) { if (surplus > 0) { // the split is 50% for paraswap, 25% for the referrer and 25% for the user paraswapFeeShare = (surplus * PARASWAP_REFERRAL_SHARE) / 10_000; partnerFeeShare = (surplus * PARTNER_REFERRAL_SHARE) / 10_000; // distribute fees from destToken returnAmount = _distributeFees( receivedAmount, destToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the return amount to the beneficiary destToken.safeTransfer(beneficiary, returnAmount); return (returnAmount, paraswapFeeShare, partnerFeeShare); } } // if slippage is positive and takeSurplus flag is true else if (feeData & IS_TAKE_SURPLUS_MASK != 0) { if (surplus > 0) { // paraswap takes 50% of the surplus and partner takes the other 50% paraswapFeeShare = (surplus * PARASWAP_SURPLUS_SHARE) / 10_000; partnerFeeShare = surplus - paraswapFeeShare; // If user surplus flag is true, transfer the partner share to the user instead of the partner if (feeData & IS_USER_SURPLUS_MASK != 0) { partnerFeeShare = 0; // Transfer the paraswap share directly to the fee wallet isDirectTransfer = true; } // distrubite fees from destToken, partner takes 50% of the surplus // and paraswap takes the other 50% returnAmount = _distributeFees( receivedAmount, destToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the return amount to the beneficiary, destToken.safeTransfer(beneficiary, returnAmount); return (returnAmount, paraswapFeeShare, partnerFeeShare); } } } // if slippage is positive and partner address is 0x0 or fee percent is 0 // paraswap will take the surplus and transfer the rest to the beneficiary // if there is no positive slippage, transfer the received amount to the beneficiary if (surplus > 0) { // If the token is blacklisted, send the received amount to the beneficiary // we won't process fees if (blacklistedTokens[destToken]) { // transfer the received amount to the beneficiary destToken.safeTransfer(beneficiary, receivedAmount); return (receivedAmount, 0, 0); } // transfer the remaining amount to the beneficiary destToken.safeTransfer(beneficiary, remainingAmount); // transfer the surplus to the fee wallet destToken.safeTransfer(feeWallet, surplus); return (remainingAmount, surplus, 0); } else { // transfer the received amount to the beneficiary destToken.safeTransfer(beneficiary, receivedAmount); return (receivedAmount, 0, 0); } } /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT FEES //////////////////////////////////////////////////////////////*/ /// @notice Process swapExactAmountOut fees and transfer the received amount and remaining amount to the /// beneficiary /// @param srcToken The token used to swapExactAmountOut /// @param destToken The token received from the swapExactAmountOut /// @param partnerAndFee Packed partner and fee data /// @param maxAmountIn The amount of srcToken passed to the swapExactAmountOut /// @param receivedAmount The amount of destToken received from the swapExactAmountOut /// @param quotedAmount The quoted expected amount of srcToken to be used to swapExactAmountOut /// @return spentAmount The amount of srcToken used to swapExactAmountOut /// @return outAmount The amount of destToken transfered to the beneficiary /// @return paraswapFeeShare The share of the fees for Paraswap /// @return partnerFeeShare The share of the fees for the partner function processSwapExactAmountOutFeesAndTransfer( address beneficiary, IERC20 srcToken, IERC20 destToken, uint256 partnerAndFee, uint256 maxAmountIn, uint256 remainingAmount, uint256 receivedAmount, uint256 quotedAmount ) internal returns (uint256 spentAmount, uint256 outAmount, uint256 paraswapFeeShare, uint256 partnerFeeShare) { // calculate the amount used to swapExactAmountOut spentAmount = maxAmountIn - (remainingAmount > 0 ? remainingAmount - 1 : remainingAmount); // initialize the surplus uint256 surplus; // initialize the return amount uint256 returnAmount; // parse partner and fee data (address payable partner, uint256 feeData) = parsePartnerAndFeeData(partnerAndFee); // check if the quotedAmount is bigger than the maxAmountIn if (quotedAmount > maxAmountIn) { revert InvalidQuotedAmount(); } // calculate the surplus, we do not take the surplus into account if it is less than // MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI if (quotedAmount > spentAmount + MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI) { surplus = quotedAmount - spentAmount; // if the cap surplus flag is passed, we cap the surplus to 1% of the quoted amount if (feeData & IS_CAP_SURPLUS_MASK != 0) { uint256 cappedSurplus = (SURPLUS_PERCENT * quotedAmount) / 10_000; surplus = surplus > cappedSurplus ? cappedSurplus : surplus; } } // if partner address is not 0x0 if (partner != address(0x0)) { // Check if skip blacklist flag is true bool skipBlacklist = feeData & IS_SKIP_BLACKLIST_MASK != 0; // Check if token is blacklisted bool isBlacklisted = blacklistedTokens[srcToken]; // If the token is blacklisted and the skipBlacklist flag is false, // send the remaining amount to the msg.sender, we won't process fees if (!skipBlacklist && isBlacklisted) { // transfer the remaining amount to msg.sender returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, remainingAmount); // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, --receivedAmount); return (maxAmountIn - returnAmount, receivedAmount, 0, 0); } // Check if direct transfer flag is true bool isDirectTransfer = feeData & IS_DIRECT_TRANSFER_MASK != 0; // partner takes fixed fees feePercent is greater than 0 uint256 feePercent = _getAdjustedFeePercent(feeData); if (feePercent > 0) { // fee base = min (spentAmount, quotedAmount) uint256 feeBase = spentAmount < quotedAmount ? spentAmount : quotedAmount; // calculate fixed fees uint256 fee = (feeBase * feePercent) / 10_000; partnerFeeShare = (fee * PARTNER_SHARE_PERCENT) / 10_000; paraswapFeeShare = fee - partnerFeeShare; // distrubite fees from srcToken returnAmount = _distributeFees( remainingAmount, srcToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the rest to msg.sender returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, returnAmount); // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, --receivedAmount); return (maxAmountIn - returnAmount, receivedAmount, paraswapFeeShare, partnerFeeShare); } // if slippage is postive and referral flag is true if (feeData & IS_REFERRAL_MASK != 0) { if (surplus > 0) { // the split is 50% for paraswap, 25% for the referrer and 25% for the user paraswapFeeShare = (surplus * PARASWAP_REFERRAL_SHARE) / 10_000; partnerFeeShare = (surplus * PARTNER_REFERRAL_SHARE) / 10_000; // distribute fees from srcToken returnAmount = _distributeFees( remainingAmount, srcToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the rest to msg.sender returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, returnAmount); // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, --receivedAmount); return (maxAmountIn - returnAmount, receivedAmount, paraswapFeeShare, partnerFeeShare); } } // if slippage is positive and takeSurplus flag is true else if (feeData & IS_TAKE_SURPLUS_MASK != 0) { if (surplus > 0) { // paraswap takes 50% of the surplus and partner takes the other 50% paraswapFeeShare = (surplus * PARASWAP_SURPLUS_SHARE) / 10_000; partnerFeeShare = surplus - paraswapFeeShare; // If user surplus flag is true, transfer the partner share to the user instead of the partner if (feeData & IS_USER_SURPLUS_MASK != 0) { partnerFeeShare = 0; // Transfer the paraswap share directly to the fee wallet isDirectTransfer = true; } // distrubite fees from srcToken, partner takes 50% of the surplus // and paraswap takes the other 50% returnAmount = _distributeFees( remainingAmount, srcToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ); // transfer the rest to msg.sender returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, returnAmount); // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, --receivedAmount); return (maxAmountIn - returnAmount, receivedAmount, paraswapFeeShare, partnerFeeShare); } } } // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, --receivedAmount); // if slippage is positive and partner address is 0x0 or fee percent is 0 // paraswap will take the surplus, and transfer the rest to msg.sender // if there is no positive slippage, transfer the remaining amount to msg.sender if (surplus > 0) { // If the token is blacklisted, send the remaining amount to the msg.sender // we won't process fees if (blacklistedTokens[srcToken]) { // transfer the remaining amount to msg.sender returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, remainingAmount); return (maxAmountIn - returnAmount, receivedAmount, 0, 0); } // transfer the surplus to the fee wallet srcToken.safeTransfer(feeWallet, surplus); // transfer the remaining amount to msg.sender returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, remainingAmount - surplus); return (maxAmountIn - returnAmount, receivedAmount, surplus, 0); } else { // transfer the remaining amount to msg.sender returnAmount = _transferIfGreaterThanOne(srcToken, msg.sender, remainingAmount); return (maxAmountIn - returnAmount, receivedAmount, 0, 0); } } /// @notice Process swapExactAmountOut fees for UniV3 swapExactAmountOut, doing a transferFrom user to the fee /// vault or partner and feeWallet /// @param beneficiary The user's address /// @param srcToken The token used to swapExactAmountOut /// @param destToken The token received from the swapExactAmountOut /// @param partnerAndFee Packed partner and fee data /// @param maxAmountIn The amount of srcToken passed to the swapExactAmountOut /// @param receivedAmount The amount of destToken received from the swapExactAmountOut /// @param spentAmount The amount of srcToken used to swapExactAmountOut /// @param quotedAmount The quoted expected amount of srcToken to be used to swapExactAmountOut /// @return totalSpentAmount The total amount of srcToken used to swapExactAmountOut /// @return returnAmount The amount of destToken transfered to the beneficiary /// @return paraswapFeeShare The share of the fees for Paraswap /// @return partnerFeeShare The share of the fees for the partner function processSwapExactAmountOutFeesAndTransferUniV3( address beneficiary, IERC20 srcToken, IERC20 destToken, uint256 partnerAndFee, uint256 maxAmountIn, uint256 receivedAmount, uint256 spentAmount, uint256 quotedAmount ) internal returns (uint256 totalSpentAmount, uint256 returnAmount, uint256 paraswapFeeShare, uint256 partnerFeeShare) { // initialize the surplus uint256 surplus; // calculate remaining amount uint256 remainingAmount = maxAmountIn - spentAmount; // parse partner and fee data (address payable partner, uint256 feeData) = parsePartnerAndFeeData(partnerAndFee); // check if the quotedAmount is bigger than the fromAmount if (quotedAmount > maxAmountIn) { revert InvalidQuotedAmount(); } // calculate the surplus, we do not take the surplus into account if it is less than // MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI if (quotedAmount > spentAmount + MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI) { surplus = quotedAmount - spentAmount; // if the cap surplus flag is passed, we cap the surplus to 1% of the quoted amount if (feeData & IS_CAP_SURPLUS_MASK != 0) { uint256 cappedSurplus = (SURPLUS_PERCENT * quotedAmount) / 10_000; surplus = surplus > cappedSurplus ? cappedSurplus : surplus; } } // if partner address is not 0x0 if (partner != address(0x0)) { // Check if skip blacklist flag is true bool skipBlacklist = feeData & IS_SKIP_BLACKLIST_MASK != 0; // Check if token is blacklisted bool isBlacklisted = blacklistedTokens[srcToken]; // If the token is blacklisted and the skipBlacklist flag is false, // we won't process fees if (!skipBlacklist && isBlacklisted) { // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, receivedAmount); return (spentAmount, receivedAmount, 0, 0); } // Check if direct transfer flag is true bool isDirectTransfer = feeData & IS_DIRECT_TRANSFER_MASK != 0; // partner takes fixed fees feePercent is greater than 0 uint256 feePercent = _getAdjustedFeePercent(feeData); if (feePercent > 0) { // fee base = min (spentAmount, quotedAmount) uint256 feeBase = spentAmount < quotedAmount ? spentAmount : quotedAmount; // calculate fixed fees uint256 fee = (feeBase * feePercent) / 10_000; partnerFeeShare = (fee * PARTNER_SHARE_PERCENT) / 10_000; paraswapFeeShare = fee - partnerFeeShare; // distrubite fees from srcToken totalSpentAmount = _distributeFeesUniV3( remainingAmount, msg.sender, srcToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ) + spentAmount; // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, receivedAmount); return (totalSpentAmount, receivedAmount, paraswapFeeShare, partnerFeeShare); } // if slippage is postive and referral flag is true else if (feeData & IS_REFERRAL_MASK != 0) { if (surplus > 0) { // the split is 50% for paraswap, 25% for the referrer and 25% for the user paraswapFeeShare = (surplus * PARASWAP_REFERRAL_SHARE) / 10_000; partnerFeeShare = (surplus * PARTNER_REFERRAL_SHARE) / 10_000; // distribute fees from srcToken totalSpentAmount = _distributeFeesUniV3( remainingAmount, msg.sender, srcToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ) + spentAmount; // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, receivedAmount); return (totalSpentAmount, receivedAmount, paraswapFeeShare, partnerFeeShare); } } // if slippage is positive and takeSurplus flag is true else if (feeData & IS_TAKE_SURPLUS_MASK != 0) { if (surplus > 0) { // paraswap takes 50% of the surplus and partner takes the other 50% paraswapFeeShare = (surplus * PARASWAP_SURPLUS_SHARE) / 10_000; partnerFeeShare = surplus - paraswapFeeShare; // If user surplus flag is true, transfer the partner share to the user instead of the partner if (feeData & IS_USER_SURPLUS_MASK != 0) { partnerFeeShare = 0; // Transfer the paraswap share directly to the fee wallet isDirectTransfer = true; } // partner takes 50% of the surplus and paraswap takes the other 50% // distrubite fees from srcToken totalSpentAmount = _distributeFeesUniV3( remainingAmount, msg.sender, srcToken, partner, partnerFeeShare, paraswapFeeShare, skipBlacklist, isBlacklisted, isDirectTransfer ) + spentAmount; // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, receivedAmount); return (totalSpentAmount, receivedAmount, paraswapFeeShare, partnerFeeShare); } } } // transfer the received amount of destToken to the beneficiary destToken.safeTransfer(beneficiary, receivedAmount); // if slippage is positive and partner address is 0x0 or fee percent is 0 // paraswap will take the surplus if (surplus > 0) { // If the token is blacklisted, we won't process fees if (blacklistedTokens[srcToken]) { return (spentAmount, receivedAmount, 0, 0); } // transfer the surplus to the fee wallet srcToken.safeTransferFrom(msg.sender, feeWallet, surplus); } return (spentAmount + surplus, receivedAmount, surplus, 0); } /*////////////////////////////////////////////////////////////// PUBLIC //////////////////////////////////////////////////////////////*/ /// @inheritdoc IAugustusFees function parsePartnerAndFeeData(uint256 partnerAndFee) public pure returns (address payable partner, uint256 feeData) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { partner := shr(96, partnerAndFee) feeData := and(partnerAndFee, 0xFFFFFFFFFFFFFFFFFFFFFFFF) } } /*////////////////////////////////////////////////////////////// PRIVATE //////////////////////////////////////////////////////////////*/ /// @notice Distribute fees to the partner and paraswap /// @param currentBalance The current balance of the token before distributing the fees /// @param token The token to distribute the fees for /// @param partner The partner address /// @param partnerShare The partner share /// @param paraswapShare The paraswap share /// @param skipBlacklist Whether to skip the blacklist and transfer the fees directly to the partner /// @param isBlacklisted Whether the token is blacklisted /// @param directTransfer Whether to transfer the fees directly to the partner instead of the fee vault /// @return newBalance The new balance of the token after distributing the fees function _distributeFees( uint256 currentBalance, IERC20 token, address payable partner, uint256 partnerShare, uint256 paraswapShare, bool skipBlacklist, bool isBlacklisted, bool directTransfer ) private returns (uint256 newBalance) { uint256 totalFees = partnerShare + paraswapShare; if (totalFees == 0) { return currentBalance; } else { if (skipBlacklist && isBlacklisted) { // totalFees should be just the partner share, paraswap does not take fees // on blacklisted tokens, the rest of the fees are sent to sender based on // newBalance = currentBalance - totalFees totalFees = partnerShare; // revert if the balance is not enough to pay the fees if (totalFees > currentBalance) { revert InsufficientBalanceToPayFees(); } if (partnerShare > 0) { token.safeTransfer(partner, partnerShare); } } else { // revert if the balance is not enough to pay the fees if (totalFees > currentBalance) { revert InsufficientBalanceToPayFees(); } if (directTransfer) { // transfer the fees directly to the partner and paraswap if (paraswapShare > 0) { token.safeTransfer(feeWallet, paraswapShare); } if (partnerShare > 0) { token.safeTransfer(partner, partnerShare); } } else { // transfer the fees to the fee vault token.safeTransfer(address(FEE_VAULT), totalFees); // Setup fee registration data address[] memory feeAddresses = new address[](2); uint256[] memory feeAmounts = new uint256[](2); feeAddresses[0] = partner; feeAmounts[0] = partnerShare; feeAddresses[1] = feeWalletDelegate; feeAmounts[1] = paraswapShare; IAugustusFeeVault.FeeRegistration memory feeData = IAugustusFeeVault.FeeRegistration({ token: token, addresses: feeAddresses, fees: feeAmounts }); // Register the fees FEE_VAULT.registerFees(feeData); } } } newBalance = currentBalance - totalFees; } /// @notice Distribute fees for UniV3 /// @param currentBalance The current balance of the token before distributing the fees /// @param payer The user's address /// @param token The token to distribute the fees for /// @param partner The partner address /// @param partnerShare The partner share /// @param paraswapShare The paraswap share /// @param skipBlacklist Whether to skip the blacklist and transfer the fees directly to the partner /// @param isBlacklisted Whether the token is blacklisted /// @param directTransfer Whether to transfer the fees directly to the partner instead of the fee vault /// @return totalFees The total fees distributed function _distributeFeesUniV3( uint256 currentBalance, address payer, IERC20 token, address payable partner, uint256 partnerShare, uint256 paraswapShare, bool skipBlacklist, bool isBlacklisted, bool directTransfer ) private returns (uint256 totalFees) { totalFees = partnerShare + paraswapShare; if (totalFees != 0) { if (skipBlacklist && isBlacklisted) { // totalFees should be just the partner share, paraswap does not take fees // on blacklisted tokens, the rest of the fees will remain on the payer's address totalFees = partnerShare; // revert if the balance is not enough to pay the fees if (totalFees > currentBalance) { revert InsufficientBalanceToPayFees(); } // transfer the fees to the partner if (partnerShare > 0) { // transfer the fees to the partner token.safeTransferFrom(payer, partner, partnerShare); } } else { // revert if the balance is not enough to pay the fees if (totalFees > currentBalance) { revert InsufficientBalanceToPayFees(); } if (directTransfer) { // transfer the fees directly to the partner and paraswap if (paraswapShare > 0) { token.safeTransferFrom(payer, feeWallet, paraswapShare); } if (partnerShare > 0) { token.safeTransferFrom(payer, partner, partnerShare); } } else { // transfer the fees to the fee vault token.safeTransferFrom(payer, address(FEE_VAULT), totalFees); // Setup fee registration data address[] memory feeAddresses = new address[](2); uint256[] memory feeAmounts = new uint256[](2); feeAddresses[0] = partner; feeAmounts[0] = partnerShare; feeAddresses[1] = feeWalletDelegate; feeAmounts[1] = paraswapShare; IAugustusFeeVault.FeeRegistration memory feeData = IAugustusFeeVault.FeeRegistration({ token: token, addresses: feeAddresses, fees: feeAmounts }); // Register the fees FEE_VAULT.registerFees(feeData); } } // othwerwise do not transfer the fees } return totalFees; } /// @notice Get the adjusted fee percent by masking feePercent with FEE_PERCENT_IN_BASIS_POINTS_MASK, /// if the fee percent is bigger than MAX_FEE_PERCENT, then set it to MAX_FEE_PERCENT /// @param feePercent The fee percent /// @return adjustedFeePercent The adjusted fee percent function _getAdjustedFeePercent(uint256 feePercent) private pure returns (uint256) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { feePercent := and(feePercent, FEE_PERCENT_IN_BASIS_POINTS_MASK) // if feePercent is bigger than MAX_FEE_PERCENT, then set it to MAX_FEE_PERCENT if gt(feePercent, MAX_FEE_PERCENT) { feePercent := MAX_FEE_PERCENT } } return feePercent; } /// @notice Transfers amount to recipient if the amount is bigger than 1, leaving 1 wei dust on the contract /// @param token The token to transfer /// @param recipient The address to transfer to /// @param amount The amount to transfer function _transferIfGreaterThanOne( IERC20 token, address recipient, uint256 amount ) private returns (uint256 amountOut) { if (amount > 1) { unchecked { --amount; } token.safeTransfer(recipient, amount); return amount; } return 0; } /// @notice Transfer amount to beneficiary, leaving 1 wei dust on the contract /// @param token The token to transfer /// @param beneficiary The address to transfer to /// @param amount The amount to transfer function _transferAndLeaveDust(IERC20 token, address beneficiary, uint256 amount) private { unchecked { --amount; } token.safeTransfer(beneficiary, amount); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Contracts import { GenericUtils } from "../../util/GenericUtils.sol"; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IGenericSwapExactAmountIn } from "../../interfaces/IGenericSwapExactAmountIn.sol"; // Libraries import { ERC20Utils } from "../../libraries/ERC20Utils.sol"; // Types import { GenericData } from "../../AugustusV6Types.sol"; /// @title GenericSwapExactAmountIn /// @notice Router for executing generic swaps with exact amount in through an executor abstract contract GenericSwapExactAmountIn is IGenericSwapExactAmountIn, GenericUtils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @inheritdoc IGenericSwapExactAmountIn function swapExactAmountIn( address executor, GenericData calldata swapData, uint256 partnerAndFee, bytes calldata permit, bytes calldata executorData ) external payable whenNotPaused returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference swapData IERC20 destToken = swapData.destToken; IERC20 srcToken = swapData.srcToken; uint256 amountIn = swapData.fromAmount; uint256 minAmountOut = swapData.toAmount; uint256 quotedAmountOut = swapData.quotedAmount; address payable beneficiary = swapData.beneficiary; // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Check if toAmount is valid if (minAmountOut == 0) { revert InvalidToAmount(); } // Check if srcToken is ETH if (srcToken.isETH(amountIn) == 0) { // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } srcToken.safeTransferFrom(msg.sender, executor, amountIn); } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, executor, amountIn); } } // Execute swap _callSwapExactAmountInExecutor(executor, executorData, amountIn); // Check balance after swap receivedAmount = destToken.getBalance(address(this)); // Check if swap succeeded if (receivedAmount < minAmountOut) { revert InsufficientReturnAmount(); } // Process fees and transfer destToken to beneficiary return processSwapExactAmountInFeesAndTransfer( beneficiary, destToken, partnerAndFee, receivedAmount, quotedAmountOut ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IGenericSwapExactAmountOut } from "../../interfaces/IGenericSwapExactAmountOut.sol"; // Libraries import { ERC20Utils } from "../../libraries/ERC20Utils.sol"; // Types import { GenericData } from "../../AugustusV6Types.sol"; // Utils import { GenericUtils } from "../../util/GenericUtils.sol"; /// @title GenericSwapExactAmountOut /// @notice Router for executing generic swaps with exact amount out through an executor abstract contract GenericSwapExactAmountOut is IGenericSwapExactAmountOut, GenericUtils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT //////////////////////////////////////////////////////////////*/ /// @inheritdoc IGenericSwapExactAmountOut function swapExactAmountOut( address executor, GenericData calldata swapData, uint256 partnerAndFee, bytes calldata permit, bytes calldata executorData ) external payable whenNotPaused returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare) { // Dereference swapData IERC20 destToken = swapData.destToken; IERC20 srcToken = swapData.srcToken; uint256 maxAmountIn = swapData.fromAmount; uint256 amountOut = swapData.toAmount; uint256 quotedAmountIn = swapData.quotedAmount; address payable beneficiary = swapData.beneficiary; // Make sure srcToken and destToken are different if (srcToken == destToken) { revert ArbitrageNotSupported(); } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Check if toAmount is valid if (amountOut == 0) { revert InvalidToAmount(); } // Check contract balance uint256 balanceBefore = srcToken.getBalance(address(this)); // Check if srcToken is ETH // Transfer srcToken to executor if not ETH if (srcToken.isETH(maxAmountIn) == 0) { // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } srcToken.safeTransferFrom(msg.sender, executor, maxAmountIn); } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, executor, maxAmountIn); } } else { // If srcToken is ETH, we have to deduct msg.value from balanceBefore balanceBefore = balanceBefore - msg.value; } // Execute swap _callSwapExactAmountOutExecutor(executor, executorData, maxAmountIn, amountOut); // Check balance of destToken receivedAmount = destToken.getBalance(address(this)); // Check balance of srcToken, deducting the balance before the swap if it is greater than 1 uint256 remainingAmount = srcToken.getBalance(address(this)) - (balanceBefore > 1 ? balanceBefore : 0); // Check if swap succeeded if (receivedAmount < amountOut) { revert InsufficientReturnAmount(); } // Process fees and transfer destToken and srcToken to beneficiary return processSwapExactAmountOutFeesAndTransfer( beneficiary, srcToken, destToken, partnerAndFee, maxAmountIn, remainingAmount, receivedAmount, quotedAmountIn ); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IAugustusRFQRouter } from "../../interfaces/IAugustusRFQRouter.sol"; // Libraries import { ERC20Utils } from "../../libraries/ERC20Utils.sol"; // Types import { AugustusRFQData, OrderInfo } from "../../AugustusV6Types.sol"; // Utils import { AugustusRFQUtils } from "../../util/AugustusRFQUtils.sol"; import { WETHUtils } from "../../util/WETHUtils.sol"; import { PauseUtils } from "../../util/PauseUtils.sol"; import { Permit2Utils } from "../../util/Permit2Utils.sol"; import { AugustusFees } from "../../fees/AugustusFees.sol"; /// @title AugustusRFQRouter /// @notice A contract for executing direct AugustusRFQ swaps abstract contract AugustusRFQRouter is IAugustusRFQRouter, AugustusRFQUtils, AugustusFees, WETHUtils, Permit2Utils, PauseUtils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// TRY BATCH FILL //////////////////////////////////////////////////////////////*/ /// @inheritdoc IAugustusRFQRouter // solhint-disable-next-line code-complexity function swapOnAugustusRFQTryBatchFill( AugustusRFQData calldata data, OrderInfo[] calldata orders, bytes calldata permit ) external payable whenNotPaused returns (uint256 spentAmount, uint256 receivedAmount) { // Dereference data address payable beneficiary = data.beneficiary; uint256 ordersLength = orders.length; uint256 fromAmount = data.fromAmount; uint256 toAmount = data.toAmount; uint8 wrapApproveDirection = data.wrapApproveDirection; // Decode wrapApproveDirection // First 2 bits are for wrap // Next 1 bit is for approve // Last 1 bit is for direction uint8 wrap; bool approve; bool direction; // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { wrap := and(3, wrapApproveDirection) approve := and(shr(2, wrapApproveDirection), 1) direction := and(shr(3, wrapApproveDirection), 1) } // Check if beneficiary is valid if (beneficiary == address(0)) { beneficiary = payable(msg.sender); } // Check if toAmount is valid if (toAmount == 0) { revert InvalidToAmount(); } // Check if ordersLength is valid if (ordersLength == 0) { revert InvalidOrdersLength(); } // Check if msg.sender is authorized to be the taker for all orders for (uint256 i = 0; i < ordersLength; ++i) { _checkAuthorization(orders[i].order.nonceAndMeta); } // Dereference srcToken and destToken IERC20 srcToken = IERC20(orders[0].order.takerAsset); IERC20 destToken = IERC20(orders[0].order.makerAsset); // Check if we need to wrap or permit if (wrap != 1) { // If msg.value is not 0, revert if (msg.value > 0) { revert IncorrectEthAmount(); } // Check the length of the permit field, // if < 257 and > 0 we should execute regular permit // and if it is >= 257 we execute permit2 if (permit.length < 257) { // Permit if needed if (permit.length > 0) { srcToken.permit(permit); } srcToken.safeTransferFrom(msg.sender, address(this), fromAmount); } else { // Otherwise Permit2.permitTransferFrom permit2TransferFrom(permit, address(this), fromAmount); } } else { // Check if msg.value is equal to fromAmount if (fromAmount != msg.value) { revert IncorrectEthAmount(); } // If it is ETH. wrap it to WETH WETH.deposit{ value: fromAmount }(); } if (approve) { // Approve srcToken to AugustusRFQ srcToken.approve(address(AUGUSTUS_RFQ)); } // Check if we need to execute a swapExactAmountIn or a swapExactAmountOut if (!direction) { // swapExactAmountIn // Unwrap WETH if needed if (wrap == 2) { // Execute tryBatchFillOrderTakerAmount AUGUSTUS_RFQ.tryBatchFillOrderTakerAmount(orders, fromAmount, address(this)); // Check received amount receivedAmount = IERC20(WETH).getBalance(address(this)); // Check if swap succeeded if (receivedAmount < toAmount) { revert InsufficientReturnAmount(); } // Unwrap WETH WETH.withdraw(--receivedAmount); // Transfer ETH to beneficiary ERC20Utils.ETH.safeTransfer(beneficiary, receivedAmount); } else { // Check balance of beneficiary before swap uint256 beforeBalance = destToken.getBalance(beneficiary); // Execute tryBatchFillOrderTakerAmount AUGUSTUS_RFQ.tryBatchFillOrderTakerAmount(orders, fromAmount, beneficiary); // set receivedAmount to afterBalance - beforeBalance receivedAmount = destToken.getBalance(beneficiary) - beforeBalance; // Check if swap succeeded if (receivedAmount < toAmount) { revert InsufficientReturnAmount(); } } // Return spentAmount and receivedAmount return (fromAmount, receivedAmount); } else { // swapExactAmountOut // Unwrap WETH if needed if (wrap == 2) { // Execute tryBatchFillOrderMakerAmount AUGUSTUS_RFQ.tryBatchFillOrderMakerAmount(orders, toAmount, address(this)); // Check remaining WETH balance receivedAmount = IERC20(WETH).getBalance(address(this)); // Unwrap WETH WETH.withdraw(--receivedAmount); // Transfer ETH to beneficiary ERC20Utils.ETH.safeTransfer(beneficiary, receivedAmount); // Set toAmount to receivedAmount toAmount = receivedAmount; } else { // Execute tryBatchFillOrderMakerAmount AUGUSTUS_RFQ.tryBatchFillOrderMakerAmount(orders, toAmount, beneficiary); } // Check remaining amount uint256 remainingAmount = srcToken.getBalance(address(this)); // Send remaining srcToken to msg.sender if (remainingAmount > 1) { // If srcToken was ETH if (wrap == 1) { // Unwrap WETH WETH.withdraw(--remainingAmount); // Transfer ETH to msg.sender ERC20Utils.ETH.safeTransfer(msg.sender, remainingAmount); } else { // Transfer remaining srcToken to msg.sender srcToken.safeTransfer(msg.sender, --remainingAmount); } } // Return spentAmount and receivedAmount return (fromAmount - remainingAmount, toAmount); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IAugustusRFQ } from "../interfaces/IAugustusRFQ.sol"; import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; // Libraries import { ERC20Utils } from "../libraries/ERC20Utils.sol"; /// @title AugustusRFQUtils /// @notice A contract containing common utilities for AugustusRFQ swaps contract AugustusRFQUtils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using ERC20Utils for IERC20; /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ /// @dev Emitted when the msg.sender is not authorized to be the taker error UnauthorizedUser(); /// @dev Emitted when the orders length is 0 error InvalidOrdersLength(); /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @dev AugustusRFQ address IAugustusRFQ public immutable AUGUSTUS_RFQ; // solhint-disable-line var-name-mixedcase /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _augustusRFQ) { AUGUSTUS_RFQ = IAugustusRFQ(_augustusRFQ); } /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ /// @dev Check if the msg.sender is authorized to be the taker function _checkAuthorization(uint256 nonceAndMeta) internal view { // solhint-disable-next-line no-inline-assembly assembly { // Parse nonceAndMeta if xor(and(nonceAndMeta, 0xffffffffffffffffffffffffffffffffffffffff), 0) { // If the taker is not 0, we check if the msg.sender is authorized if xor(and(nonceAndMeta, 0xffffffffffffffffffffffffffffffffffffffff), caller()) { // The taker does not match the originalSender, revert mstore(0, 0x02a43f8b00000000000000000000000000000000000000000000000000000000) // function // selector for error UnauthorizedUser(); revert(0, 4) } } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Contracts import { AugustusFees } from "../fees/AugustusFees.sol"; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; // Utils import { Permit2Utils } from "./Permit2Utils.sol"; import { PauseUtils } from "./PauseUtils.sol"; /// @title BalancerV2Utils /// @notice A contract containing common utilities for BalancerV2 swaps abstract contract BalancerV2Utils is AugustusFees, Permit2Utils, PauseUtils { /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ /// @dev Emitted when the passed selector is invalid error InvalidSelector(); /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @dev BalancerV2 vault address address payable public immutable BALANCER_VAULT; // solhint-disable-line var-name-mixedcase /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address payable _balancerVault) { BALANCER_VAULT = _balancerVault; } /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ /// @dev Decode srcToken, destToken from balancerData, beneficiary and approve flag from beneficiaryAndApproveFlag function _decodeBalancerV2Params( uint256 beneficiaryAndApproveFlag, bytes calldata balancerData ) internal pure returns (IERC20 srcToken, IERC20 destToken, address payable beneficiary, bool approve) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { // Parse beneficiaryAndApproveFlag beneficiary := and(beneficiaryAndApproveFlag, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) approve := shr(255, beneficiaryAndApproveFlag) // Load calldata without selector let callDataWithoutSelector := add(4, balancerData.offset) // Check selector switch calldataload(balancerData.offset) // If the selector is for swap(tuple singleSwap,tuple funds,uint256 limit,uint256 deadline) case 0x52bbbe2900000000000000000000000000000000000000000000000000000000 { // Load srcToken from singleSswap.assetIn srcToken := calldataload(add(callDataWithoutSelector, 288)) // Load destToken from singleSswap.assetOut destToken := calldataload(add(callDataWithoutSelector, 320)) } // If the selector is for batchSwap(uint8 kind,tuple[] swaps,address[] assets,tuple funds,int256[] // limits,uint256 deadline) case 0x945bcec900000000000000000000000000000000000000000000000000000000 { // Load assetOffset from balancerData let assetsOffset := calldataload(add(callDataWithoutSelector, 64)) // Load assetCount at assetOffset let assetsCount := calldataload(add(callDataWithoutSelector, assetsOffset)) // Get swapExactAmountIn type from first 32 bytes of balancerData let swapType := calldataload(callDataWithoutSelector) // Set fromAmount, srcToken, toAmount and destToken based on swapType switch eq(swapType, 1) case 1 { // Load srcToken as the last asset in balancerData.assets srcToken := calldataload(add(callDataWithoutSelector, add(assetsOffset, mul(assetsCount, 32)))) // Load destToken as the first asset in balancerData.assets destToken := calldataload(add(callDataWithoutSelector, add(assetsOffset, 32))) } default { // Load srcToken as the first asset in balancerData.assets srcToken := calldataload(add(callDataWithoutSelector, add(assetsOffset, 32))) // Load destToken as the last asset in balancerData.assets destToken := calldataload(add(callDataWithoutSelector, add(assetsOffset, mul(assetsCount, 32)))) } } default { // If the selector is invalid, revert mstore(0, 0x7352d91c00000000000000000000000000000000000000000000000000000000) // store the // selector for error InvalidSelector(); revert(0, 4) } // Balancer users 0x0 as ETH address so we need to convert it if eq(srcToken, 0) { srcToken := 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE } if eq(destToken, 0) { destToken := 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE } } return (srcToken, destToken, beneficiary, approve); } /// @dev Call balancerVault with data function _callBalancerV2(bytes calldata balancerData) internal { address payable targetAddress = BALANCER_VAULT; // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { // Load free memory pointer let ptr := mload(64) // Copy the balancerData to memory calldatacopy(ptr, balancerData.offset, balancerData.length) // Execute the call on balancerVault if iszero(call(gas(), targetAddress, callvalue(), ptr, balancerData.length, 0, 0)) { returndatacopy(ptr, 0, returndatasize()) // copy the revert data to memory revert(ptr, returndatasize()) // revert with the revert data } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Contracts import { AugustusFees } from "../fees/AugustusFees.sol"; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; // Utils import { WETHUtils } from "./WETHUtils.sol"; import { Permit2Utils } from "./Permit2Utils.sol"; import { PauseUtils } from "./PauseUtils.sol"; /// @title UniswapV2Utils /// @notice A contract containing common utilities for UniswapV2 swaps abstract contract UniswapV2Utils is AugustusFees, WETHUtils, Permit2Utils, PauseUtils { /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @dev Used to caluclate pool address uint256 public immutable UNISWAP_V2_POOL_INIT_CODE_HASH; /// @dev Right padded FF + UniswapV2Factory address uint256 public immutable UNISWAP_V2_FACTORY_AND_FF; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(uint256 _uniswapV2FactoryAndFF, uint256 _uniswapV2PoolInitCodeHash) { UNISWAP_V2_FACTORY_AND_FF = _uniswapV2FactoryAndFF; UNISWAP_V2_POOL_INIT_CODE_HASH = _uniswapV2PoolInitCodeHash; } /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ /// @dev Loops through UniswapV2 pools in backword direction and swaps exact amount out function _callUniswapV2PoolsSwapExactOut(uint256 amountOut, IERC20 srcToken, bytes calldata pools) internal { uint256 uniswapV2FactoryAndFF = UNISWAP_V2_FACTORY_AND_FF; uint256 uniswapV2PoolInitCodeHash = UNISWAP_V2_POOL_INIT_CODE_HASH; // solhint-disable-next-line no-inline-assembly assembly { function calculatePoolAddress( poolMemoryPtr, poolCalldataPtr, _uniswapV2FactoryAndFF, _uniswapV2PoolInitCodeHash ) { // Calculate the pool address // We can do this by first calling the keccak256 function on the passed pool values and then // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encodePacked(token0, token1)), POOL_INIT_CODE_HASH)); // The first 20 bytes of the computed address are the pool address // Store 0xff + factory address (right padded) mstore(poolMemoryPtr, _uniswapV2FactoryAndFF) // Store pools offset + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE) let token0ptr := add(poolMemoryPtr, 21) // Copy pool data (skip last bit) to free memory pointer + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE) calldatacopy(token0ptr, poolCalldataPtr, 40) // Calculate keccak256(abi.encode(address(token0), address(token1)) mstore(token0ptr, keccak256(token0ptr, 40)) // Store POOL_INIT_CODE_HASH mstore(add(token0ptr, 32), _uniswapV2PoolInitCodeHash) // Calculate address(keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, token1), POOL_INIT_CODE_HASH))); mstore(poolMemoryPtr, and(keccak256(poolMemoryPtr, 85), 0xffffffffffffffffffffffffffffffffffffffff)) // 21 // + 32 + 32 } // Calculate pool count let poolCount := div(pools.length, 64) // Initilize memory pointers let amounts := mload(64) // pointer for amounts array let poolAddresses := add(amounts, add(mul(poolCount, 32), 32)) // pointer for pools array let emptyPtr := add(poolAddresses, mul(poolCount, 32)) // pointer for empty memory // Initialize fromAmount let fromAmount := 0 // Set the final amount in the amounts array to amountOut mstore(add(amounts, mul(poolCount, 0x20)), amountOut) //---------------------------------// // Calculate Pool Addresses and Amounts //---------------------------------// // Calculate pool addresses for { let i := 0 } lt(i, poolCount) { i := add(i, 1) } { calculatePoolAddress( add(poolAddresses, mul(i, 32)), add(pools.offset, mul(i, 64)), uniswapV2FactoryAndFF, uniswapV2PoolInitCodeHash ) } // Rerverse loop through pools and calculate amounts for { let i := poolCount } gt(i, 0) { i := sub(i, 1) } { // Use previous pool data to calculate amount in let indexSub1 := sub(i, 1) // Get pool address let poolAddress := mload(add(poolAddresses, mul(indexSub1, 32))) // Get direction let direction := and(1, calldataload(add(add(pools.offset, mul(indexSub1, 64)), 32))) // Get amount let amount := mload(add(amounts, mul(i, 32))) //---------------------------------// // Calculate Amount In //---------------------------------// //---------------------------------// // Get Reserves //---------------------------------// // Store the selector mstore(emptyPtr, 0x0902f1ac00000000000000000000000000000000000000000000000000000000) // 'getReserves()' // selector // Perform the external 'getReserves' call - outputs directly to ptr if iszero(staticcall(gas(), poolAddress, emptyPtr, 4, emptyPtr, 64)) { returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } // If direction is true, getReserves returns (reserve0, reserve1) // If direction is false, getReserves returns (reserve1, reserve0) -> swap the values // Load the reserve0 value returned by the 'getReserves' call. let reserve1 := mload(emptyPtr) // Load the reserve1 value returned by the 'getReserves' call. let reserve0 := mload(add(emptyPtr, 32)) // Check if direction is true if direction { // swap reserve0 and reserve1 let temp := reserve0 reserve0 := reserve1 reserve1 := temp } //---------------------------------// // Calculate numerator = reserve0 * amountOut * 10000 let numerator := mul(mul(reserve0, amount), 10000) // Calculate denominator = (reserve1 - amountOut) * 9970 let denominator := mul(sub(reserve1, amount), 9970) // Calculate amountIn = numerator / denominator + 1 fromAmount := add(div(numerator, denominator), 1) // Store amountIn for the previous pool mstore(add(amounts, mul(indexSub1, 32)), fromAmount) } //---------------------------------// // Initialize variables let poolAddress := 0 let nextPoolAddress := 0 //---------------------------------// // Loop Swap Through Pools //---------------------------------// // Loop for each pool for { let i := 0 } lt(i, poolCount) { i := add(i, 1) } { // Check if it is the first pool if iszero(poolAddress) { // If it is the first pool, we need to transfer amount of srcToken to poolAddress // Load first pool address poolAddress := mload(poolAddresses) //---------------------------------// // Transfer amount of srcToken to poolAddress //---------------------------------// // Transfer fromAmount of srcToken to poolAddress mstore(emptyPtr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // store the // selector // (function transfer(address recipient, uint256 amount)) mstore(add(emptyPtr, 4), poolAddress) // store the recipient mstore(add(emptyPtr, 36), fromAmount) // store the amount pop(call(gas(), srcToken, 0, emptyPtr, 68, 0, 32)) // call transfer //---------------------------------// } // Adjust toAddress depending on if it is the last pool in the array let toAddress := address() // Check if it is not the last pool if lt(add(i, 1), poolCount) { // Load next pool address nextPoolAddress := mload(add(poolAddresses, mul(add(i, 1), 32))) // Adjust toAddress to next pool address toAddress := nextPoolAddress } // Check direction let direction := and(1, calldataload(add(add(pools.offset, mul(i, 64)), 32))) // if direction is 1, amount0out is 0 and amount1out is amount[i+1] // if direction is 0, amount0out is amount[i+1] and amount1out is 0 // Load amount[i+1] let amount := mload(add(amounts, mul(add(i, 1), 32))) // Initialize amount0Out and amount1Out let amount0Out := amount let amount1Out := 0 // Check if direction is true if direction { // swap amount0Out and amount1Out let temp := amount0Out amount0Out := amount1Out amount1Out := temp } //---------------------------------// // Perform Swap //---------------------------------// // Load the 'swap' selector, amount0Out, amount1Out, toAddress and data("") into memory. mstore(emptyPtr, 0x022c0d9f00000000000000000000000000000000000000000000000000000000) // 'swap()' selector mstore(add(emptyPtr, 4), amount0Out) // amount0Out mstore(add(emptyPtr, 36), amount1Out) // amount1Out mstore(add(emptyPtr, 68), toAddress) // toAddress mstore(add(emptyPtr, 100), 0x80) // data length mstore(add(emptyPtr, 132), 0) // data // Perform the external 'swap' call if iszero(call(gas(), poolAddress, 0, emptyPtr, 164, 0, 64)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } //---------------------------------// // Set poolAddress to nextPoolAddress poolAddress := nextPoolAddress } //---------------------------------// } } /// @dev Loops through UniswapV2 pools and swaps exact amount in function _callUniswapV2PoolsSwapExactIn( uint256 fromAmount, IERC20 srcToken, bytes calldata pools, address payer, bytes calldata permit2 ) internal { uint256 uniswapV2FactoryAndFF = UNISWAP_V2_FACTORY_AND_FF; uint256 uniswapV2PoolInitCodeHash = UNISWAP_V2_POOL_INIT_CODE_HASH; address permit2Address = PERMIT2; // solhint-disable-next-line no-inline-assembly assembly { //---------------------------------// // Loop Swap Through Pools //---------------------------------// // Calculate pool count let poolCount := div(pools.length, 64) // Initialize variables let p := 0 let poolAddress := 0 let nextPoolAddress := 0 let direction := 0 // Loop for each pool for { let i := 0 } lt(i, poolCount) { i := add(i, 1) } { // Check if it is the first pool if iszero(p) { //---------------------------------// // Calculate Pool Address //---------------------------------// // Calculate the pool address // We can do this by first calling the keccak256 function on the passed pool values and then // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encodePacked(token0,token1)), POOL_INIT_CODE_HASH)); // The first 20 bytes of the computed address are the pool address // Get free memory pointer let ptr := mload(64) // Store 0xff + factory address (right padded) mstore(ptr, uniswapV2FactoryAndFF) // Store pools offset + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE) let token0ptr := add(ptr, 21) // Copy pool data (skip last bit) to free memory pointer + 21 bytes (UNISWAP_V2_FACTORY_AND_FF // SIZE) calldatacopy(token0ptr, pools.offset, 40) // Calculate keccak256(abi.encodePacked(address(token0), address(token1)) mstore(token0ptr, keccak256(token0ptr, 40)) // Store POOL_INIT_CODE_HASH mstore(add(token0ptr, 32), uniswapV2PoolInitCodeHash) // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, // token1, fee)), POOL_INIT_CODE_HASH)); mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32 // Load pool p := mload(ptr) // Get the first 20 bytes of the computed address poolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff) //---------------------------------// //---------------------------------// // Transfer fromAmount of srcToken to poolAddress //---------------------------------// switch eq(payer, address()) // if payer is this contract, transfer fromAmount of srcToken to poolAddress case 1 { // Transfer fromAmount of srcToken to poolAddress mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // store the // selector // (function transfer(address recipient, uint256 amount)) mstore(add(ptr, 4), poolAddress) // store the recipient mstore(add(ptr, 36), fromAmount) // store the amount pop(call(gas(), srcToken, 0, ptr, 68, 0, 32)) // call transfer } // othwerwise transferFrom fromAmount of srcToken to poolAddress from payer default { switch gt(permit2.length, 256) case 0 { // Transfer fromAmount of srcToken to poolAddress mstore(ptr, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // store // the selector // (function transferFrom(address sender, address recipient, // uint256 amount)) mstore(add(ptr, 4), payer) // store the sender mstore(add(ptr, 36), poolAddress) // store the recipient mstore(add(ptr, 68), fromAmount) // store the amount pop(call(gas(), srcToken, 0, ptr, 100, 0, 32)) // call transferFrom } default { // Otherwise Permit2.permitTransferFrom // Store function selector mstore(ptr, 0x30f28b7a00000000000000000000000000000000000000000000000000000000) // permitTransferFrom() calldatacopy(add(ptr, 4), permit2.offset, permit2.length) // Copy data to memory mstore(add(ptr, 132), poolAddress) // Store recipient mstore(add(ptr, 164), fromAmount) // Store amount mstore(add(ptr, 196), payer) // Store payer // Call permit2.permitTransferFrom and revert if call failed if iszero(call(gas(), permit2Address, 0, ptr, add(permit2.length, 4), 0, 0)) { mstore(0, 0x6b836e6b00000000000000000000000000000000000000000000000000000000) // Store // error selector // error Permit2Failed() revert(0, 4) } } } //---------------------------------// } // Direction is the first bit of the pool data direction := and(1, calldataload(add(add(pools.offset, mul(i, 64)), 32))) //---------------------------------// // Calculate Amount Out //---------------------------------// //---------------------------------// // Get Reserves //---------------------------------// // Get free memory pointer let ptr := mload(64) // Store the selector mstore(ptr, 0x0902f1ac00000000000000000000000000000000000000000000000000000000) // 'getReserves()' // selector // Perform the external 'getReserves' call - outputs directly to ptr if iszero(staticcall(gas(), poolAddress, ptr, 4, ptr, 64)) { returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } // If direction is true, getReserves returns (reserve0, reserve1) // If direction is false, getReserves returns (reserve1, reserve0) -> swap the values // Load the reserve0 value returned by the 'getReserves' call. let reserve1 := mload(ptr) // Load the reserve1 value returned by the 'getReserves' call. let reserve0 := mload(add(ptr, 32)) // Check if direction is true if direction { // swap reserve0 and reserve1 let temp := reserve0 reserve0 := reserve1 reserve1 := temp } //---------------------------------// // Calculate amount based on fee let amountWithFee := mul(fromAmount, 9970) // Calculate numerator = amountWithFee * reserve1 let numerator := mul(amountWithFee, reserve1) // Calculate denominator = reserve0 * 10000 + amountWithFee let denominator := add(mul(reserve0, 10000), amountWithFee) // Calculate amountOut = numerator / denominator let amountOut := div(numerator, denominator) fromAmount := amountOut // if direction is true, amount0Out is 0 and amount1Out is fromAmount, // otherwise amount0Out is fromAmount and amount1Out is 0 let amount0Out := fromAmount let amount1Out := 0 // swap amount0Out and amount1Out if direction is false if direction { amount0Out := 0 amount1Out := fromAmount } //---------------------------------// // Adjust toAddress depending on if it is the last pool in the array let toAddress := address() // Check if it is not the last pool if lt(add(i, 1), poolCount) { //---------------------------------// // Calculate Next Pool Address //---------------------------------// // Store 0xff + factory address (right padded) mstore(ptr, uniswapV2FactoryAndFF) // Store pools offset + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE) let token0ptr := add(ptr, 21) // Copy next pool data to free memory pointer + 21 bytes (UNISWAP_V2_FACTORY_AND_FF SIZE) calldatacopy(token0ptr, add(pools.offset, mul(add(i, 1), 64)), 40) // Calculate keccak256(abi.encodePacked(address(token0), address(token1)) mstore(token0ptr, keccak256(token0ptr, 40)) // Store POOL_INIT_CODE_HASH mstore(add(token0ptr, 32), uniswapV2PoolInitCodeHash) // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, // token1), POOL_INIT_CODE_HASH)); mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32 // Load pool p := mload(ptr) // Get the first 20 bytes of the computed address nextPoolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff) // Adjust toAddress to next pool address toAddress := nextPoolAddress //---------------------------------// } //---------------------------------// // Perform Swap //---------------------------------// // Load the 'swap' selector, amount0Out, amount1Out, toAddress and data("") into memory. mstore(ptr, 0x022c0d9f00000000000000000000000000000000000000000000000000000000) // 'swap()' selector mstore(add(ptr, 4), amount0Out) // amount0Out mstore(add(ptr, 36), amount1Out) // amount1Out mstore(add(ptr, 68), toAddress) // toAddress mstore(add(ptr, 100), 0x80) // data length mstore(add(ptr, 132), 0) // data // Perform the external 'swap' call if iszero(call(gas(), poolAddress, 0, ptr, 164, 0, 64)) { // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } //---------------------------------// // Set poolAddress to nextPoolAddress poolAddress := nextPoolAddress } //---------------------------------// } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Contracts import { AugustusFees } from "../fees/AugustusFees.sol"; // Interfaces import { IUniswapV3SwapCallback } from "../interfaces/IUniswapV3SwapCallback.sol"; // Libraries import { SafeCastLib } from "@solady/utils/SafeCastLib.sol"; // Utils import { WETHUtils } from "./WETHUtils.sol"; import { Permit2Utils } from "./Permit2Utils.sol"; import { PauseUtils } from "./PauseUtils.sol"; /// @title UniswapV3Utils /// @notice A contract containing common utilities for UniswapV3 swaps abstract contract UniswapV3Utils is IUniswapV3SwapCallback, AugustusFees, WETHUtils, Permit2Utils, PauseUtils { /*////////////////////////////////////////////////////////////// LIBRARIES //////////////////////////////////////////////////////////////*/ using SafeCastLib for int256; /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ /// @notice Error emitted if the caller is not a Uniswap V3 pool error InvalidCaller(); /// @notice Error emitted if the transfer of tokens to the pool inside the callback failed error CallbackTransferFailed(); /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @dev Used to caluclate pool address uint256 public immutable UNISWAP_V3_POOL_INIT_CODE_HASH; /// @dev Right padded FF + UniswapV3Factory address uint256 public immutable UNISWAP_V3_FACTORY_AND_FF; /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ uint256 private constant UNISWAP_V3_MIN_SQRT = 4_295_128_740; uint256 private constant UNISWAP_V3_MAX_SQRT = 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_341; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(uint256 _uniswapV3FactoryAndFF, uint256 _uniswapV3PoolInitCodeHash) { UNISWAP_V3_FACTORY_AND_FF = _uniswapV3FactoryAndFF; UNISWAP_V3_POOL_INIT_CODE_HASH = _uniswapV3PoolInitCodeHash; } /*////////////////////////////////////////////////////////////// EXTERNAL //////////////////////////////////////////////////////////////*/ // @inheritdoc IUniswapV3SwapCallback function uniswapV3SwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata data ) external whenNotPaused { // Initialize variables uint256 uniswapV3FactoryAndFF = UNISWAP_V3_FACTORY_AND_FF; uint256 uniswapV3PoolInitCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH; address permit2Address = PERMIT2; address poolAddress; // 160 (single pool data) + 352 (permit2 length) bool isPermit2 = data.length == 512; // Check if the caller is a UniswapV3Pool deployed by the canonical UniswapV3Factory //solhint-disable-next-line no-inline-assembly assembly { // Pool address poolAddress := caller() // Get free memory pointer let ptr := mload(64) // We need make sure the caller is a UniswapV3Pool deployed by the canonical UniswapV3Factory // 1. Prepare data for calculating the pool address // Store ff+factory address, Load token0, token1, fee from bytes calldata and store pool init code hash // Store 0xff + factory address (right padded) mstore(ptr, uniswapV3FactoryAndFF) // Store data offset + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE) let token0Offset := add(ptr, 21) // Copy token0, token1, fee to free memory pointer + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE) + 1 byte // (direction) calldatacopy(add(token0Offset, 1), add(data.offset, 65), 95) // 2. Calculate the pool address // We can do this by first calling the keccak256 function on the fetched values and then // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, // token1, fee)), POOL_INIT_CODE_HASH)); // The first 20 bytes of the computed address are the pool address // Calculate keccak256(abi.encode(address(token0), address(token1), fee)) mstore(token0Offset, keccak256(token0Offset, 96)) // Store POOL_INIT_CODE_HASH mstore(add(token0Offset, 32), uniswapV3PoolInitCodeHash) // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address), keccak256(abi.encode(token0, // token1, fee)), POOL_INIT_CODE_HASH)); mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32 // Get the first 20 bytes of the computed address let computedAddress := and(mload(ptr), 0xffffffffffffffffffffffffffffffffffffffff) // Check if the caller matches the computed address (and revert if not) if xor(poolAddress, computedAddress) { mstore(0, 0x48f5c3ed00000000000000000000000000000000000000000000000000000000) // store the selector // (error InvalidCaller()) revert(0, 4) // revert with error selector } } // Check if data length is greater than 160 bytes (1 pool) // If the data length is greater than 160 bytes, we know that we are executing a multi-hop swapExactAmountOut // by recursively calling swapExactAmountOut on the next pool, until we reach the last pool in the data and // then we will transfer the tokens to the pool if (data.length > 160 && !isPermit2) { // Initialize recursive variables address payer; // solhint-disable-next-line no-inline-assembly assembly { // Copy payer address from calldata payer := calldataload(164) } // Recursive call swapExactAmountOut _callUniswapV3PoolsSwapExactAmountOut(amount0Delta > 0 ? -amount0Delta : -amount1Delta, data, payer); } else { // solhint-disable-next-line no-inline-assembly assembly { // Token to send to the pool let token // Amount to send to the pool let amount // Get free memory pointer let ptr := mload(64) // If the caller is the computed address, then we can safely assume that the caller is a UniswapV3Pool // deployed by the canonical UniswapV3Factory // 3. Transfer amount to the pool // Check if amount0Delta or amount1Delta is positive and which token we need to send to the pool if sgt(amount0Delta, 0) { // If amount0Delta is positive, we need to send amount0Delta token0 to the pool token := and(calldataload(add(data.offset, 64)), 0xffffffffffffffffffffffffffffffffffffffff) amount := amount0Delta } if sgt(amount1Delta, 0) { // If amount1Delta is positive, we need to send amount1Delta token1 to the pool token := calldataload(add(data.offset, 96)) amount := amount1Delta } // Based on the data passed to the callback, we know the fromAddress that will pay for the // swap, if it is this contract, we will execute the transfer() function, // otherwise, we will execute transferFrom() // Check if fromAddress is this contract let fromAddress := calldataload(164) switch eq(fromAddress, address()) // If fromAddress is this contract, execute transfer() case 1 { // Prepare external call data mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // store the // selector // (function transfer(address recipient, uint256 amount)) mstore(add(ptr, 4), poolAddress) // store the recipient mstore(add(ptr, 36), amount) // store the amount let success := call(gas(), token, 0, ptr, 68, 0, 32) // call transfer if success { switch returndatasize() // check the return data size case 0 { success := gt(extcodesize(token), 0) } default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) } } if iszero(success) { mstore(0, 0x1bbb4abe00000000000000000000000000000000000000000000000000000000) // store the // selector // (error CallbackTransferFailed()) revert(0, 4) // revert with error selector } } // If fromAddress is not this contract, execute transferFrom() or permitTransferFrom() default { switch isPermit2 // If permit2 is not present, execute transferFrom() case 0 { mstore(ptr, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // store the // selector // (function transferFrom(address sender, address recipient, // uint256 amount)) mstore(add(ptr, 4), fromAddress) // store the sender mstore(add(ptr, 36), poolAddress) // store the recipient mstore(add(ptr, 68), amount) // store the amount let success := call(gas(), token, 0, ptr, 100, 0, 32) // call transferFrom if success { switch returndatasize() // check the return data size case 0 { success := gt(extcodesize(token), 0) } default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) } } if iszero(success) { mstore(0, 0x1bbb4abe00000000000000000000000000000000000000000000000000000000) // store the // selector // (error CallbackTransferFailed()) revert(0, 4) // revert with error selector } } // If permit2 is present, execute permitTransferFrom() default { // Otherwise Permit2.permitTransferFrom // Store function selector mstore(ptr, 0x30f28b7a00000000000000000000000000000000000000000000000000000000) // permitTransferFrom() calldatacopy(add(ptr, 4), 292, 352) // Copy data to memory mstore(add(ptr, 132), poolAddress) // Store pool address as recipient mstore(add(ptr, 164), amount) // Store amount as amount mstore(add(ptr, 196), fromAddress) // Store payer // Call permit2.permitTransferFrom and revert if call failed if iszero(call(gas(), permit2Address, 0, ptr, 356, 0, 0)) { mstore(0, 0x6b836e6b00000000000000000000000000000000000000000000000000000000) // Store // error selector // error Permit2Failed() revert(0, 4) } } } } } } /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ /// @dev Loops through pools and performs swaps function _callUniswapV3PoolsSwapExactAmountIn( int256 fromAmount, bytes calldata pools, address fromAddress, bytes calldata permit2 ) internal returns (uint256 receivedAmount) { uint256 uniswapV3FactoryAndFF = UNISWAP_V3_FACTORY_AND_FF; uint256 uniswapV3PoolInitCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH; // solhint-disable-next-line no-inline-assembly assembly { //---------------------------------// // Loop Swap Through Pools //---------------------------------// // Calculate pool count let poolCount := div(pools.length, 96) // Initialize variables let p := 0 let poolAddress := 0 let nextPoolAddress := 0 let direction := 0 let isPermit2 := gt(permit2.length, 256) // Get free memory pointer let ptr := mload(64) // Loop through pools for { let i := 0 } lt(i, poolCount) { i := add(i, 1) } { // Check if it is the first pool if iszero(p) { //---------------------------------// // Calculate Pool Address //---------------------------------// // Calculate the pool address // We can do this by first calling the keccak256 function on the passed pool values and then // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, // token1, fee)), POOL_INIT_CODE_HASH)); // The first 20 bytes of the computed address are the pool address // Store 0xff + factory address (right padded) mstore(ptr, uniswapV3FactoryAndFF) // Store pools offset + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE) let token0ptr := add(ptr, 21) // Copy pool data (skip first byte) to free memory pointer + 21 bytes (UNISWAP_V3_FACTORY_AND_FF // SIZE) calldatacopy(add(token0ptr, 1), add(pools.offset, 1), 95) // Calculate keccak256(abi.encode(address(token0), address(token1), fee)) mstore(token0ptr, keccak256(token0ptr, 96)) // Store POOL_INIT_CODE_HASH mstore(add(token0ptr, 32), uniswapV3PoolInitCodeHash) // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, // token1, fee)), POOL_INIT_CODE_HASH)); mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32 // Load pool p := mload(ptr) // Get the first 20 bytes of the computed address poolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff) //---------------------------------// } // Direction is the first bit of the pool data direction := shr(255, calldataload(add(pools.offset, mul(i, 96)))) // Check if it is not the last pool if lt(add(i, 1), poolCount) { //---------------------------------// // Calculate Next Pool Address //---------------------------------// // Store 0xff + factory address (right padded) mstore(ptr, uniswapV3FactoryAndFF) // Store pools offset + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE) let token0ptr := add(ptr, 21) // Copy next pool data to free memory pointer + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE) calldatacopy(add(token0ptr, 1), add(add(pools.offset, 1), mul(add(i, 1), 96)), 95) // Calculate keccak256(abi.encode(address(token0), address(token1), fee)) mstore(token0ptr, keccak256(token0ptr, 96)) // Store POOL_INIT_CODE_HASH mstore(add(token0ptr, 32), uniswapV3PoolInitCodeHash) // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, // token1, fee)), POOL_INIT_CODE_HASH)); mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32 // Load pool p := mload(ptr) // Get the first 20 bytes of the computed address nextPoolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff) //---------------------------------// } // Adjust fromAddress and fromAmount if it's not the first pool if gt(i, 0) { fromAddress := address() } //---------------------------------// // Perform Swap //---------------------------------// //---------------------------------// // Return based on direction //---------------------------------// // Initialize data length let dataLength := 0xa0 // Initialize total data length let totalDataLength := 356 // If permit2 is present include permit2 data length in total data length if eq(isPermit2, 1) { totalDataLength := add(totalDataLength, permit2.length) dataLength := add(dataLength, permit2.length) } // Return amount0 or amount1 depending on direction switch direction case 0 { // Prepare external call data // Store swap selector (0x128acb08) mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000) // Store toAddress mstore(add(ptr, 4), address()) // Store direction mstore(add(ptr, 36), 0) // Store fromAmount mstore(add(ptr, 68), fromAmount) // Store sqrtPriceLimitX96 mstore(add(ptr, 100), UNISWAP_V3_MAX_SQRT) // Store data offset mstore(add(ptr, 132), 0xa0) /// Store data length mstore(add(ptr, 164), dataLength) // Store fromAddress mstore(add(ptr, 228), fromAddress) // Store token0, token1, fee calldatacopy(add(ptr, 260), add(pools.offset, mul(i, 96)), 96) // If permit2 is present, store permit2 data if eq(isPermit2, 1) { // Store permit2 data calldatacopy(add(ptr, 356), permit2.offset, permit2.length) } // Perform the external 'swap' call if iszero(call(gas(), poolAddress, 0, ptr, totalDataLength, ptr, 32)) { // store return value directly to free memory pointer // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } // If direction is 0, return amount0 fromAmount := mload(ptr) } default { // Prepare external call data // Store swap selector (0x128acb08) mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000) // Store toAddress mstore(add(ptr, 4), address()) // Store direction mstore(add(ptr, 36), 1) // Store fromAmount mstore(add(ptr, 68), fromAmount) // Store sqrtPriceLimitX96 mstore(add(ptr, 100), UNISWAP_V3_MIN_SQRT) // Store data offset mstore(add(ptr, 132), 0xa0) /// Store data length mstore(add(ptr, 164), dataLength) // Store fromAddress mstore(add(ptr, 228), fromAddress) // Store token0, token1, fee calldatacopy(add(ptr, 260), add(pools.offset, mul(i, 96)), 96) // If permit2 is present, store permit2 data if eq(isPermit2, 1) { // Store permit2 data calldatacopy(add(ptr, 356), permit2.offset, permit2.length) } // Perform the external 'swap' call if iszero(call(gas(), poolAddress, 0, ptr, totalDataLength, ptr, 64)) { // store return value directly to free memory pointer // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } // If direction is 1, return amount1 fromAmount := mload(add(ptr, 32)) } //---------------------------------// //---------------------------------// // The next pool address was already calculated so we can set it as the current pool address for the // next iteration of the loop poolAddress := nextPoolAddress // fromAmount = -fromAmount fromAmount := sub(0, fromAmount) } //---------------------------------// } return fromAmount.toUint256(); } /// @dev Recursively loops through pools and performs swaps function _callUniswapV3PoolsSwapExactAmountOut( int256 fromAmount, bytes calldata pools, address fromAddress ) internal returns (uint256 spentAmount, uint256 receivedAmount) { uint256 uniswapV3FactoryAndFF = UNISWAP_V3_FACTORY_AND_FF; uint256 uniswapV3PoolInitCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH; // solhint-disable-next-line no-inline-assembly assembly { //---------------------------------// // Adjust data received from recursive call //---------------------------------// // Initialize variables let poolsStartOffset := pools.offset let poolsLength := pools.length let previousPoolAddress := 0 // Check if pools length is not divisible by 96 if gt(mod(pools.length, 96), 0) { // Check if pools length is greater than 128 bytes (1 pool) if gt(pools.length, 160) { // Get the previous pool address from the first 20 bytes of pool data previousPoolAddress := and(calldataload(pools.offset), 0xffffffffffffffffffffffffffffffffffffffff) // Relculate the offset to skip data poolsStartOffset := add(pools.offset, 160) // Recalculate the length to skip data poolsLength := sub(pools.length, 160) } } // Get free memory pointer let ptr := mload(64) //---------------------------------// // Calculate Pool Address //---------------------------------// // Calculate the pool address // We can do this by first calling the keccak256 function on the passed pool values and then // calculating keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, // token1, fee)), POOL_INIT_CODE_HASH)); // The first 20 bytes of the computed address are the pool address // Store 0xff + factory address (right padded) mstore(ptr, uniswapV3FactoryAndFF) // Store pools offset + 21 bytes (UNISWAP_V3_FACTORY_AND_FF SIZE) let token0ptr := add(ptr, 21) // Copy pool data (skip first byte) to free memory pointer + 21 bytes (UNISWAP_V3_FACTORY_AND_FF // SIZE) calldatacopy(add(token0ptr, 1), add(poolsStartOffset, 1), 95) // Calculate keccak256(abi.encode(address(token0), address(token1), fee)) mstore(token0ptr, keccak256(token0ptr, 96)) // Store POOL_INIT_CODE_HASH mstore(add(token0ptr, 32), uniswapV3PoolInitCodeHash) // Calculate keccak256(abi.encodePacked(hex'ff', address(factory_address), // keccak256(abi.encode(token0, // token1, fee)), POOL_INIT_CODE_HASH)); mstore(ptr, keccak256(ptr, 85)) // 21 + 32 + 32 // Load pool let p := mload(ptr) // Get the first 20 bytes of the computed address let poolAddress := and(p, 0xffffffffffffffffffffffffffffffffffffffff) //---------------------------------// //---------------------------------// // Adjust toAddress //---------------------------------// let toAddress := address() // If it's not the first entry to recursion, we use the pool address from the previous pool as // the toAddress if xor(previousPoolAddress, 0) { toAddress := previousPoolAddress } //---------------------------------// // Direction is the first bit of the pool data let direction := shr(255, calldataload(poolsStartOffset)) //---------------------------------// // Perform Swap //---------------------------------// //---------------------------------// // Return based on direction //---------------------------------// // Return amount0 or amount1 depending on direction switch direction case 0 { // Prepare external call data // Store swap selector (0x128acb08) mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000) // Store toAddress mstore(add(ptr, 4), toAddress) // Store direction mstore(add(ptr, 36), 0) // Store fromAmount mstore(add(ptr, 68), fromAmount) // Store sqrtPriceLimitX96 mstore(add(ptr, 100), UNISWAP_V3_MAX_SQRT) // Store data offset mstore(add(ptr, 132), 0xa0) /// Store data length mstore(add(ptr, 164), add(64, poolsLength)) // Store poolAddress mstore(add(ptr, 196), poolAddress) // Store fromAddress mstore(add(ptr, 228), fromAddress) // Store token0, token1, fee calldatacopy(add(ptr, 260), poolsStartOffset, poolsLength) // Perform the external 'swap' call if iszero(call(gas(), poolAddress, 0, ptr, add(poolsLength, 260), ptr, 64)) { // store return value directly to free memory pointer // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } // If direction is 0, return amount0 as fromAmount fromAmount := mload(ptr) // return amount1 as spentAmount spentAmount := mload(add(ptr, 32)) } default { // Prepare external call data // Store swap selector (0x128acb08) mstore(ptr, 0x128acb0800000000000000000000000000000000000000000000000000000000) // Store toAddress mstore(add(ptr, 4), toAddress) // Store direction mstore(add(ptr, 36), 1) // Store fromAmount mstore(add(ptr, 68), fromAmount) // Store sqrtPriceLimitX96 mstore(add(ptr, 100), UNISWAP_V3_MIN_SQRT) // Store data offset mstore(add(ptr, 132), 0xa0) /// Store data length mstore(add(ptr, 164), add(64, poolsLength)) // Store poolAddress mstore(add(ptr, 196), poolAddress) // Store fromAddress mstore(add(ptr, 228), fromAddress) // Store token0, token1, fee calldatacopy(add(ptr, 260), poolsStartOffset, poolsLength) // Perform the external 'swap' call if iszero(call(gas(), poolAddress, 0, ptr, add(poolsLength, 260), ptr, 64)) { // store return value directly to free memory pointer // The call failed; we retrieve the exact error message and revert with it returndatacopy(0, 0, returndatasize()) // Copy the error message to the start of memory revert(0, returndatasize()) // Revert with the error message } // If direction is 1, return amount1 as fromAmount fromAmount := mload(add(ptr, 32)) // return amount0 as spentAmount spentAmount := mload(ptr) } //---------------------------------// //---------------------------------// // fromAmount = -fromAmount fromAmount := sub(0, fromAmount) } return (spentAmount, fromAmount.toUint256()); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IWETH } from "../interfaces/IWETH.sol"; /// @title WETHUtils /// @notice A contract containing common utilities for WETH abstract contract WETHUtils { /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @dev WETH address IWETH public immutable WETH; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _weth) { WETH = IWETH(_weth); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// @title Permit2Utils /// @notice A contract containing common utilities for Permit2 abstract contract Permit2Utils { /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ error Permit2Failed(); /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ /// @dev Permit2 address address public immutable PERMIT2; // solhint-disable-line var-name-mixedcase /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _permit2) { PERMIT2 = _permit2; } /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ /// @dev Parses data and executes permit2.permitTransferFrom, reverts if it fails function permit2TransferFrom(bytes calldata data, address recipient, uint256 amount) internal { address targetAddress = PERMIT2; // solhint-disable-next-line no-inline-assembly assembly { // Get free memory pointer let ptr := mload(64) // Store function selector mstore(ptr, 0x30f28b7a00000000000000000000000000000000000000000000000000000000) // permitTransferFrom() // Copy data to memory calldatacopy(add(ptr, 4), data.offset, data.length) // Store recipient mstore(add(ptr, 132), recipient) // Store amount mstore(add(ptr, 164), amount) // Store owner mstore(add(ptr, 196), caller()) // Call permit2.permitTransferFrom and revert if call failed if iszero(call(gas(), targetAddress, 0, ptr, add(data.length, 4), 0, 0)) { mstore(0, 0x6b836e6b00000000000000000000000000000000000000000000000000000000) // Store error selector // error Permit2Failed() revert(0, 4) } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { BalancerV2Data } from "../AugustusV6Types.sol"; /// @title IBalancerV2SwapExactAmountIn /// @notice Interface for executing swapExactAmountIn directly on Balancer V2 pools interface IBalancerV2SwapExactAmountIn is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @notice Executes a swapExactAmountIn on Balancer V2 pools /// @param balancerData Struct containing data for the swap /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit Permit data for the swap /// @param data The calldata to execute /// the first 20 bytes are the beneficiary address and the left most bit is the approve flag /// @return receivedAmount The amount of destToken received after fees /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountInOnBalancerV2( BalancerV2Data calldata balancerData, uint256 partnerAndFee, bytes calldata permit, bytes calldata data ) external payable returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; /// @title ERC20Utils /// @notice Optimized functions for ERC20 tokens library ERC20Utils { /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ error IncorrectEthAmount(); error PermitFailed(); error TransferFromFailed(); error TransferFailed(); error ApprovalFailed(); /*////////////////////////////////////////////////////////////// CONSTANTS //////////////////////////////////////////////////////////////*/ IERC20 internal constant ETH = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); /*////////////////////////////////////////////////////////////// APPROVE //////////////////////////////////////////////////////////////*/ /// @dev Vendored from Solady by @vectorized - SafeTransferLib.approveWithRetry /// https://github.com/Vectorized/solady/src/utils/SafeTransferLib.sol#L325 /// Instead of approving a specific amount, this function approves for uint256(-1) (type(uint256).max). function approve(IERC20 token, address to) internal { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { mstore(0x14, to) // Store the `to` argument. mstore(0x34, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) // Store the `amount` // argument (type(uint256).max). mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. // Perform the approval, retrying upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x34, 0) // Store 0 for the `amount`. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval. mstore(0x34, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) // Store // type(uint256).max for the `amount`. // Retry the approval, reverting upon failure. if iszero( and( or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0, 0x8164f84200000000000000000000000000000000000000000000000000000000) // store the selector (error ApprovalFailed()) revert(0, 4) // revert with error selector } } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /*////////////////////////////////////////////////////////////// PERMIT //////////////////////////////////////////////////////////////*/ /// @dev Executes an ERC20 permit and reverts if invalid length is provided function permit(IERC20 token, bytes calldata data) internal { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { // check the permit length switch data.length // 32 * 7 = 224 EIP2612 Permit case 224 { let x := mload(64) // get the free memory pointer mstore(x, 0xd505accf00000000000000000000000000000000000000000000000000000000) // store the selector // function permit(address owner, address spender, uint256 // amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) calldatacopy(add(x, 4), data.offset, 224) // store the args pop(call(gas(), token, 0, x, 228, 0, 32)) // call ERC20 permit, skip checking return data } // 32 * 8 = 256 DAI-Style Permit case 256 { let x := mload(64) // get the free memory pointer mstore(x, 0x8fcbaf0c00000000000000000000000000000000000000000000000000000000) // store the selector // function permit(address holder, address spender, uint256 // nonce, uint256 expiry, bool allowed, uint8 v, bytes32 r, bytes32 s) calldatacopy(add(x, 4), data.offset, 256) // store the args pop(call(gas(), token, 0, x, 260, 0, 32)) // call ERC20 permit, skip checking return data } default { mstore(0, 0xb78cb0dd00000000000000000000000000000000000000000000000000000000) // store the selector // (error PermitFailed()) revert(0, 4) } } } /*////////////////////////////////////////////////////////////// ETH //////////////////////////////////////////////////////////////*/ /// @dev Returns 1 if the token is ETH, 0 if not ETH function isETH(IERC20 token, uint256 amount) internal view returns (uint256 fromETH) { // solhint-disable-next-line no-inline-assembly assembly ("memory-safe") { // If token is ETH if eq(token, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) { // if msg.value is not equal to fromAmount, then revert if xor(amount, callvalue()) { mstore(0, 0x8b6ebb4d00000000000000000000000000000000000000000000000000000000) // store the selector // (error IncorrectEthAmount()) revert(0, 4) // revert with error selector } // return 1 if ETH fromETH := 1 } // If token is not ETH if xor(token, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) { // if msg.value is not equal to 0, then revert if gt(callvalue(), 0) { mstore(0, 0x8b6ebb4d00000000000000000000000000000000000000000000000000000000) // store the selector // (error IncorrectEthAmount()) revert(0, 4) // revert with error selector } } } // return 0 if not ETH } /*////////////////////////////////////////////////////////////// TRANSFER //////////////////////////////////////////////////////////////*/ /// @dev Executes transfer and reverts if it fails, works for both ETH and ERC20 transfers function safeTransfer(IERC20 token, address recipient, uint256 amount) internal returns (bool success) { // solhint-disable-next-line no-inline-assembly assembly { switch eq(token, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) // ETH case 1 { // transfer ETH // Cap gas at 10000 to avoid reentrancy success := call(10000, recipient, amount, 0, 0, 0, 0) } // ERC20 default { let x := mload(64) // get the free memory pointer mstore(x, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // store the selector // (function transfer(address recipient, uint256 amount)) mstore(add(x, 4), recipient) // store the recipient mstore(add(x, 36), amount) // store the amount success := call(gas(), token, 0, x, 68, 0, 32) // call transfer if success { switch returndatasize() // check the return data size case 0 { success := gt(extcodesize(token), 0) } default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) } } } if iszero(success) { mstore(0, 0x90b8ec1800000000000000000000000000000000000000000000000000000000) // store the selector // (error TransferFailed()) revert(0, 4) // revert with error selector } } } /*////////////////////////////////////////////////////////////// TRANSFER FROM //////////////////////////////////////////////////////////////*/ /// @dev Executes transferFrom and reverts if it fails function safeTransferFrom( IERC20 srcToken, address sender, address recipient, uint256 amount ) internal returns (bool success) { // solhint-disable-next-line no-inline-assembly assembly { let x := mload(64) // get the free memory pointer mstore(x, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // store the selector // (function transferFrom(address sender, address recipient, // uint256 amount)) mstore(add(x, 4), sender) // store the sender mstore(add(x, 36), recipient) // store the recipient mstore(add(x, 68), amount) // store the amount success := call(gas(), srcToken, 0, x, 100, 0, 32) // call transferFrom if success { switch returndatasize() // check the return data size case 0 { success := gt(extcodesize(srcToken), 0) } default { success := and(gt(returndatasize(), 31), eq(mload(0), 1)) } } if iszero(success) { mstore(x, 0x7939f42400000000000000000000000000000000000000000000000000000000) // store the selector // (error TransferFromFailed()) revert(x, 4) // revert with error selector } } } /*////////////////////////////////////////////////////////////// BALANCE //////////////////////////////////////////////////////////////*/ /// @dev Returns the balance of an account, works for both ETH and ERC20 tokens function getBalance(IERC20 token, address account) internal view returns (uint256 balanceOf) { // solhint-disable-next-line no-inline-assembly assembly { switch eq(token, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) // ETH case 1 { balanceOf := balance(account) } // ERC20 default { let x := mload(64) // get the free memory pointer mstore(x, 0x70a0823100000000000000000000000000000000000000000000000000000000) // store the selector // (function balanceOf(address account)) mstore(add(x, 4), account) // store the account let success := staticcall(gas(), token, x, 36, x, 32) // call balanceOf if success { balanceOf := mload(x) } // load the balance } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; /*////////////////////////////////////////////////////////////// GENERIC SWAP DATA //////////////////////////////////////////////////////////////*/ /// @notice Struct containg data for generic swapExactAmountIn/swapExactAmountOut /// @param srcToken The token to swap from /// @param destToken The token to swap to /// @param fromAmount The amount of srcToken to swap /// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut /// @param toAmount The minimum amount of destToken to receive /// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut /// @param quotedAmount The quoted expected amount of destToken/srcToken /// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut /// @param metadata Packed uuid and additional metadata /// @param beneficiary The address to send the swapped tokens to struct GenericData { IERC20 srcToken; IERC20 destToken; uint256 fromAmount; uint256 toAmount; uint256 quotedAmount; bytes32 metadata; address payable beneficiary; } /*////////////////////////////////////////////////////////////// UNISWAPV2 //////////////////////////////////////////////////////////////*/ /// @notice Struct for UniswapV2 swapExactAmountIn/swapExactAmountOut data /// @param srcToken The token to swap from /// @param destToken The token to swap to /// @param fromAmount The amount of srcToken to swap /// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut /// @param quotedAmount The quoted expected amount of destToken/srcToken /// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut /// @param toAmount The minimum amount of destToken to receive /// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut /// @param metadata Packed uuid and additional metadata /// @param beneficiary The address to send the swapped tokens to /// @param pools data consisting of concatenated token0 and token1 address for each pool with the direction flag being /// the right most bit of the packed token0-token1 pair bytes used in the path struct UniswapV2Data { IERC20 srcToken; IERC20 destToken; uint256 fromAmount; uint256 toAmount; uint256 quotedAmount; bytes32 metadata; address payable beneficiary; bytes pools; } /*////////////////////////////////////////////////////////////// UNISWAPV3 //////////////////////////////////////////////////////////////*/ /// @notice Struct for UniswapV3 swapExactAmountIn/swapExactAmountOut data /// @param srcToken The token to swap from /// @param destToken The token to swap to /// @param fromAmount The amount of srcToken to swap /// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut /// @param quotedAmount The quoted expected amount of destToken/srcToken /// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut /// @param toAmount The minimum amount of destToken to receive /// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut /// @param metadata Packed uuid and additional metadata /// @param beneficiary The address to send the swapped tokens to /// @param pools data consisting of concatenated token0- /// token1-fee bytes for each pool used in the path, with the direction flag being the left most bit of token0 in the /// concatenated bytes struct UniswapV3Data { IERC20 srcToken; IERC20 destToken; uint256 fromAmount; uint256 toAmount; uint256 quotedAmount; bytes32 metadata; address payable beneficiary; bytes pools; } /*////////////////////////////////////////////////////////////// CURVE V1 //////////////////////////////////////////////////////////////*/ /// @notice Struct for CurveV1 swapExactAmountIn data /// @param curveData Packed data for the Curve pool, first 160 bits is the target exchange address, /// the 161st bit is the approve flag, bits from (162 - 163) are used for the wrap flag, //// bits from (164 - 165) are used for the swapType flag and the last 91 bits are unused: /// Approve Flag - a) 0 -> do not approve b) 1 -> approve /// Wrap Flag - a) 0 -> do not wrap b) 1 -> wrap native & srcToken == eth /// c) 2 -> unwrap and destToken == eth d) 3 - >srcToken == eth && do not wrap /// Swap Type Flag - a) 0 -> EXCHANGE b) 1 -> EXCHANGE_UNDERLYING /// @param curveAssets Packed uint128 index i and uint128 index j of the pool /// The first 128 bits is the index i and the second 128 bits is the index j /// @param srcToken The token to swap from /// @param destToken The token to swap to /// @param fromAmount The amount of srcToken to swap /// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut /// @param toAmount The minimum amount that must be recieved /// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut /// @param quotedAmount The expected amount of destToken to be recieved /// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut /// @param metadata Packed uuid and additional metadata /// @param beneficiary The address to send the swapped tokens to struct CurveV1Data { uint256 curveData; uint256 curveAssets; IERC20 srcToken; IERC20 destToken; uint256 fromAmount; uint256 toAmount; uint256 quotedAmount; bytes32 metadata; address payable beneficiary; } /*////////////////////////////////////////////////////////////// CURVE V2 //////////////////////////////////////////////////////////////*/ /// @notice Struct for CurveV2 swapExactAmountIn data /// @param curveData Packed data for the Curve pool, first 160 bits is the target exchange address, /// the 161st bit is the approve flag, bits from (162 - 163) are used for the wrap flag, //// bits from (164 - 165) are used for the swapType flag and the last 91 bits are unused /// Approve Flag - a) 0 -> do not approve b) 1 -> approve /// Approve Flag - a) 0 -> do not approve b) 1 -> approve /// Wrap Flag - a) 0 -> do not wrap b) 1 -> wrap native & srcToken == eth /// c) 2 -> unwrap and destToken == eth d) 3 - >srcToken == eth && do not wrap /// Swap Type Flag - a) 0 -> EXCHANGE b) 1 -> EXCHANGE_UNDERLYING c) 2 -> EXCHANGE_UNDERLYING_FACTORY_ZAP /// @param i The index of the srcToken /// @param j The index of the destToken /// The first 128 bits is the index i and the second 128 bits is the index j /// @param poolAddress The address of the CurveV2 pool (only used for EXCHANGE_UNDERLYING_FACTORY_ZAP) /// @param srcToken The token to swap from /// @param destToken The token to swap to /// @param fromAmount The amount of srcToken to swap /// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut /// @param toAmount The minimum amount that must be recieved /// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut /// @param quotedAmount The expected amount of destToken to be recieved /// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut /// @param metadata Packed uuid and additional metadata /// @param beneficiary The address to send the swapped tokens to struct CurveV2Data { uint256 curveData; uint256 i; uint256 j; address poolAddress; IERC20 srcToken; IERC20 destToken; uint256 fromAmount; uint256 toAmount; uint256 quotedAmount; bytes32 metadata; address payable beneficiary; } /*////////////////////////////////////////////////////////////// BALANCER V2 //////////////////////////////////////////////////////////////*/ /// @notice Struct for BalancerV2 swapExactAmountIn data /// @param fromAmount The amount of srcToken to swap /// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut /// @param toAmount The minimum amount of destToken to receive /// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut /// @param quotedAmount The quoted expected amount of destToken/srcToken /// = quotedAmountOut for swapExactAmountIn and quotedAmountIn for swapExactAmountOut /// @param metadata Packed uuid and additional metadata /// @param beneficiaryAndApproveFlag The beneficiary address and approve flag packed into one uint256, /// the first 20 bytes are the beneficiary address and the left most bit is the approve flag struct BalancerV2Data { uint256 fromAmount; uint256 toAmount; uint256 quotedAmount; bytes32 metadata; uint256 beneficiaryAndApproveFlag; } /*////////////////////////////////////////////////////////////// MAKERPSM //////////////////////////////////////////////////////////////*/ /// @notice Struct for Maker PSM swapExactAmountIn data /// @param srcToken The token to swap from /// @param destToken The token to swap to /// @param fromAmount The amount of srcToken to swap /// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut /// @param toAmount The minimum amount of destToken to receive /// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut /// @param toll Used to calculate gem amount for the swapExactAmountIn /// @param to18ConversionFactor Used to calculate gem amount for the swapExactAmountIn /// @param gemJoinAddress The address of the gemJoin contract /// @param exchange The address of the exchange contract /// @param metadata Packed uuid and additional metadata /// @param beneficiaryDirectionApproveFlag The beneficiary address, swap direction and approve flag packed /// into one uint256, the first 20 bytes are the beneficiary address, the left most bit is the approve flag and the /// second left most bit is the swap direction flag, 0 for swapExactAmountIn and 1 for swapExactAmountOut struct MakerPSMData { IERC20 srcToken; IERC20 destToken; uint256 fromAmount; uint256 toAmount; uint256 toll; uint256 to18ConversionFactor; address exchange; address gemJoinAddress; bytes32 metadata; uint256 beneficiaryDirectionApproveFlag; } /*////////////////////////////////////////////////////////////// AUGUSTUS RFQ //////////////////////////////////////////////////////////////*/ /// @notice Order struct for Augustus RFQ /// @param nonceAndMeta The nonce and meta data packed into one uint256, /// the first 160 bits is the user address and the last 96 bits is the nonce /// @param expiry The expiry of the order /// @param makerAsset The address of the maker asset /// @param takerAsset The address of the taker asset /// @param maker The address of the maker /// @param taker The address of the taker, if the taker is address(0) anyone can take the order /// @param makerAmount The amount of makerAsset /// @param takerAmount The amount of takerAsset struct Order { uint256 nonceAndMeta; uint128 expiry; address makerAsset; address takerAsset; address maker; address taker; uint256 makerAmount; uint256 takerAmount; } /// @notice Struct containing order info for Augustus RFQ /// @param order The order struct /// @param signature The signature for the order /// @param takerTokenFillAmount The amount of takerToken to fill /// @param permitTakerAsset The permit data for the taker asset /// @param permitMakerAsset The permit data for the maker asset struct OrderInfo { Order order; bytes signature; uint256 takerTokenFillAmount; bytes permitTakerAsset; bytes permitMakerAsset; } /// @notice Struct containing common data for executing swaps on Augustus RFQ /// @param fromAmount The amount of srcToken to swap /// = amountIn for swapExactAmountIn and maxAmountIn for swapExactAmountOut /// @param toAmount The minimum amount of destToken to receive /// = minAmountOut for swapExactAmountIn and amountOut for swapExactAmountOut /// @param wrapApproveDirection The wrap, approve and direction flag packed into one uint8, /// the first 2 bits is wrap flag (10 for wrap dest, 01 for wrap src, 00 for no wrap), the next bit is the approve flag /// (1 for approve, 0 for no approve) and the last bit is the direction flag (0 for swapExactAmountIn and 1 for /// swapExactAmountOut) /// @param metadata Packed uuid and additional metadata struct AugustusRFQData { uint256 fromAmount; uint256 toAmount; uint8 wrapApproveDirection; bytes32 metadata; address payable beneficiary; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { CurveV1Data } from "../AugustusV6Types.sol"; /// @title ICurveV1SwapExactAmountIn /// @notice Interface for direct swaps on Curve V1 interface ICurveV1SwapExactAmountIn is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @notice Executes a swapExactAmountIn on Curve V1 pools /// @param curveV1Data Struct containing data for the swap /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit Permit data for the swap /// @return receivedAmount The amount of destToken received after fees /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountInOnCurveV1( CurveV1Data calldata curveV1Data, uint256 partnerAndFee, bytes calldata permit ) external payable returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Storage import { AugustusStorage } from "../storage/AugustusStorage.sol"; /// @title PauseUtils /// @notice Provides a modifier to check if the contract is paused abstract contract PauseUtils is AugustusStorage { /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ /// @notice Error emitted when the contract is paused error ContractPaused(); /*////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////*/ // Check if the contract is paused, if it is, revert modifier whenNotPaused() { if (paused) { revert ContractPaused(); } _; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { CurveV2Data } from "../AugustusV6Types.sol"; /// @title ICurveV2SwapExactAmountIn /// @notice Interface for direct swaps on Curve V2 interface ICurveV2SwapExactAmountIn is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @notice Executes a swapExactAmountIn on Curve V2 pools /// @param curveV2Data Struct containing data for the swap /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit Permit data for the swap /// @return receivedAmount The amount of destToken received after fees /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountInOnCurveV2( CurveV2Data calldata curveV2Data, uint256 partnerAndFee, bytes calldata permit ) external payable returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { UniswapV2Data } from "../AugustusV6Types.sol"; /// @title IUniswapV2SwapExactAmountIn /// @notice Interface for direct swaps on Uniswap V2 interface IUniswapV2SwapExactAmountIn is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT //////////////////////////////////////////////////////////////*/ /// @notice Executes a swapExactAmountIn on Uniswap V2 pools /// @param uniData struct containing data for the swap /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit The permit data /// @return receivedAmount The amount of destToken received after fees /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountInOnUniswapV2( UniswapV2Data calldata uniData, uint256 partnerAndFee, bytes calldata permit ) external payable returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { UniswapV3Data } from "../AugustusV6Types.sol"; /// @title IUniswapV3SwapExactAmountIn /// @notice Interface for executing direct swapExactAmountIn on Uniswap V3 interface IUniswapV3SwapExactAmountIn is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @notice Executes a swapExactAmountIn on Uniswap V3 pools /// @param uniData struct containing data for the swap /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit The permit data /// @return receivedAmount The amount of destToken received after fees /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountInOnUniswapV3( UniswapV3Data calldata uniData, uint256 partnerAndFee, bytes calldata permit ) external payable returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe integer casting library that reverts on overflow. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol) library SafeCastLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ error Overflow(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* UNSIGNED INTEGER SAFE CASTING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function toUint8(uint256 x) internal pure returns (uint8) { if (x >= 1 << 8) _revertOverflow(); return uint8(x); } function toUint16(uint256 x) internal pure returns (uint16) { if (x >= 1 << 16) _revertOverflow(); return uint16(x); } function toUint24(uint256 x) internal pure returns (uint24) { if (x >= 1 << 24) _revertOverflow(); return uint24(x); } function toUint32(uint256 x) internal pure returns (uint32) { if (x >= 1 << 32) _revertOverflow(); return uint32(x); } function toUint40(uint256 x) internal pure returns (uint40) { if (x >= 1 << 40) _revertOverflow(); return uint40(x); } function toUint48(uint256 x) internal pure returns (uint48) { if (x >= 1 << 48) _revertOverflow(); return uint48(x); } function toUint56(uint256 x) internal pure returns (uint56) { if (x >= 1 << 56) _revertOverflow(); return uint56(x); } function toUint64(uint256 x) internal pure returns (uint64) { if (x >= 1 << 64) _revertOverflow(); return uint64(x); } function toUint72(uint256 x) internal pure returns (uint72) { if (x >= 1 << 72) _revertOverflow(); return uint72(x); } function toUint80(uint256 x) internal pure returns (uint80) { if (x >= 1 << 80) _revertOverflow(); return uint80(x); } function toUint88(uint256 x) internal pure returns (uint88) { if (x >= 1 << 88) _revertOverflow(); return uint88(x); } function toUint96(uint256 x) internal pure returns (uint96) { if (x >= 1 << 96) _revertOverflow(); return uint96(x); } function toUint104(uint256 x) internal pure returns (uint104) { if (x >= 1 << 104) _revertOverflow(); return uint104(x); } function toUint112(uint256 x) internal pure returns (uint112) { if (x >= 1 << 112) _revertOverflow(); return uint112(x); } function toUint120(uint256 x) internal pure returns (uint120) { if (x >= 1 << 120) _revertOverflow(); return uint120(x); } function toUint128(uint256 x) internal pure returns (uint128) { if (x >= 1 << 128) _revertOverflow(); return uint128(x); } function toUint136(uint256 x) internal pure returns (uint136) { if (x >= 1 << 136) _revertOverflow(); return uint136(x); } function toUint144(uint256 x) internal pure returns (uint144) { if (x >= 1 << 144) _revertOverflow(); return uint144(x); } function toUint152(uint256 x) internal pure returns (uint152) { if (x >= 1 << 152) _revertOverflow(); return uint152(x); } function toUint160(uint256 x) internal pure returns (uint160) { if (x >= 1 << 160) _revertOverflow(); return uint160(x); } function toUint168(uint256 x) internal pure returns (uint168) { if (x >= 1 << 168) _revertOverflow(); return uint168(x); } function toUint176(uint256 x) internal pure returns (uint176) { if (x >= 1 << 176) _revertOverflow(); return uint176(x); } function toUint184(uint256 x) internal pure returns (uint184) { if (x >= 1 << 184) _revertOverflow(); return uint184(x); } function toUint192(uint256 x) internal pure returns (uint192) { if (x >= 1 << 192) _revertOverflow(); return uint192(x); } function toUint200(uint256 x) internal pure returns (uint200) { if (x >= 1 << 200) _revertOverflow(); return uint200(x); } function toUint208(uint256 x) internal pure returns (uint208) { if (x >= 1 << 208) _revertOverflow(); return uint208(x); } function toUint216(uint256 x) internal pure returns (uint216) { if (x >= 1 << 216) _revertOverflow(); return uint216(x); } function toUint224(uint256 x) internal pure returns (uint224) { if (x >= 1 << 224) _revertOverflow(); return uint224(x); } function toUint232(uint256 x) internal pure returns (uint232) { if (x >= 1 << 232) _revertOverflow(); return uint232(x); } function toUint240(uint256 x) internal pure returns (uint240) { if (x >= 1 << 240) _revertOverflow(); return uint240(x); } function toUint248(uint256 x) internal pure returns (uint248) { if (x >= 1 << 248) _revertOverflow(); return uint248(x); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SIGNED INTEGER SAFE CASTING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function toInt8(int256 x) internal pure returns (int8) { int8 y = int8(x); if (x != y) _revertOverflow(); return y; } function toInt16(int256 x) internal pure returns (int16) { int16 y = int16(x); if (x != y) _revertOverflow(); return y; } function toInt24(int256 x) internal pure returns (int24) { int24 y = int24(x); if (x != y) _revertOverflow(); return y; } function toInt32(int256 x) internal pure returns (int32) { int32 y = int32(x); if (x != y) _revertOverflow(); return y; } function toInt40(int256 x) internal pure returns (int40) { int40 y = int40(x); if (x != y) _revertOverflow(); return y; } function toInt48(int256 x) internal pure returns (int48) { int48 y = int48(x); if (x != y) _revertOverflow(); return y; } function toInt56(int256 x) internal pure returns (int56) { int56 y = int56(x); if (x != y) _revertOverflow(); return y; } function toInt64(int256 x) internal pure returns (int64) { int64 y = int64(x); if (x != y) _revertOverflow(); return y; } function toInt72(int256 x) internal pure returns (int72) { int72 y = int72(x); if (x != y) _revertOverflow(); return y; } function toInt80(int256 x) internal pure returns (int80) { int80 y = int80(x); if (x != y) _revertOverflow(); return y; } function toInt88(int256 x) internal pure returns (int88) { int88 y = int88(x); if (x != y) _revertOverflow(); return y; } function toInt96(int256 x) internal pure returns (int96) { int96 y = int96(x); if (x != y) _revertOverflow(); return y; } function toInt104(int256 x) internal pure returns (int104) { int104 y = int104(x); if (x != y) _revertOverflow(); return y; } function toInt112(int256 x) internal pure returns (int112) { int112 y = int112(x); if (x != y) _revertOverflow(); return y; } function toInt120(int256 x) internal pure returns (int120) { int120 y = int120(x); if (x != y) _revertOverflow(); return y; } function toInt128(int256 x) internal pure returns (int128) { int128 y = int128(x); if (x != y) _revertOverflow(); return y; } function toInt136(int256 x) internal pure returns (int136) { int136 y = int136(x); if (x != y) _revertOverflow(); return y; } function toInt144(int256 x) internal pure returns (int144) { int144 y = int144(x); if (x != y) _revertOverflow(); return y; } function toInt152(int256 x) internal pure returns (int152) { int152 y = int152(x); if (x != y) _revertOverflow(); return y; } function toInt160(int256 x) internal pure returns (int160) { int160 y = int160(x); if (x != y) _revertOverflow(); return y; } function toInt168(int256 x) internal pure returns (int168) { int168 y = int168(x); if (x != y) _revertOverflow(); return y; } function toInt176(int256 x) internal pure returns (int176) { int176 y = int176(x); if (x != y) _revertOverflow(); return y; } function toInt184(int256 x) internal pure returns (int184) { int184 y = int184(x); if (x != y) _revertOverflow(); return y; } function toInt192(int256 x) internal pure returns (int192) { int192 y = int192(x); if (x != y) _revertOverflow(); return y; } function toInt200(int256 x) internal pure returns (int200) { int200 y = int200(x); if (x != y) _revertOverflow(); return y; } function toInt208(int256 x) internal pure returns (int208) { int208 y = int208(x); if (x != y) _revertOverflow(); return y; } function toInt216(int256 x) internal pure returns (int216) { int216 y = int216(x); if (x != y) _revertOverflow(); return y; } function toInt224(int256 x) internal pure returns (int224) { int224 y = int224(x); if (x != y) _revertOverflow(); return y; } function toInt232(int256 x) internal pure returns (int232) { int232 y = int232(x); if (x != y) _revertOverflow(); return y; } function toInt240(int256 x) internal pure returns (int240) { int240 y = int240(x); if (x != y) _revertOverflow(); return y; } function toInt248(int256 x) internal pure returns (int248) { int248 y = int248(x); if (x != y) _revertOverflow(); return y; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OTHER SAFE CASTING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function toInt256(uint256 x) internal pure returns (int256) { if (x >= 1 << 255) _revertOverflow(); return int256(x); } function toUint256(int256 x) internal pure returns (uint256) { if (x < 0) _revertOverflow(); return uint256(x); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function _revertOverflow() private pure { /// @solidity memory-safe-assembly assembly { // Store the function selector of `Overflow()`. mstore(0x00, 0x35278d12) // Revert with (offset, size). revert(0x1c, 0x04) } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { BalancerV2Data } from "../AugustusV6Types.sol"; /// @title IBalancerV2SwapExactAmountOut /// @notice Interface for executing swapExactAmountOut directly on Balancer V2 pools interface IBalancerV2SwapExactAmountOut is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT //////////////////////////////////////////////////////////////*/ /// @notice Executes a swapExactAmountOut on Balancer V2 pools /// @param balancerData Struct containing data for the swap /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit Permit data for the swap /// @param data The calldata to execute /// @return spentAmount The actual amount of tokens used to swap /// @return receivedAmount The amount of tokens received /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountOutOnBalancerV2( BalancerV2Data calldata balancerData, uint256 partnerAndFee, bytes calldata permit, bytes calldata data ) external payable returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { UniswapV2Data } from "../AugustusV6Types.sol"; /// @title IUniswapV2SwapExactAmountOut /// @notice Interface for direct swapExactAmountOut on Uniswap V2 interface IUniswapV2SwapExactAmountOut is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @notice Executes a swapExactAmountOut on Uniswap V2 pools /// @param swapData struct containing data for the swap /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit The permit data /// @return spentAmount The actual amount of tokens used to swap /// @return receivedAmount The amount of tokens received /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountOutOnUniswapV2( UniswapV2Data calldata swapData, uint256 partnerAndFee, bytes calldata permit ) external payable returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { UniswapV3Data } from "../AugustusV6Types.sol"; /// @title IUniswapV3SwapExactAmountOut /// @notice Interface for executing direct swapExactAmountOut on Uniswap V3 interface IUniswapV3SwapExactAmountOut is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT //////////////////////////////////////////////////////////////*/ /// @notice Executes a swapExactAmountOut on Uniswap V3 pools /// @param swapData struct containing data for the swap /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit The permit data /// @return spentAmount The actual amount of tokens used to swap /// @return receivedAmount The amount of tokens received /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountOutOnUniswapV3( UniswapV3Data calldata swapData, uint256 partnerAndFee, bytes calldata permit ) external payable returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; /// @title IAugustusFeeVault /// @notice Interface for the AugustusFeeVault contract interface IAugustusFeeVault { /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ /// @notice Error emitted when withdraw amount is zero or exceeds the stored amount error InvalidWithdrawAmount(); /// @notice Error emmitted when caller is not an approved augustus contract error UnauthorizedCaller(); /// @notice Error emitted when an invalid parameter length is passed error InvalidParameterLength(); /// @notice Error emitted when batch withdraw fails error BatchCollectFailed(); /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ /// @notice Emitted when an augustus contract approval status is set /// @param augustus The augustus contract address /// @param approved The approval status event AugustusApprovalSet(address indexed augustus, bool approved); /*////////////////////////////////////////////////////////////// STRUCTS //////////////////////////////////////////////////////////////*/ /// @notice Struct to register fees /// @param addresses The addresses to register fees for /// @param token The token to register fees for /// @param fees The fees to register struct FeeRegistration { address[] addresses; IERC20 token; uint256[] fees; } /*////////////////////////////////////////////////////////////// COLLECT //////////////////////////////////////////////////////////////*/ /// @notice Allows partners to withdraw fees allocated to them and stored in the vault /// @param token The token to withdraw fees in /// @param amount The amount of fees to withdraw /// @param recipient The address to send the fees to /// @return success Whether the transfer was successful or not function withdrawSomeERC20(IERC20 token, uint256 amount, address recipient) external returns (bool success); /// @notice Allows partners to withdraw all fees allocated to them and stored in the vault for a given token /// @param token The token to withdraw fees in /// @param recipient The address to send the fees to /// @return success Whether the transfer was successful or not function withdrawAllERC20(IERC20 token, address recipient) external returns (bool success); /// @notice Allows partners to withdraw all fees allocated to them and stored in the vault for multiple tokens /// @param tokens The tokens to withdraw fees i /// @param recipient The address to send the fees to /// @return success Whether the transfer was successful or not function batchWithdrawAllERC20(IERC20[] calldata tokens, address recipient) external returns (bool success); /// @notice Allows partners to withdraw fees allocated to them and stored in the vault /// @param tokens The tokens to withdraw fees in /// @param amounts The amounts of fees to withdraw /// @param recipient The address to send the fees to /// @return success Whether the transfer was successful or not function batchWithdrawSomeERC20( IERC20[] calldata tokens, uint256[] calldata amounts, address recipient ) external returns (bool success); /*////////////////////////////////////////////////////////////// BALANCE GETTERS //////////////////////////////////////////////////////////////*/ /// @notice Get the balance of a given token for a given partner /// @param token The token to get the balance of /// @param partner The partner to get the balance for /// @return feeBalance The balance of the given token for the given partner function getBalance(IERC20 token, address partner) external view returns (uint256 feeBalance); /// @notice Get the balances of a given partner for multiple tokens /// @param tokens The tokens to get the balances of /// @param partner The partner to get the balances for /// @return feeBalances The balances of the given tokens for the given partner function batchGetBalance( IERC20[] calldata tokens, address partner ) external view returns (uint256[] memory feeBalances); /// @notice Returns the unallocated fees for a given token /// @param token The token to get the unallocated fees for /// @return unallocatedFees The unallocated fees for the given token function getUnallocatedFees(IERC20 token) external view returns (uint256 unallocatedFees); /*////////////////////////////////////////////////////////////// OWNER //////////////////////////////////////////////////////////////*/ /// @notice Registers the given feeData to the vault /// @param feeData The fee registration data function registerFees(FeeRegistration memory feeData) external; /// @notice Sets the augustus contract approval status /// @param augustus The augustus contract address /// @param approved The approval status function setAugustusApproval(address augustus, bool approved) external; /// @notice Sets the contract pause state /// @param _isPaused The new pause state function setContractPauseState(bool _isPaused) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// @title IAugustusFees /// @notice Interface for the AugustusFees contract, which handles the fees for the Augustus aggregator interface IAugustusFees { /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ /// @notice Error emmited when the balance is not enough to pay the fees error InsufficientBalanceToPayFees(); /// @notice Error emmited when the quotedAmount is bigger than the fromAmount error InvalidQuotedAmount(); /*////////////////////////////////////////////////////////////// PUBLIC //////////////////////////////////////////////////////////////*/ /// @notice Parses the `partnerAndFee` parameter to extract the partner address and fee data. /// @dev `partnerAndFee` is a uint256 value where data is packed in a specific bit layout. /// /// The bit layout for `partnerAndFee` is as follows: /// - The most significant 160 bits (positions 255 to 96) represent the partner address. /// - Bits 95 to 92 are reserved for flags indicating various fee processing conditions: /// - 95th bit: `IS_TAKE_SURPLUS_MASK` - Partner takes surplus /// - 94th bit: `IS_REFERRAL_MASK` - Referral takes surplus /// - 93rd bit: `IS_SKIP_BLACKLIST_MASK` - Bypass token blacklist when processing fees /// - 92nd bit: `IS_CAP_SURPLUS_MASK` - Cap surplus to 1% of quoted amount /// - The least significant 16 bits (positions 15 to 0) encode the fee percentage. /// /// @param partnerAndFee Packed uint256 containing both partner address and fee data. /// @return partner The extracted partner address as a payable address. /// @return feeData The extracted fee data containing the fee percentage and flags. function parsePartnerAndFeeData(uint256 partnerAndFee) external pure returns (address payable partner, uint256 feeData); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; // @title AugustusStorage // @notice Inherited storage layout for AugustusV6, // contracts should inherit this contract to access the storage layout contract AugustusStorage { /*////////////////////////////////////////////////////////////// FEES //////////////////////////////////////////////////////////////*/ // @dev Mapping of tokens to boolean indicating if token is blacklisted for fee collection mapping(IERC20 token => bool isBlacklisted) public blacklistedTokens; // @dev Fee wallet to directly transfer paraswap share to address payable public feeWallet; // @dev Fee wallet address to register the paraswap share to in the fee vault address payable public feeWalletDelegate; /*////////////////////////////////////////////////////////////// CONTROL //////////////////////////////////////////////////////////////*/ // @dev Contract paused state bool public paused; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Contracts import { AugustusFees } from "../fees/AugustusFees.sol"; // Utils import { Permit2Utils } from "./Permit2Utils.sol"; import { PauseUtils } from "./PauseUtils.sol"; /// @title GenericUtils /// @notice A contract containing common utilities for Generic swaps abstract contract GenericUtils is AugustusFees, Permit2Utils, PauseUtils { /*////////////////////////////////////////////////////////////// INTERNAL //////////////////////////////////////////////////////////////*/ /// @dev Call executor with executorData and amountIn function _callSwapExactAmountInExecutor( address executor, bytes calldata executorData, uint256 amountIn ) internal { // solhint-disable-next-line no-inline-assembly assembly { // get the length of the executorData // + 4 bytes for the selector // + 32 bytes for fromAmount // + 32 bytes for sender let totalLength := add(executorData.length, 68) calldatacopy(add(0x7c, 4), executorData.offset, executorData.length) // store the executorData mstore(add(0x7c, add(4, executorData.length)), amountIn) // store the amountIn mstore(add(0x7c, add(36, executorData.length)), caller()) // store the sender // call executor and forward call value if iszero(call(gas(), executor, callvalue(), 0x7c, totalLength, 0, 0)) { returndatacopy(0x7c, 0, returndatasize()) // copy the revert data to memory revert(0x7c, returndatasize()) // revert with the revert data } } } /// @dev Call executor with executorData, maxAmountIn, amountOut function _callSwapExactAmountOutExecutor( address executor, bytes calldata executorData, uint256 maxAmountIn, uint256 amountOut ) internal { // solhint-disable-next-line no-inline-assembly assembly { // get the length of the executorData // + 4 bytes for the selector // + 32 bytes for fromAmount // + 32 bytes for toAmount // + 32 bytes for sender let totalLength := add(executorData.length, 100) calldatacopy(add(0x7c, 4), executorData.offset, executorData.length) // store the executorData mstore(add(0x7c, add(4, executorData.length)), maxAmountIn) // store the maxAmountIn mstore(add(0x7c, add(36, executorData.length)), amountOut) // store the amountOut mstore(add(0x7c, add(68, executorData.length)), caller()) // store the sender // call executor and forward call value if iszero(call(gas(), executor, callvalue(), 0x7c, totalLength, 0, 0)) { returndatacopy(0x7c, 0, returndatasize()) // copy the revert data to memory revert(0x7c, returndatasize()) // revert with the revert data } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { GenericData } from "../AugustusV6Types.sol"; /// @title IGenericSwapExactAmountIn /// @notice Interface for executing a generic swapExactAmountIn through an Augustus executor interface IGenericSwapExactAmountIn is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT IN //////////////////////////////////////////////////////////////*/ /// @notice Executes a generic swapExactAmountIn using the given executorData on the given executor /// @param executor The address of the executor contract to use /// @param swapData Generic data containing the swap information /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit The permit data /// @param executorData The data to execute on the executor /// @return receivedAmount The amount of destToken received after fees /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountIn( address executor, GenericData calldata swapData, uint256 partnerAndFee, bytes calldata permit, bytes calldata executorData ) external payable returns (uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { GenericData } from "../AugustusV6Types.sol"; /// @title IGenericSwapExactAmountOut /// @notice Interface for executing a generic swapExactAmountOut through an Augustus executor interface IGenericSwapExactAmountOut is IErrors { /*////////////////////////////////////////////////////////////// SWAP EXACT AMOUNT OUT //////////////////////////////////////////////////////////////*/ /// @notice Executes a generic swapExactAmountOut using the given executorData on the given executor /// @param executor The address of the executor contract to use /// @param swapData Generic data containing the swap information /// @param partnerAndFee packed partner address and fee percentage, the first 12 bytes is the feeData and the last /// 20 bytes is the partner address /// @param permit The permit data /// @param executorData The data to execute on the executor /// @return spentAmount The actual amount of tokens used to swap /// @return receivedAmount The amount of tokens received from the swap /// @return paraswapShare The share of the fees for Paraswap /// @return partnerShare The share of the fees for the partner function swapExactAmountOut( address executor, GenericData calldata swapData, uint256 partnerAndFee, bytes calldata permit, bytes calldata executorData ) external payable returns (uint256 spentAmount, uint256 receivedAmount, uint256 paraswapShare, uint256 partnerShare); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; // Interfaces import { IErrors } from "./IErrors.sol"; // Types import { AugustusRFQData, OrderInfo } from "../AugustusV6Types.sol"; /// @title IAugustusRFQRouter /// @notice Interface for direct swaps on AugustusRFQ interface IAugustusRFQRouter is IErrors { /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ /// @notice Emitted when the passed msg.value is not equal to the fromAmount error IncorrectEthAmount(); /*////////////////////////////////////////////////////////////// TRY BATCH FILL //////////////////////////////////////////////////////////////*/ /// @notice Executes a tryBatchFillTakerAmount or tryBatchFillMakerAmount call on AugustusRFQ /// the function that is executed is defined by the direction flag in the data param /// @param data Struct containing common data for AugustusRFQ /// @param orders An array containing AugustusRFQ orderInfo data /// @param permit Permit data for the swap /// @return spentAmount The amount of tokens spent /// @return receivedAmount The amount of tokens received function swapOnAugustusRFQTryBatchFill( AugustusRFQData calldata data, OrderInfo[] calldata orders, bytes calldata permit ) external payable returns (uint256 spentAmount, uint256 receivedAmount); }
// SPDX-License-Identifier: ISC pragma solidity 0.8.22; pragma abicoder v2; // Types import { Order, OrderInfo } from "../AugustusV6Types.sol"; interface IAugustusRFQ { /// @dev Allows taker to fill an order /// @param order Order quote to fill /// @param signature Signature of the maker corresponding to the order function fillOrder(Order calldata order, bytes calldata signature) external; /// @dev The same as fillOrder but allows sender to specify the target beneficiary address /// @param order Order quote to fill /// @param signature Signature of the maker corresponding to the order /// @param target Address of the receiver function fillOrderWithTarget(Order calldata order, bytes calldata signature, address target) external; /// @dev Allows taker to fill an order partially /// @param order Order quote to fill /// @param signature Signature of the maker corresponding to the order /// @param takerTokenFillAmount Maximum taker token to fill this order with. function partialFillOrder( Order calldata order, bytes calldata signature, uint256 takerTokenFillAmount ) external returns (uint256 makerTokenFilledAmount); /// @dev Same as `partialFillOrder` but it allows to specify the destination address /// @param order Order quote to fill /// @param signature Signature of the maker corresponding to the order /// @param takerTokenFillAmount Maximum taker token to fill this order with. /// @param target Address that will receive swap funds function partialFillOrderWithTarget( Order calldata order, bytes calldata signature, uint256 takerTokenFillAmount, address target ) external returns (uint256 makerTokenFilledAmount); /// @dev Same as `partialFillOrderWithTarget` but it allows to pass permit /// @param order Order quote to fill /// @param signature Signature of the maker corresponding to the order /// @param takerTokenFillAmount Maximum taker token to fill this order with. /// @param target Address that will receive swap funds /// @param permitTakerAsset Permit calldata for taker /// @param permitMakerAsset Permit calldata for maker function partialFillOrderWithTargetPermit( Order calldata order, bytes calldata signature, uint256 takerTokenFillAmount, address target, bytes calldata permitTakerAsset, bytes calldata permitMakerAsset ) external returns (uint256 makerTokenFilledAmount); /// @dev batch fills orders until the takerFillAmount is swapped /// @dev skip the order if it fails /// @param orderInfos OrderInfo to fill /// @param takerFillAmount total taker amount to fill /// @param target Address of receiver function tryBatchFillOrderTakerAmount( OrderInfo[] calldata orderInfos, uint256 takerFillAmount, address target ) external; /// @dev batch fills orders until the makerFillAmount is swapped /// @dev skip the order if it fails /// @param orderInfos OrderInfo to fill /// @param makerFillAmount total maker amount to fill /// @param target Address of receiver function tryBatchFillOrderMakerAmount( OrderInfo[] calldata orderInfos, uint256 makerFillAmount, address target ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// @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: MIT pragma solidity 0.8.22; import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; /// @title IWETH /// @notice An interface for WETH IERC20 interface IWETH is IERC20 { function deposit() external payable; function withdraw(uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// @title IErrors /// @notice Common interface for errors interface IErrors { /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ /// @notice Emitted when the returned amount is less than the minimum amount error InsufficientReturnAmount(); /// @notice Emitted when the specified toAmount is less than the minimum amount (2) error InvalidToAmount(); /// @notice Emmited when the srcToken and destToken are the same error ArbitrageNotSupported(); }
{ "remappings": [ "@prb/test/=lib/prb-test/src/", "forge-std/=lib/forge-std/src/", "@openzeppelin/=lib/openzeppelin-contracts/contracts/", "@solady/=lib/solady/src/", "@create3/=lib/create3-factory/src/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "create3-factory/=lib/create3-factory/", "ds-test/=lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "prb-test/=lib/prb-test/src/", "solady/=lib/solady/", "solidity-bytes-utils/=lib/solidity-bytes-utils/contracts/", "solmate/=lib/create3-factory/lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "none", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_diamondCutFacet","type":"address"},{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address payable","name":"_balancerVault","type":"address"},{"internalType":"uint256","name":"_uniV3FactoryAndFF","type":"uint256"},{"internalType":"uint256","name":"_uniswapV3PoolInitCodeHash","type":"uint256"},{"internalType":"uint256","name":"_uniswapV2FactoryAndFF","type":"uint256"},{"internalType":"uint256","name":"_uniswapV2PoolInitCodeHash","type":"uint256"},{"internalType":"address","name":"_rfq","type":"address"},{"internalType":"address payable","name":"_feeVault","type":"address"},{"internalType":"address","name":"_permit2","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ArbitrageNotSupported","type":"error"},{"inputs":[],"name":"CallbackTransferFailed","type":"error"},{"inputs":[],"name":"ContractPaused","type":"error"},{"inputs":[],"name":"DiamondFunctionDoesNotExist","type":"error"},{"inputs":[],"name":"IncorrectEthAmount","type":"error"},{"inputs":[{"internalType":"address","name":"_initializationContractAddress","type":"address"},{"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"InitializationFunctionReverted","type":"error"},{"inputs":[],"name":"InsufficientBalanceToPayFees","type":"error"},{"inputs":[],"name":"InsufficientReturnAmount","type":"error"},{"inputs":[],"name":"InvalidCaller","type":"error"},{"inputs":[],"name":"InvalidOrdersLength","type":"error"},{"inputs":[],"name":"InvalidQuotedAmount","type":"error"},{"inputs":[],"name":"InvalidSelector","type":"error"},{"inputs":[],"name":"InvalidToAmount","type":"error"},{"inputs":[],"name":"Permit2Failed","type":"error"},{"inputs":[],"name":"UnauthorizedUser","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"facetAddress","type":"address"},{"internalType":"enum IDiamondCut.FacetCutAction","name":"action","type":"uint8"},{"internalType":"bytes4[]","name":"functionSelectors","type":"bytes4[]"}],"indexed":false,"internalType":"struct IDiamondCut.FacetCut[]","name":"_diamondCut","type":"tuple[]"},{"indexed":false,"internalType":"address","name":"_init","type":"address"},{"indexed":false,"internalType":"bytes","name":"_calldata","type":"bytes"}],"name":"DiamondCut","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"AUGUSTUS_RFQ","outputs":[{"internalType":"contract IAugustusRFQ","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BALANCER_VAULT","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_VAULT","outputs":[{"internalType":"contract IAugustusFeeVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINIMUM_SURPLUS_EPSILON_AND_ONE_WEI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARASWAP_REFERRAL_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARASWAP_SLIPPAGE_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARASWAP_SURPLUS_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARTNER_REFERRAL_SHARE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PARTNER_SHARE_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SURPLUS_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V2_FACTORY_AND_FF","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V2_POOL_INIT_CODE_HASH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V3_FACTORY_AND_FF","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V3_POOL_INIT_CODE_HASH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"blacklistedTokens","outputs":[{"internalType":"bool","name":"isBlacklisted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeWallet","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeWalletDelegate","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"partnerAndFee","type":"uint256"}],"name":"parsePartnerAndFeeData","outputs":[{"internalType":"address payable","name":"partner","type":"address"},{"internalType":"uint256","name":"feeData","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct GenericData","name":"swapData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"executorData","type":"bytes"}],"name":"swapExactAmountIn","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"uint256","name":"beneficiaryAndApproveFlag","type":"uint256"}],"internalType":"struct BalancerV2Data","name":"balancerData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapExactAmountInOnBalancerV2","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"curveData","type":"uint256"},{"internalType":"uint256","name":"curveAssets","type":"uint256"},{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct CurveV1Data","name":"curveV1Data","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountInOnCurveV1","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"curveData","type":"uint256"},{"internalType":"uint256","name":"i","type":"uint256"},{"internalType":"uint256","name":"j","type":"uint256"},{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct CurveV2Data","name":"curveV2Data","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountInOnCurveV2","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"pools","type":"bytes"}],"internalType":"struct UniswapV2Data","name":"uniData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountInOnUniswapV2","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"pools","type":"bytes"}],"internalType":"struct UniswapV3Data","name":"uniData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountInOnUniswapV3","outputs":[{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"executor","type":"address"},{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct GenericData","name":"swapData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"executorData","type":"bytes"}],"name":"swapExactAmountOut","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"uint256","name":"beneficiaryAndApproveFlag","type":"uint256"}],"internalType":"struct BalancerV2Data","name":"balancerData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapExactAmountOutOnBalancerV2","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"pools","type":"bytes"}],"internalType":"struct UniswapV2Data","name":"uniData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountOutOnUniswapV2","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"srcToken","type":"address"},{"internalType":"contract IERC20","name":"destToken","type":"address"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"},{"internalType":"bytes","name":"pools","type":"bytes"}],"internalType":"struct UniswapV3Data","name":"uniData","type":"tuple"},{"internalType":"uint256","name":"partnerAndFee","type":"uint256"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapExactAmountOutOnUniswapV3","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint256","name":"paraswapShare","type":"uint256"},{"internalType":"uint256","name":"partnerShare","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint256","name":"toAmount","type":"uint256"},{"internalType":"uint8","name":"wrapApproveDirection","type":"uint8"},{"internalType":"bytes32","name":"metadata","type":"bytes32"},{"internalType":"address payable","name":"beneficiary","type":"address"}],"internalType":"struct AugustusRFQData","name":"data","type":"tuple"},{"components":[{"components":[{"internalType":"uint256","name":"nonceAndMeta","type":"uint256"},{"internalType":"uint128","name":"expiry","type":"uint128"},{"internalType":"address","name":"makerAsset","type":"address"},{"internalType":"address","name":"takerAsset","type":"address"},{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"taker","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmount","type":"uint256"}],"internalType":"struct Order","name":"order","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"takerTokenFillAmount","type":"uint256"},{"internalType":"bytes","name":"permitTakerAsset","type":"bytes"},{"internalType":"bytes","name":"permitMakerAsset","type":"bytes"}],"internalType":"struct OrderInfo[]","name":"orders","type":"tuple[]"},{"internalType":"bytes","name":"permit","type":"bytes"}],"name":"swapOnAugustusRFQTryBatchFill","outputs":[{"internalType":"uint256","name":"spentAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
34620009f057620070e3388190036101a0601f8201601f19168101906001600160401b03821190821017620009b2576101609282916040526101a03912620009f0576200004e6101a062000a34565b6200005b6101c062000a34565b90620000696101e062000a34565b906200007761020062000a34565b610220516102405161026051610280519294919390916200009a6102a062000a34565b96620000a86102c062000a34565b620000b56102e062000a34565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c132080546001600160a01b039586166001600160a01b03198216811790925591949091167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a36200012662000a14565b9960018b525f5b60208110620009c657506200014162000a14565b60018152602036818301376307e4c70760e21b6200015f8262000a49565b526200016a620009f4565b6001600160a01b0390921682525f602083015260408201526200018d8b62000a49565b52620001998a62000a49565b50604051986001600160401b0360208b01908111908b1117620009b25794898989898e94602085016040525f85525f995b86518b1015620006c2576020620001e28c8962000a6b565b5101516003811015620006ae57806200038957506001600160a01b036200020a8c8962000a6b565b5151169960406200021c8d8a62000a6b565b510151996200022e8b51151562000ac0565b6200023b8c151562000b21565b6001600160a01b038c165f9081525f80516020620070c383398151915260205260409020546001600160601b0316998a1562000378575b5f9a5b8c518c10156200034e576001600160e01b0319620002948d8f62000a6b565b51165f8181525f805160206200708383398151915260205260409020546001600160a01b0316620002e357818f620002d490600194620002da9462000fda565b62000b83565b9b019a62000275565b60405162461bcd60e51b815260206004820152603560248201527f4c69624469616d6f6e644375743a2043616e2774206164642066756e6374696f60448201527f6e207468617420616c72656164792065786973747300000000000000000000006064820152608490fd5b5094995094995094995094996001909b91969b5b0199949a95909a989398979297969196620001ca565b620003838d62000eff565b62000272565b6001819b939597999b9a929496989a145f146200053b5750918a97959391620003d39c9a99979593604060018060a01b03620003c68c8c62000a6b565b5151169e8f9b8b62000a6b565b5101519a620003e58c51151562000ac0565b620003f28b151562000b21565b6001600160a01b038b165f9081525f80516020620070c383398151915260205260409020546001600160601b03169a8b1562000529575b505f9a5b8c518c101562000511578f908d6200044e8e63ffffffff60e01b9262000a6b565b51165f8181525f805160206200708383398151915260205260409020546001600160a01b03169190838314620004a657600193828262000497620002d4946200049d9762000bf7565b62000fda565b9b019a6200042d565b60405162461bcd60e51b815260206004820152603860248201527f4c69624469616d6f6e644375743a2043616e2774207265706c6163652066756e60448201527f6374696f6e20776974682073616d652066756e6374696f6e00000000000000006064820152608490fd5b50949950949950949991969b50949960019062000362565b620005349062000eff565b8e62000429565b60020362000659576001600160a01b03620005578c8b62000a6b565b515116996040620005698d8c62000a6b565b5101519a6200057b8c51151562000ac0565b620005ee575f5b8b51811015620005d85780620005d18d620005a860019463ffffffff60e01b9262000a6b565b5116805f525f8051602062007083833981519152602052838060a01b0360405f20541662000bf7565b0162000582565b509295989b9194979a60019194979a5062000362565b60405162461bcd60e51b815260206004820152603660248201527f4c69624469616d6f6e644375743a2052656d6f7665206661636574206164647260448201527f657373206d7573742062652061646472657373283029000000000000000000006064820152608490fd5b60405162461bcd60e51b815260206004820152602760248201527f4c69624469616d6f6e644375743a20496e636f727265637420466163657443756044820152663a20b1ba34b7b760c91b6064820152608490fd5b634e487b7160e01b5f52602160045260245ffd5b8989898e898b60405191606083016060845282518091526080840190602060808260051b8701019401915f905b828210620009185750505050916200073881927f8faa70878671ccd212d20771b795c50af8fd3ff6cf27f4bde57e5d4de0aeb673945f6020850152838203604085015262000a80565b0390a16001600160a01b0390811660805290811660a0521660c05260e05261010052610140526101205261018091825261016052604051615ff2918262001091833960805182818161063701528181612eb30152818161302c015281816131100152818161332e01526133c0015260a051828181610f1701528181615bef0152615e76015260c05182818161038d015281816104ec01528181610d2701528181610e1401528181611046015281816110ef01528181611215015281816114ac01528181611a8301528181611b8a01528181611e0301528181611ed5015281816125fb0152818161274a015281816128e301528181612f2d01528181613188015281816132540152818161344301528181613a9b01526155e3015260e05182818161093c015281816121c40152818161376801526158b8015261010051828181611572015281816117180152818161185d01526152d101526101205182818161150501528181614ece01528181615786015261581c0152610140518281816108cf01528181614ead0152818161574d01526157e901526101605182818161143f015281816120bc01528181613f8c01528181614ab70152614b3c01525181818161083e0152818161207f01528181613f5401528181614a780152614b040152f35b868603607f19018152835180516001600160a01b031687526020810151949693949293919260608301916003821015620006ae57604060809160209384870152015193606060408201528451809452019201905f905b8082106200098e57505050602080600192970192019201909291620006ef565b82516001600160e01b0319168452602093840193909201916001909101906200096e565b634e487b7160e01b5f52604160045260245ffd5b808c60208093620009d6620009f4565b925f84525f8385015260606040850152010152016200012d565b5f80fd5b60405190606082016001600160401b03811183821017620009b257604052565b60408051919082016001600160401b03811183821017620009b257604052565b51906001600160a01b0382168203620009f057565b80511562000a575760200190565b634e487b7160e01b5f52603260045260245ffd5b805182101562000a575760209160051b010190565b91908251928382525f5b84811062000aab575050825f602080949584010152601f8019910116010190565b60208183018101518483018201520162000a8a565b1562000ac857565b60405162461bcd60e51b815260206004820152602b60248201527f4c69624469616d6f6e644375743a204e6f2073656c6563746f727320696e206660448201526a1858d95d081d1bc818dd5d60aa1b6064820152608490fd5b1562000b2957565b60405162461bcd60e51b815260206004820152602c60248201527f4c69624469616d6f6e644375743a204164642066616365742063616e2774206260448201526b65206164647265737328302960a01b6064820152608490fd5b6001600160601b0390811690811462000b9c5760010190565b634e487b7160e01b5f52601160045260245ffd5b919091805483101562000a57575f52601c60205f208360031c019260021b1690565b5f80516020620070a3833981519152805482101562000a57575f5260205f2001905f90565b6001600160a01b0390811691821562000e945730831462000e385763ffffffff60e01b809116805f525f805160206200708383398151915293602090858252604093845f205460a01c96825f525f80516020620070c383398151915294858552865f2054925f19998a850194851162000b9c57889187898888850362000da6575b9450505050505f52858552865f208054801562000d3c578a019062000c9e828262000bb0565b63ffffffff82549160031b1b19169055555f5283525f858120551562000cc7575b505050505050565b5f80516020620070a383398151915294855487810190811162000b9c57825f52848452816001875f2001549180830362000d50575b505050855495861562000d3c575f97600197019162000d1b8362000bd2565b909182549160031b1b1916905555855252822001555f808080808062000cbf565b634e487b7160e01b5f52603160045260245ffd5b62000d5b9062000bd2565b90549060031b1c1662000d918162000d738462000bd2565b90919060018060a01b038084549260031b9316831b921b1916179055565b5f528484526001865f2001555f818162000cfc565b62000de18562000e2c9762000dfe94845f5280875262000dc98d835f2062000bb0565b90549060031b1c60e01b9687955f52525f2062000bb0565b90919063ffffffff83549160031b9260e01c831b921b1916179055565b165f90815284885289902080546001600160a01b031660a09290921b6001600160a01b031916919091179055565b865f8087898862000c78565b60405162461bcd60e51b815260206004820152602e60248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f766520696d6d7560448201526d3a30b1363290333ab731ba34b7b760911b6064820152608490fd5b60405162461bcd60e51b815260206004820152603760248201527f4c69624469616d6f6e644375743a2043616e27742072656d6f76652066756e6360448201527f74696f6e207468617420646f65736e27742065786973740000000000000000006064820152608490fd5b62000f09620009f4565b602481527f4c69624469616d6f6e644375743a204e657720666163657420686173206e6f20602082015263636f646560e01b6040820152813b1562000fb057505f80516020620070a383398151915280546001600160a01b0383165f9081525f80516020620070c383398151915260205260409020600101819055919068010000000000000000831015620009b2578262000d7391600162000fae9501905562000bd2565b565b60405162461bcd60e51b81526020600482015290819062000fd690602483019062000a80565b0390fd5b6001600160e01b031981165f8181525f80516020620070838339815191526020819052604090912080546001600160a01b031660a09590951b6001600160a01b0319169490941790935590926001600160a01b03165f8181525f80516020620070c383398151915260205260409020805491949068010000000000000000831015620009b2578262000de1916001620010769501815562000bb0565b5f5260205260405f209060018060a01b031982541617905556fe6080604052600436101561001d575b366135435761001b61353a565b005b5f3560e01c80631a01c5321461023757806342f3b24114610232578063523819fb1461022d57806355c0bff0146102285780635c8b5f44146102235780635c975abb1461021e5780635e94e28d1461021957806366c31b841461021457806367d817401461020f5780636a6f511a1461020a5780636afdd850146102055780637f45767514610200578063838bf44d146101fb578063876a02f6146101f65780638940192a146101f157806390a0c0ea146101ce5780639facd044146101ec578063a76f4eb6146101e7578063aad8a491146101e2578063ad5c4648146101dd578063b613cc8a146101d8578063bc163846146101d3578063c8e416dd146101ce578063d6ed22e6146101c9578063d85ca173146101c4578063da35bb0d146101bf578063e37ed256146101ba578063e3ead59e146101b5578063e65dc2f2146101b0578063e8bb3b6c146101ab578063ed386afa146101a6578063f25f4b56146101a1578063fa461e331461019c5763fe12941f0361000e5761236c565b612015565b611fc4565b611f8b565b611d3d565b611cec565b611be5565b611958565b6118b2565b611787565b61163a565b610f3b565b611528565b6114d0565b611462565b61140a565b610faf565b610f75565b610ecd565b610c20565b610be6565b610a15565b6108f2565b61089a565b610861565b610809565b610796565b6106e4565b610679565b6105ed565b6105b4565b61057a565b61026e565b9181601f8401121561026a5782359167ffffffffffffffff831161026a576020838186019501011161026a57565b5f80fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601610160811261026a576101201361026a576101443567ffffffffffffffff811161026a576102c46004913690830161023c565b9060ff60025460a01c16610547576102da6123be565b926102e36123cd565b906084359360a435946102f46123fb565b94833590871561051e5773ffffffffffffffffffffffffffffffffffffffff9889881615610516575b916002949391610385938b8316809260018560a01c169060038660a11c169861034688866135f5565b6104db57876101018210156104c95750806104b8575b505061036a8630338661386a565b505b6104a8575b50505b8460036024359360a31c169161390c565b0361049857847f0000000000000000000000000000000000000000000000000000000000000000166103bf6103ba3083613acb565b612442565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af180156104935761047a575b5047935b84106104525761044e61043160c435866101243586888b16613bb9565b604080519384526020840192909252908201529081906060820190565b0390f35b6040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761048d926124ae565b80610570565b5f610410565b6124e3565b6104a23083613acb565b93610414565b6104b191613660565b5f81610371565b6104c291856137b8565b5f8061035c565b90916104d692309161371f565b61036c565b50509150501561037457610511818c7f000000000000000000000000000000000000000000000000000000000000000016613660565b610374565b33975061031d565b846040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b826040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b5f91031261026a57565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516121348152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160648152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b73ffffffffffffffffffffffffffffffffffffffff81160361026a57565b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5773ffffffffffffffffffffffffffffffffffffffff6004356106c98161065b565b165f525f602052602060ff60405f2054166040519015158152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060ff60025460a01c166040519015158152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9160608383011261026a5760043567ffffffffffffffff9384821161026a5761010090828503011261026a57600401926024359260443591821161026a576107929160040161023c565b9091565b61079f36610727565b9060ff60029493945460a01c166107df5761044e936107bd9361256b565b6040805194855260208501939093529183015260608201529081906080820190565b60046040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160c88152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b359061096b8261065b565b565b906101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261026a576004356109a58161065b565b9160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82011261026a57602491610104359167ffffffffffffffff916101243583811161026a57826109fa9160040161023c565b939093926101443591821161026a576107929160040161023c565b610a1e3661096d565b909192959360ff60025460a01c166107df57610a3c602087016123f1565b610a45876123f1565b604088013593606089013595610a5d60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c86168d861614610bbc578c8b1615610bb4575b8815610b8a57610a983086613acb565b99610aa389876135f5565b610b6f579282610ada9592858b80966101018f99105f14610b5f57505080610b4e575b5050610ad48482338a61386a565b50614865565b610b02610ae73084613acb565b96610af23084613acb565b906001811115610b465790612474565b948610610b1c5761044e9860806107bd99013597166144dd565b60046040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b505f90612474565b610b5891896137b8565b5f80610ac6565b9091610b6a9361371f565b614865565b505097610ada928892610b8489933490612474565b9a614865565b60046040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b339a50610a88565b60046040517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516127108152f35b610c2936610727565b60ff60025460a01c166107df57610c3f846123f1565b92610c4c602086016123f1565b60409485870135606088013594610c6560c08a016123f1565b96610c7360e08b018b6124ee565b8895919515610ea45773ffffffffffffffffffffffffffffffffffffffff95868b1615610e9c575b3392610ca787826135f5565b610e1157610cc896610cc3916101018810610df1575b50613ef9565b61489b565b938410610dc85773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81831614610d20575b90610d0494939291608061044e9801359416614b83565b9251918252602082015260408101919091529081906060820190565b93929190847f00000000000000000000000000000000000000000000000000000000000000001694853b1561026a575f875180977f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381610d8c8a600483019190602083019252565b03925af180156104935761044e98610d0497608092610db5575b50985050909192939450610ced565b80610487610dc2926124ae565b5f610da6565b600486517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b878781610e00575b5050610cbd565b610e09926137b8565b5f8787610df9565b507f000000000000000000000000000000000000000000000000000000000000000087169250823b1561026a575f869360048e51809981937fd0e30db00000000000000000000000000000000000000000000000000000000083525af195861561049357610cc896610e89575b50610cc33093613ef9565b80610487610e96926124ae565b5f610e7e565b339a50610c9b565b60048b517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516113888152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516109c48152f35b610fb836610727565b9260ff60025460a01c166107df57610fcf816123f1565b93610fdc602083016123f1565b936040948584013592606085013597610ff760c087016123f1565b9861100560e08801886124ee565b9190938115610ea45773ffffffffffffffffffffffffffffffffffffffff93848d1615611402575b61103789826135f5565b1580159b906113aa57505050827f000000000000000000000000000000000000000000000000000000000000000016936110713086613acb565b94803b1561026a578b517fd0e30db00000000000000000000000000000000000000000000000000000000081525f816004818d865af1801561049357611397575b50905b848716938583169185831461136e57906110d0918486614ea7565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93840361135d57847f000000000000000000000000000000000000000000000000000000000000000016809114611334576111203082613acb565b90838211611324575b803b1561026a578c517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611311575b5047995b6111833083613acb565b928b106112e857156112bc57506111a6904794600181115f14610b465790612474565b60018111611206575b50916111e29795939160806111d761044e9c999795934790600181115f14610b465790612474565b965b013597166144dd565b93519283526020830191909152604082015260608101919091529081906080820190565b99969492909795939161123b817f0000000000000000000000000000000000000000000000000000000000000000169b612442565b9a803b1561026a578a517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481019c909c525f908c90602490829084905af19a8b15610493576111d76111e29a60809261044e9e6112a9575b50939597999c505050919395976111af565b806104876112b6926124ae565b5f611297565b91509160806112e26111e29a98969461044e9d9a9896600181115f14610b465790612474565b966111d9565b60048c517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761131e926124ae565b5f611175565b9061132e90612a14565b90611129565b60048c517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b506113683087613acb565b99611179565b60048e517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b806104876113a4926124ae565b5f6110b2565b6113b8969192963084613acb565b968a6101018210156113f05750806113df575b50506113d98930338561386a565b506110b5565b6113e991846137b8565b5f806113cb565b90916113fd92309161371f565b6110b5565b339c5061102d565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261026a57600490565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8101610100811261026a5760a01361026a5760049160a4359167ffffffffffffffff9160c43583811161026a57826116209160040161023c565b9390939260e43591821161026a576107929160040161023c565b611643366115c5565b949360ff60025460a01c166107df5784359060208601359261166981608089013561518d565b819b9294919973ffffffffffffffffffffffffffffffffffffffff9c8d80881690871614610bbc578915610b8a578d161561177f575b6116a93086613acb565b996116b489876135f5565b611766576116eb949392919089610101821015611754575080611743575b50506116e08830338861386a565b505b611712576152c2565b6116f8610ae73084613acb565b948610610b1c5761044e9860406107bd99013597166144dd565b61173e8c7f00000000000000000000000000000000000000000000000000000000000000001685613660565b6152c2565b61174d91876137b8565b5f806116d2565b909161176192309161371f565b6116e2565b50505096906117796116eb923490612474565b976152c2565b339a5061169f565b611790366115c5565b90949360ff60025460a01c166107df578435926020860135926117b788608089013561518d565b909a9291948b988815610b8a5773ffffffffffffffffffffffffffffffffffffffff809d16156118aa575b611804969798999a6117f482866135f5565b15611828575b50505050506152c2565b61180e3082613acb565b928310610b1c5761044e9560406104319601359416613bb9565b61010183101561189a578261184693611889575b505030338561386a565b505b611856575b808080806117fa565b611883908a7f00000000000000000000000000000000000000000000000000000000000000001690613660565b5f61184d565b61189391866137b8565b5f8061183c565b6118a592309161371f565b611848565b3399506117e2565b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576118e536611596565b67ffffffffffffffff60a43581811161026a573660238201121561026a57806004013582811161026a573660248260051b8401011161026a5760c43592831161026a576119469361193c602494369060040161023c565b9490930190612a3f565b60408051928352602083019190915290f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36016101a0811261026a576101601361026a576101843567ffffffffffffffff811161026a576119ae6004913690830161023c565b60ff60025460a01c16610547576119c36123d9565b926119cc6123e5565b9060c4359260e435946119dd612408565b946119e66123cd565b9184358815611bbc5773ffffffffffffffffffffffffffffffffffffffff998a891615611bb4575b918160029695938c611a7b969416809360018460a01c169060038560a11c1699611a3888866135f5565b611b795787610101821015611b67575080611b56575b5050611a5c8630338661386a565b505b611b46575b50505b604435918660036024359360a31c1691615431565b03611b3657847f000000000000000000000000000000000000000000000000000000000000000016611ab06103ba3083613acb565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af1801561049357611b23575b5047935b84106104525761044e61043161010435866101643586888b16613bb9565b80610487611b30926124ae565b5f611b01565b611b403083613acb565b93611b05565b611b4f91613660565b5f82611a63565b611b6091856137b8565b5f80611a4e565b9091611b7492309161371f565b611a5e565b505091505015611a6657611baf828d7f000000000000000000000000000000000000000000000000000000000000000016613660565b611a66565b339850611a0e565b856040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b611bee3661096d565b959260ff60029593955460a01c166107df57611c0c602087016123f1565b91611c16876123f1565b97604088013591606089013596611c2f60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c8b1615611ce4575b8915610b8a578281611c7698611c668980956135f5565b15611c9a575b5050505050615616565b611c803082613acb565b928310610b1c5761044e9560806104319601359416613bb9565b610101851015611cd45784611cb795611cc3575b5050339061386a565b505b5f84828280611c6c565b611ccd91836137b8565b5f80611cae565b611cdf94915061371f565b611cb9565b339a50611c4f565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b611d4636610727565b9160ff60025460a01c166107df57611d5d846123f1565b93611d6a602082016123f1565b906040948582013590606083013595611d8560c085016123f1565b98611d9360e08601866124ee565b9033928a15611f625773ffffffffffffffffffffffffffffffffffffffff9796959493929190888e1615611f5a575b611dcc87826135f5565b611ed257611de3966101018710611ec1575b61563c565b82821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03611eb157817f000000000000000000000000000000000000000000000000000000000000000016611e306103ba3083613acb565b90803b1561026a5787517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611e9e575b5047945b8510610dc8579161044e9693916080610d04969401359416613bb9565b80610487611eab926124ae565b5f611e7d565b611ebb3084613acb565b94611e81565b8615611dde57611dde8787846137b8565b507f00000000000000000000000000000000000000000000000000000000000000008816959250853b1561026a578b51957fd0e30db00000000000000000000000000000000000000000000000000000000087525f8760048187855af196871561049357611de397611f47575b50309361563c565b80610487611f54926124ae565b5f611f3f565b339d50611dc2565b60048c517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576020604051600b8152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461026a5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576004602435813560443567ffffffffffffffff811161026a5761206a903690850161023c565b91909260ff60025460a01c16612343576040517f00000000000000000000000000000000000000000000000000000000000000008152610200841460158201605f6041880160168501376060812090527f000000000000000000000000000000000000000000000000000000000000000060358301526055822080925273ffffffffffffffffffffffffffffffffffffffff809216331861231c5760a0851180612314575b15612148575061001b9550505f821315612138575061212d9061253f565b915b60a43592613f31565b612142915061253f565b9161212f565b935093905f915f93604051965f8213612302575b50505f82136122f2575b505060a435923084146001146122a8571561221257507f30f28b7a0000000000000000000000000000000000000000000000000000000083525f928392610164926101606101248885013733608484015260a483015260c4820152827f00000000000000000000000000000000000000000000000000000000000000005af1156121ec57005b7f6b836e6b000000000000000000000000000000000000000000000000000000005f525ffd5b92805f926020947f23b872dd000000000000000000000000000000000000000000000000000000006064945287830152336024830152604482015282855af19081612286575b501561226057005b7f1bbb4abe000000000000000000000000000000000000000000000000000000005f525ffd5b90503d156122a0575060015f5114601f3d11165b5f612258565b3b151561229a565b509260209250805f927fa9059cbb00000000000000000000000000000000000000000000000000000000604493523387830152602482015282855af1908161228657501561226057005b9092506060915001355f80612166565b90945060408201351692505f8061215c565b50801561210f565b867f48f5c3ed000000000000000000000000000000000000000000000000000000005f525ffd5b846040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760406004356bffffffffffffffffffffffff8251918060601c8352166020820152f35b6044356123ca8161065b565b90565b6064356123ca8161065b565b6084356123ca8161065b565b60a4356123ca8161065b565b356123ca8161065b565b610104356123ca8161065b565b610144356123ca8161065b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161246f57565b612415565b9190820391821161246f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff81116124c257604052565b612481565b6060810190811067ffffffffffffffff8211176124c257604052565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561026a570180359067ffffffffffffffff821161026a5760200191813603831361026a57565b7f8000000000000000000000000000000000000000000000000000000000000000811461246f575f0390565b91612575836123f1565b91612582602085016123f1565b9061258f60c086016123f1565b9161259d60e08701876124ee565b97606088013515610b8a5773ffffffffffffffffffffffffffffffffffffffff851615612a0c575b336125d460408a0135896135f5565b1515975f97895f146129a0575050505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016946126263087613acb565b98863b1561026a5760405f8a60048351809481937fd0e30db000000000000000000000000000000000000000000000000000000000835201358c5af180156104935761298d575b503096925b73ffffffffffffffffffffffffffffffffffffffff851673ffffffffffffffffffffffffffffffffffffffff851614610bbc5787826126c5926126c06126bb60608f0135613ef9565b61253f565b613f31565b8099919360608c01358210610b1c5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee998a73ffffffffffffffffffffffffffffffffffffffff8916146128cc575b73ffffffffffffffffffffffffffffffffffffffff1630036128755750501561284257505050479361277573ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001698610af2308b613acb565b94600186116127b4575b506127ac97505b73ffffffffffffffffffffffffffffffffffffffff6040608089013598013594166144dd565b929391929091565b90946127bf90612442565b97803b1561026a576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101999099525f908990602490829084905af1908115610493576127ac986128289261282f575b5047906001811115610b465790612474565b935f61277f565b8061048761283c926124ae565b5f612816565b6127ac9992965060601015612866575061286090610af23087613acb565b93612786565b61286091506040880135612474565b9399509750506127ac99506060106128af575b5073ffffffffffffffffffffffffffffffffffffffff604060808901359801359416614144565b6128c59196506128bf3388613acb565b90612474565b945f612888565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168073ffffffffffffffffffffffffffffffffffffffff891614610bbc57803b1561026a575f60405180927f2e1a7d4d0000000000000000000000000000000000000000000000000000000082528183816129658a600483019190602083019252565b03925af180156104935761297a575b50612708565b80610487612987926124ae565b5f612974565b8061048761299a926124ae565b5f61266d565b6129b29b9298939b9491943086613acb565b9b6101018110156129f157806129e0575b505060608211156126725791506129da3384613acb565b91612672565b6129ea91866137b8565b5f806129c3565b90612a0592995060408c013591309161371f565b3096612672565b3394506125c5565b801561246f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b9392919060ff60025460a01c166107df5761079294612db2565b3560ff8116810361026a5790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9015612acd578035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b612a67565b9190811015612acd5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561026a57016020813591019167ffffffffffffffff821161026a57813603831361026a57565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b929493919060609180606086016060875252608090608086019260808260051b8801019481945f925b848410612bfc575050505050505060409173ffffffffffffffffffffffffffffffffffffffff9195602085015216910152565b909192939495967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a820301835287357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818336030181121561026a5782016101808135835260209182810135906fffffffffffffffffffffffffffffffff821680920361026a5784612d82612d5b8695612d188f612cf18e612da1998b60019e0152612cca6040612cae818c01610960565b73ffffffffffffffffffffffffffffffffffffffff16908a0152565b612cd5818a01610960565b73ffffffffffffffffffffffffffffffffffffffff1690880152565b612cfc818801610960565b73ffffffffffffffffffffffffffffffffffffffff1690860152565b612d2860a0612cfc818801610960565b60c0808601359085015260e080860135908501526101009080612d4d83880188612b12565b929093870152850191612b62565b6101208085013590840152610140612d7581860186612b12565b9185840390860152612b62565b91612d936101609182810190612b12565b929091818503910152612b62565b990193019401929195949390612bc9565b9493929192612dc3608087016123f1565b91863594602088013594612dda6040809a01612a59565b9060019473ffffffffffffffffffffffffffffffffffffffff9586881615613532575b8815611f62578415613509575f5b8581106134ed575050612e45612e2c6060612e268789612a94565b016123f1565b73ffffffffffffffffffffffffffffffffffffffff1690565b90612e57612e2c8d612e26888a612a94565b936003811693600185149283613437573461340e5760019291908d6101018210156133fc5750806133eb575b5050612e918c30338761386a565b505b818160021c166133ba575b60031c166130fb5750506002036130165750827f00000000000000000000000000000000000000000000000000000000000000001691823b1561026a57612f1a92875f80948c51968795869485937f1c64b820000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613003575b50807f00000000000000000000000000000000000000000000000000000000000000001692612f583085613acb565b908110612fda57612f6890612a14565b95833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101879052925f908490602490829084905af191821561049357612fc2938793612fc7575b5016615350565b509190565b80610487612fd4926124ae565b5f612fbb565b600487517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b80610487613010926124ae565b5f612f29565b9190838599969916916130298385613acb565b947f000000000000000000000000000000000000000000000000000000000000000016803b1561026a575f92838a936130908b519a8b96879586947f1c64b82000000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af1918215610493576130b3946130ae936130e8575b50613acb565b612474565b9384106130bf57509190565b600490517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b806104876130f5926124ae565b5f6130a8565b9793925099979593906002145f1461332757847f000000000000000000000000000000000000000000000000000000000000000016803b1561026a57613175935f80948b51968795869485937f01fb36ba000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613314575b50817f000000000000000000000000000000000000000000000000000000000000000016906131bb6131b63084613acb565b612a14565b91803b1561026a575f875180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161320089600483019190602083019252565b03925af180156104935761321d9284928692612fc7575016615350565b50955b61322a3082613acb565b9360018511613245575b5050506132419250612474565b9190565b939493156132f3575061327a907f00000000000000000000000000000000000000000000000000000000000000001693612a14565b91833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052925f908490602490829084905af192831561049357613241936132e0575b506132d68233615350565b505b5f8080613234565b806104876132ed926124ae565b5f6132cb565b92505061330e61330561324194612a14565b8093339061538b565b506132d8565b80610487613321926124ae565b5f613184565b84999392997f000000000000000000000000000000000000000000000000000000000000000016803b1561026a575f92838c936133928c51978896879586947f01fb36ba00000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af18015610493576133a7575b50613220565b806104876133b4926124ae565b5f6133a1565b6133e6897f00000000000000000000000000000000000000000000000000000000000000001685613660565b612e9e565b6133f591866137b8565b5f80612e83565b909161340992309161371f565b612e93565b60048f517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b5050348b036134c457877f00000000000000000000000000000000000000000000000000000000000000001690813b1561026a575f8c928f60049051809581937fd0e30db00000000000000000000000000000000000000000000000000000000083525af1918215610493576001926134b1575b50612e93565b806104876134be926124ae565b5f6134ab565b60048d517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b806135036134fd8493898b612ad2565b35615302565b01612e0b565b60048c517f91b3fafa000000000000000000000000000000000000000000000000000000008152fd5b339750612dfd565b333b1561026a57565b7fffffffff000000000000000000000000000000000000000000000000000000005f35165f527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff60405f20541680156135cb575f8091368280378136915af43d5f803e156135c7573d5ff35b3d5ffd5b60046040517f7a2ee929000000000000000000000000000000000000000000000000000000008152fd5b91905f9273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9182821461364e575b501861361f57565b3461362657565b7f8b6ebb4d000000000000000000000000000000000000000000000000000000005f5260045ffd5b9093503418613626576001925f613617565b906014527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90816034526f095ea7b300000000000000000000000090815f5260205f6044601082855af13d1560015f51141716156136c2575b5050505f603452565b60105f60449260209582958360345283528238868683865af1506034525af13d1560015f51141716156136f7575f80806136b9565b7f8164f842000000000000000000000000000000000000000000000000000000005f5260045ffd5b906004905f94859482604051957f30f28b7a00000000000000000000000000000000000000000000000000000000875285870137608485015260a48401523360c48401520190827f00000000000000000000000000000000000000000000000000000000000000005af11561379057565b7f6b836e6b000000000000000000000000000000000000000000000000000000005f5260045ffd5b918060e01461382e57610100146137f1577fb78cb0dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101045f9182602094610100604051937f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af150565b5060e45f918260209460e0604051937fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af150565b93929091604051927f23b872dd00000000000000000000000000000000000000000000000000000000845260048401526024830152604482015260205f60648382875af192836138e7575b5082156138bf5750565b807f7939f4240000000000000000000000000000000000000000000000000000000060049252fd5b9092503d15613903575060015f5114601f3d1116915b5f6138b5565b3b1515916138fd565b919293906040519360018214613a57575b915f95608494928796946001146139fb5760031460011461399b577f3df02124000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b3d5f803e3d5ffd5b6fffffffffffffffffffffffffffffffff907f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015234905af161096b573d5f803e3d5ffd5b507fa6417ed6000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b929093917fd0e30db00000000000000000000000000000000000000000000000000000000083525f806004853473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af115613993579193909261391d565b5f9291600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831414613b2f576020906024604051809481937f70a0823100000000000000000000000000000000000000000000000000000000835260048301525afa613b2a575b50565b519150565b31925050565b90600b820180921161246f57565b9190820180921161246f57565b908160640291606483040361246f57565b906113889182810292818404149015171561246f57565b906109c49182810292818404149015171561246f57565b906121349182810292818404149015171561246f57565b8181029291811591840414171561246f57565b9091949392613bda5f96906bffffffffffffffffffffffff8260601c921690565b9490613be582613b35565b8311613eb3575b613bf68884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216613cb5575b505086159050613c9b57613c4e613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b5460ff1690565b613c9b57508482613c6785613c8f94613c959796615968565b613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b9061538b565b50612442565b91905f90565b9250613cae93945082906103ba92615968565b905f905f90565b6b2000000000000000000000008199949395979699161594851594613cfa613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680613eac575b613e93576b0800000000000000000000008316151597613d2084615995565b938415613da1575050948a94613d8c94613d6e613d66613d949b9686613d849f9e9c979b613d51816103ba9e613b43565b871115613d9957613d6191613b43565b613ba6565b612710900490565b90613d7b613d6683613b8f565b9c8d8093612474565b9d8e93615ad2565b928391615968565b929190565b505084613ba6565b80929c99969794506b400000000000000000000000919a989593501615155f14613e0f575089613dd857505050505b5f8080613c13565b8698975091613d949693918a6103ba96613d8c95613e07613dff613d669f613d6690613b61565b9e8f94613b78565b9c8d92615ad2565b939291906b8000000000000000000000008516613e31575b5050505050613dd0565b8a15613e27579092948a92949998506b040000000000000000000000613e5f613d66613e689d9a999a613b61565b9b8c8095612474565b9a16613e87575b926103ba95928a889693613d949a99613d8c97615ad2565b5f995060019550613e6f565b505050509390506103ba9250839150613cae9495615968565b5086613d01565b9650613ebf8183612474565b966b100000000000000000000000861615613bec5796613ee1613d6683613b50565b9081811115613ef257505b96613bec565b9050613eec565b7f8000000000000000000000000000000000000000000000000000000000000000811015613f245790565b6335278d125f526004601cfd5b939192908094845f92606082066140ee575b50505f6040949596855197889586947f0000000000000000000000000000000000000000000000000000000000000000865260158601605f6001860160168901376060812090527f0000000000000000000000000000000000000000000000000000000000000000603587015273ffffffffffffffffffffffffffffffffffffffff6055872016968030916140e6575b50843560ff1c861461405d577f128acb0800000000000000000000000000000000000000000000000000000000875260048701526001602487015260448601526401000276a4606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca60208301519251925b5f03615d3d565b7f128acb080000000000000000000000000000000000000000000000000000000087526004870152846024870152604486015273fffd8963efd1fc6a506488495d951d5263988d25606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca6020835193015192614056565b90505f613fd3565b60a0821115613f435760a0810197507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6090910195503573ffffffffffffffffffffffffffffffffffffffff1691505f6040613f43565b919093979594979692965f9761417561415d8884612474565b91906bffffffffffffffffffffffff8260601c921690565b909286116144b3578a9561418889613b35565b811161446d575b73ffffffffffffffffffffffffffffffffffffffff8416614249575b50505050916141bc9187949361538b565b50816141d4575b506141cd91613b43565b9291905f90565b9050614200613c478273ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b61423f576141cd91614238858093614230612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b90339061386a565b50916141c3565b509291505f908190565b6b200000000000000000000000829b93949b9a99959796989a161591821591614292613c478d73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9380614466575b614448576b08000000000000000000000082161515946142b883615995565b92831561431d575050906142d6918088105f14613d61575086613ba6565b6127109004996142e58b613b8f565b61271090046142f581809d612474565b9c8d923361430298615d47565b9061430c91613b43565b926143169261538b565b5093929190565b909d9b9c979b979a9899979695949392508d91506b4000000000000000000000008116156143995750614363575050505050916141bc918794935b91939481935f6141ab565b909192939998949695979a61437781613b61565b61271090049b6143878d92613b78565b61271090049b8c913361430298615d47565b946b800000000000000000000000869b9a999897929b166143c6575b505050505050906141bc9291614358565b909192939495969798996143df578c99989796956143b5565b988c929394959b9a96996b04000000000000000000000061440661440f9f613d6690613b61565b9e8f8096612474565b9d1661443c575b928c6143169a99989693614431969361443699963390615d47565b613b43565b9361538b565b5f9c5060019650614416565b5050505050509450919061445d93955061538b565b5091905f905f90565b5083614299565b9950614479888b612474565b996b10000000000000000000000082161561418f579961449b613d668c613b50565b90818111156144ac57505b9961418f565b90506144a6565b60046040517fb1c349e8000000000000000000000000000000000000000000000000000000008152fd5b979590939492968315155f1461485c576144ff6144f985612442565b87612474565b5f98606081901c906bffffffffffffffffffffffff1690928881116144b35761452783613b35565b8111614816575b73ffffffffffffffffffffffffffffffffffffffff84166145f8575b505050509061455b61456392612a14565b97889161538b565b5084156145df57614594613c478373ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b6145df576141cd92916145d7866128bf936145d1826145cb612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8661538b565b50612474565b903390615fa1565b6145f09394506128bf913390615fa1565b91905f905f90565b6b20000000000000000000000082161591821591614636613c478b73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b938061480f575b6147e7576b080000000000000000000000821615159461465c83615995565b9182614798575050506b4000000000000000000000008116156146f757508a61469557505050509061455b614563925b91925f8061454a565b8a6146a78996989c9b9a99959b613b61565b61271090049b6146b78d92613b78565b61271090049b6146c8968d92615ad2565b336146d292615fa1565b916146dc90612a14565b80976146e79261538b565b506146f191612474565b93929190565b9291936b8000000000000000000000008416614720575b50505050509061455b6145639261468c565b8b1561470e578b919293999695976b04000000000000000000000061474e613d666147579f9e9c989e613b61565b9d8e8095612474565b9b1661478c575b938a9b61477e9487946146f19c9d6145d7956145d19b9a6147849a615ad2565b94612a14565b98899161538b565b5f9a506001945061475e565b8b989a9d9c9b979e506147b593508082105f146147e05750613ba6565b6127109004996147c48b613b8f565b61271090046147d481809d612474565b9c6146c8968e93615ad2565b9050613ba6565b50505050505093926145f095965061477e6145d193614807923390615fa1565b96879161538b565b508361463d565b9950614822828b612474565b996b10000000000000000000000082161561452e5799614844613d668c613b50565b908181111561485557505b9961452e565b905061484f565b6144ff846144f9565b91805f958695606493607c95608037608083015260a08201523360c0820152019134905af11561489157565b3d5f607c3e3d607cfd5b94919392935f925f945f93604051975f975b6060860489106148ca57505050505050505050506123ca90615d3d565b909192939495969798809b9a8915614afe575b6060880460018c0110614a71575b8a614a69575b8a60a061016492896001610100821114614a5b575b506060830288013560ff1c156149be579460608086945f946040997f128acb080000000000000000000000000000000000000000000000000000000088523060048901526001602489015260448801526401000276a4606488015260a0608488015260a48701528960e487015202890161010485013760016101008b11146149b1575b5af1156139935760208a0151945b600187965f0399019796959a98999a9493929190946148ad565b8989610164850137614989565b9460608086945f946020997f128acb08000000000000000000000000000000000000000000000000000000008852306004890152866024890152604488015273fffd8963efd1fc6a506488495d951d5263988d25606488015260a0608488015260a48701528960e487015202890161010485013760016101008b1114614a4e575b5af11561399357895194614997565b8989610164850137614a3f565b809192940193019089614906565b3093506148f1565b97505096507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f60016060818c010285010160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916976148eb565b985050507f00000000000000000000000000000000000000000000000000000000000000008a5260158a01605f6001840160168d01376060812090527f000000000000000000000000000000000000000000000000000000000000000060358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916916148dd565b9091949392614ba45f96906bffffffffffffffffffffffff8260601c921690565b9490614baf82613b35565b8311614e2b575b614bc08884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216614c67575b505086159050614c5057614c11613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b614c50575081614c26848793614c499561538b565b50613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b5091905f90565b939450614c5f9250839161538b565b50905f905f90565b6b2000000000000000000000008199949395979699161594851594614cac613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680614e24575b614e0e576b0800000000000000000000008316151597614cd284615995565b938415614d2c57505093614d1699989793614d00613d66614d269995858f9a9699613d5181614d1e9c613b43565b90614d0d613d6683613b8f565b9b8c8093612474565b9c8d93615ad2565b92839161538b565b50929190565b80929c99969794506b400000000000000000000000919a989593501615155f14614d97575089614d6357505050505b5f8080614bdd565b869897509089614d26969795614d1e959493614d8f614d87613d66613d669f613b61565b9d8e94613b78565b9b8c92615ad2565b939291906b8000000000000000000000008516614db9575b5050505050614d5b565b8a15614daf579092948a92949998506b040000000000000000000000613e5f613d66614de79d9a999a613b61565b9a16614e02575b918987969492614d2698614d1e9795615ad2565b5f995060019550614dee565b5050505094959050849250614c5f93915061538b565b5086614cb3565b9650614e378183612474565b966b100000000000000000000000861615614bb65796614e59613d6683613b50565b9081811115614e6a57505b96614bb6565b9050614e64565b92918352602860158401918237602881209052603582015273ffffffffffffffffffffffffffffffffffffffff60558220169052565b929190917f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000091604051917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08260011c16935f97858501525f5b8360061c8110615169575050508060061c805b61507957505f935f965f955b8360061c8710614f5557505050505050505050565b8015615027575b308460061c600189011061500f575b60a46040925f928a6001810160051b8a01518b8b826020899560061b8d010135600116615004575b507f022c0d9f000000000000000000000000000000000000000000000000000000009082019091016020908101919091528b8d018d0160248101929092526044820192909252606481019290925260806084830152838201859052019083905af11561399357600188960195614f40565b935087925081614f93565b508486016001880160051b0160200151985088614f6b565b508385016020818101517fa9059cbb0000000000000000000000000000000000000000000000000000000092880180830193845260248101829052604490810185905290925f9190828c5af150614f5c565b8284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201600590811b820160209081015184831b8701517f0902f1ac00000000000000000000000000000000000000000000000000000000948901909201938452919950929160409160049082905afa156139935760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92602087808801010151906040888089010101519182602087870160061b8c010135851661515e575b50906126f28161271093030292020204018098838301901b8501520180614f34565b9092506126f261513c565b80615187838560019460061b8b0160208560051b8c8c010101614e71565b01614f21565b9190918235807f52bbbe2900000000000000000000000000000000000000000000000000000000146152b0577f945bcec90000000000000000000000000000000000000000000000000000000014615207577f7352d91c000000000000000000000000000000000000000000000000000000005f5260045ffd5b60448301359283810193600485013594600183600401351460011461529d5760059590951b01016004013592602401355b8015615282575b8315615266575b929173ffffffffffffffffffffffffffffffffffffffff82169160ff1c90565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9350615246565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61523f565b602401359460051b010160040135615238565b50610144830135926101240135615238565b905f80918060405194853783347f00000000000000000000000000000000000000000000000000000000000000005af1156152fa5750565b3d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff16806153205750565b331861532857565b7f02a43f8b000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8080938193612710f190811561536357565b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b929173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee841460011461541d5760446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282865af191826153f8575b505b811561536357565b9091503d15615414575060015f5114601f3d1116905b5f6153ee565b3b15159061540e565b5f809394508092918192612710f1906153f0565b929490936040519460019384821461559c575b905f988998979695949392806001146155515760021461550157506003146001146154b15790869392916084967f5b41b90800000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b60a4957f394747c5000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152806064840152608483015234905af161096b573d5f803e3d5ffd5b889594939291509660a4977f64a14558000000000000000000000000000000000000000000000000000000008852600488015260248701526044860152606485015260848401525af11561399357565b50505090869392916084967f65b2489b00000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b969594939291907fd0e30db00000000000000000000000000000000000000000000000000000000086525f806004883473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1156139935790919293949596615444565b906044835f958695607c9460803760808201523360a0820152019134905af11561489157565b9095929193955f955f945f985f965b8660061c8810615662575050505050505050505050565b89156157e3575b602090818960061b8b01013560011696604051927f0902f1ac000000000000000000000000000000000000000000000000000000008452604084600481865afa156139935783519084015190818a6157da575b506126f291612710838502910201920202049687905f906157d1575b30918a60061c60018d0110615744575b5f9360a493869386937f022c0d9f0000000000000000000000000000000000000000000000000000000060409952600486015260248501526044840152608060648401528160848401525af1156139935760018a97019661564b565b92939d509b50507f00000000000000000000000000000000000000000000000000000000000000008c5260158c0160288060018c0160061b8d018337812090527f000000000000000000000000000000000000000000000000000000000000000060358d015260558c209a73ffffffffffffffffffffffffffffffffffffffff8c169c8d919093926156e8565b50505f876156d8565b9150905f6156bc565b506040517f000000000000000000000000000000000000000000000000000000000000000081529850601589016028808a8337812090527f000000000000000000000000000000000000000000000000000000000000000060358a0152605589209873ffffffffffffffffffffffffffffffffffffffff8a1690308314600114615929576101008411156158e4577f30f28b7a0000000000000000000000000000000000000000000000000000000081525f806004928688858301378460848201528960a48201528560c482015283870190827f00000000000000000000000000000000000000000000000000000000000000005af1156121ec5750615669565b5f6064827f23b872dd000000000000000000000000000000000000000000000000000000006020945285600482015284602482015289604482015282895af150615669565b5f6044827fa9059cbb000000000000000000000000000000000000000000000000000000006020945284600482015289602482015282895af150615669565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613b2793019161538b565b613fff1660c881116159a45790565b5060c890565b6040519061096b826124c7565b604051906159c4826124c7565b600282526040366020840137565b805115612acd5760200190565b805160011015612acd5760400190565b9081518082526020808093019301915f5b828110615a0e575050505090565b835185529381019392810192600101615a00565b6020808252825160608284015280516080840181905293949360a084019392918201905f5b818110615aa8575050508473ffffffffffffffffffffffffffffffffffffffff6040926123ca96970151168284015201519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828503019101526159ef565b825173ffffffffffffffffffffffffffffffffffffffff1686529483019491830191600101615a47565b96909591939294615ae38487613b43565b9283615af457505050505050505090565b9697959681615d35575b5015615b745750505082828111615b4a576123ca9481615b21575b505050612474565b73ffffffffffffffffffffffffffffffffffffffff615b4193169061538b565b505f8080615b19565b60046040517f3ff640db000000000000000000000000000000000000000000000000000000008152fd5b84829693979211615b4a5715615bcf57856123ca96615b9c575b5081615b2157505050612474565b615bc890615bc2612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8361538b565b505f615b8e565b615cc09073ffffffffffffffffffffffffffffffffffffffff93929396877f00000000000000000000000000000000000000000000000000000000000000001694615c1b88878561538b565b50615c56615c276159b7565b99615c306159b7565b9616615c3b8b6159d2565b9073ffffffffffffffffffffffffffffffffffffffff169052565b615c5f856159d2565b52615c8e615c85612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b8a6159df565b615c97846159df565b52615ca06159aa565b96875273ffffffffffffffffffffffffffffffffffffffff166020870152565b6040850152803b1561026a57615d095f949185926040519687809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1928315610493576123ca93615d225750612474565b80610487615d2f926124ae565b5f6145d1565b90505f615afe565b5f8112613f245790565b969790929594919394615d5a8387613b43565b9889615d6d575b50505050505050505090565b819994959697989991615f99575b5015615dcf5750505082948311615b4a5782615da5575b505050505b5f8080808080808080615d61565b73ffffffffffffffffffffffffffffffffffffffff615dc594169161386a565b505f808080615d92565b9091968711615b4a5715615e515780615e1d575b5082615df3575b50505050615d97565b73ffffffffffffffffffffffffffffffffffffffff615e1394169161386a565b505f808080615dea565b615e4a90615e43612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b838561386a565b505f615de3565b91615f289193949273ffffffffffffffffffffffffffffffffffffffff95615e9e88887f00000000000000000000000000000000000000000000000000000000000000001680988661386a565b50615ebe615eaa6159b7565b97615eb36159b7565b9616615c3b896159d2565b615ec7856159d2565b52615ef6615eed612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b886159df565b615eff846159df565b52615f086159aa565b94855273ffffffffffffffffffffffffffffffffffffffff166020850152565b6040830152803b1561026a57615f715f929183926040519485809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1801561049357615f86575b50615d97565b80610487615f93926124ae565b5f615f80565b90505f615d7b565b91909160018211615fb3575050505f90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615fe1920192839161538b565b509056fea164736f6c6343000816000ac8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131cc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131ec8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131d000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a290326330000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8ff1f98431c8ad98523631ae4a59f267346ea31f9840000000000000000000000e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54ff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000096e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0600000000000000000000000000700052c0608f670705380a4900e0a8080010cc000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Deployed Bytecode
0x6080604052600436101561001d575b366135435761001b61353a565b005b5f3560e01c80631a01c5321461023757806342f3b24114610232578063523819fb1461022d57806355c0bff0146102285780635c8b5f44146102235780635c975abb1461021e5780635e94e28d1461021957806366c31b841461021457806367d817401461020f5780636a6f511a1461020a5780636afdd850146102055780637f45767514610200578063838bf44d146101fb578063876a02f6146101f65780638940192a146101f157806390a0c0ea146101ce5780639facd044146101ec578063a76f4eb6146101e7578063aad8a491146101e2578063ad5c4648146101dd578063b613cc8a146101d8578063bc163846146101d3578063c8e416dd146101ce578063d6ed22e6146101c9578063d85ca173146101c4578063da35bb0d146101bf578063e37ed256146101ba578063e3ead59e146101b5578063e65dc2f2146101b0578063e8bb3b6c146101ab578063ed386afa146101a6578063f25f4b56146101a1578063fa461e331461019c5763fe12941f0361000e5761236c565b612015565b611fc4565b611f8b565b611d3d565b611cec565b611be5565b611958565b6118b2565b611787565b61163a565b610f3b565b611528565b6114d0565b611462565b61140a565b610faf565b610f75565b610ecd565b610c20565b610be6565b610a15565b6108f2565b61089a565b610861565b610809565b610796565b6106e4565b610679565b6105ed565b6105b4565b61057a565b61026e565b9181601f8401121561026a5782359167ffffffffffffffff831161026a576020838186019501011161026a57565b5f80fd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601610160811261026a576101201361026a576101443567ffffffffffffffff811161026a576102c46004913690830161023c565b9060ff60025460a01c16610547576102da6123be565b926102e36123cd565b906084359360a435946102f46123fb565b94833590871561051e5773ffffffffffffffffffffffffffffffffffffffff9889881615610516575b916002949391610385938b8316809260018560a01c169060038660a11c169861034688866135f5565b6104db57876101018210156104c95750806104b8575b505061036a8630338661386a565b505b6104a8575b50505b8460036024359360a31c169161390c565b0361049857847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2166103bf6103ba3083613acb565b612442565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af180156104935761047a575b5047935b84106104525761044e61043160c435866101243586888b16613bb9565b604080519384526020840192909252908201529081906060820190565b0390f35b6040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761048d926124ae565b80610570565b5f610410565b6124e3565b6104a23083613acb565b93610414565b6104b191613660565b5f81610371565b6104c291856137b8565b5f8061035c565b90916104d692309161371f565b61036c565b50509150501561037457610511818c7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216613660565b610374565b33975061031d565b846040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b826040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b5f91031261026a57565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516121348152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160648152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a06168152f35b73ffffffffffffffffffffffffffffffffffffffff81160361026a57565b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5773ffffffffffffffffffffffffffffffffffffffff6004356106c98161065b565b165f525f602052602060ff60405f2054166040519015158152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060ff60025460a01c166040519015158152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9160608383011261026a5760043567ffffffffffffffff9384821161026a5761010090828503011261026a57600401926024359260443591821161026a576107929160040161023c565b9091565b61079f36610727565b9060ff60029493945460a01c166107df5761044e936107bd9361256b565b6040805194855260208501939093529183015260608201529081906080820190565b60046040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517fff1f98431c8ad98523631ae4a59f267346ea31f98400000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405160c88152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517fff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f00000000000000000000008152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3168152f35b359061096b8261065b565b565b906101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261026a576004356109a58161065b565b9160e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82011261026a57602491610104359167ffffffffffffffff916101243583811161026a57826109fa9160040161023c565b939093926101443591821161026a576107929160040161023c565b610a1e3661096d565b909192959360ff60025460a01c166107df57610a3c602087016123f1565b610a45876123f1565b604088013593606089013595610a5d60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c86168d861614610bbc578c8b1615610bb4575b8815610b8a57610a983086613acb565b99610aa389876135f5565b610b6f579282610ada9592858b80966101018f99105f14610b5f57505080610b4e575b5050610ad48482338a61386a565b50614865565b610b02610ae73084613acb565b96610af23084613acb565b906001811115610b465790612474565b948610610b1c5761044e9860806107bd99013597166144dd565b60046040517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b505f90612474565b610b5891896137b8565b5f80610ac6565b9091610b6a9361371f565b614865565b505097610ada928892610b8489933490612474565b9a614865565b60046040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b339a50610a88565b60046040517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516127108152f35b610c2936610727565b60ff60025460a01c166107df57610c3f846123f1565b92610c4c602086016123f1565b60409485870135606088013594610c6560c08a016123f1565b96610c7360e08b018b6124ee565b8895919515610ea45773ffffffffffffffffffffffffffffffffffffffff95868b1615610e9c575b3392610ca787826135f5565b610e1157610cc896610cc3916101018810610df1575b50613ef9565b61489b565b938410610dc85773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81831614610d20575b90610d0494939291608061044e9801359416614b83565b9251918252602082015260408101919091529081906060820190565b93929190847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21694853b1561026a575f875180977f2e1a7d4d000000000000000000000000000000000000000000000000000000008252818381610d8c8a600483019190602083019252565b03925af180156104935761044e98610d0497608092610db5575b50985050909192939450610ced565b80610487610dc2926124ae565b5f610da6565b600486517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b878781610e00575b5050610cbd565b610e09926137b8565b5f8787610df9565b507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc287169250823b1561026a575f869360048e51809981937fd0e30db00000000000000000000000000000000000000000000000000000000083525af195861561049357610cc896610e89575b50610cc33093613ef9565b80610487610e96926124ae565b5f610e7e565b339a50610c9b565b60048b517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516113888152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040516109c48152f35b610fb836610727565b9260ff60025460a01c166107df57610fcf816123f1565b93610fdc602083016123f1565b936040948584013592606085013597610ff760c087016123f1565b9861100560e08801886124ee565b9190938115610ea45773ffffffffffffffffffffffffffffffffffffffff93848d1615611402575b61103789826135f5565b1580159b906113aa57505050827f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216936110713086613acb565b94803b1561026a578b517fd0e30db00000000000000000000000000000000000000000000000000000000081525f816004818d865af1801561049357611397575b50905b848716938583169185831461136e57906110d0918486614ea7565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee93840361135d57847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216809114611334576111203082613acb565b90838211611324575b803b1561026a578c517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611311575b5047995b6111833083613acb565b928b106112e857156112bc57506111a6904794600181115f14610b465790612474565b60018111611206575b50916111e29795939160806111d761044e9c999795934790600181115f14610b465790612474565b965b013597166144dd565b93519283526020830191909152604082015260608101919091529081906080820190565b99969492909795939161123b817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2169b612442565b9a803b1561026a578a517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481019c909c525f908c90602490829084905af19a8b15610493576111d76111e29a60809261044e9e6112a9575b50939597999c505050919395976111af565b806104876112b6926124ae565b5f611297565b91509160806112e26111e29a98969461044e9d9a9896600181115f14610b465790612474565b966111d9565b60048c517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b8061048761131e926124ae565b5f611175565b9061132e90612a14565b90611129565b60048c517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b506113683087613acb565b99611179565b60048e517f0d56c171000000000000000000000000000000000000000000000000000000008152fd5b806104876113a4926124ae565b5f6110b2565b6113b8969192963084613acb565b968a6101018210156113f05750806113df575b50506113d98930338561386a565b506110b5565b6113e991846137b8565b5f806113cb565b90916113fd92309161371f565b6110b5565b339c5061102d565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b548152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760206040517f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f8152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8168152f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261026a57600490565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8101610100811261026a5760a01361026a5760049160a4359167ffffffffffffffff9160c43583811161026a57826116209160040161023c565b9390939260e43591821161026a576107929160040161023c565b611643366115c5565b949360ff60025460a01c166107df5784359060208601359261166981608089013561518d565b819b9294919973ffffffffffffffffffffffffffffffffffffffff9c8d80881690871614610bbc578915610b8a578d161561177f575b6116a93086613acb565b996116b489876135f5565b611766576116eb949392919089610101821015611754575080611743575b50506116e08830338861386a565b505b611712576152c2565b6116f8610ae73084613acb565b948610610b1c5761044e9860406107bd99013597166144dd565b61173e8c7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81685613660565b6152c2565b61174d91876137b8565b5f806116d2565b909161176192309161371f565b6116e2565b50505096906117796116eb923490612474565b976152c2565b339a5061169f565b611790366115c5565b90949360ff60025460a01c166107df578435926020860135926117b788608089013561518d565b909a9291948b988815610b8a5773ffffffffffffffffffffffffffffffffffffffff809d16156118aa575b611804969798999a6117f482866135f5565b15611828575b50505050506152c2565b61180e3082613acb565b928310610b1c5761044e9560406104319601359416613bb9565b61010183101561189a578261184693611889575b505030338561386a565b505b611856575b808080806117fa565b611883908a7f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c81690613660565b5f61184d565b61189391866137b8565b5f8061183c565b6118a592309161371f565b611848565b3399506117e2565b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576118e536611596565b67ffffffffffffffff60a43581811161026a573660238201121561026a57806004013582811161026a573660248260051b8401011161026a5760c43592831161026a576119469361193c602494369060040161023c565b9490930190612a3f565b60408051928352602083019190915290f35b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36016101a0811261026a576101601361026a576101843567ffffffffffffffff811161026a576119ae6004913690830161023c565b60ff60025460a01c16610547576119c36123d9565b926119cc6123e5565b9060c4359260e435946119dd612408565b946119e66123cd565b9184358815611bbc5773ffffffffffffffffffffffffffffffffffffffff998a891615611bb4575b918160029695938c611a7b969416809360018460a01c169060038560a11c1699611a3888866135f5565b611b795787610101821015611b67575080611b56575b5050611a5c8630338661386a565b505b611b46575b50505b604435918660036024359360a31c1691615431565b03611b3657847f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216611ab06103ba3083613acb565b90803b1561026a576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152838101928352915f91839182908490829060200103925af1801561049357611b23575b5047935b84106104525761044e61043161010435866101643586888b16613bb9565b80610487611b30926124ae565b5f611b01565b611b403083613acb565b93611b05565b611b4f91613660565b5f82611a63565b611b6091856137b8565b5f80611a4e565b9091611b7492309161371f565b611a5e565b505091505015611a6657611baf828d7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216613660565b611a66565b339850611a0e565b856040517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b611bee3661096d565b959260ff60029593955460a01c166107df57611c0c602087016123f1565b91611c16876123f1565b97604088013591606089013596611c2f60c08b016123f1565b9873ffffffffffffffffffffffffffffffffffffffff9b8c8b1615611ce4575b8915610b8a578281611c7698611c668980956135f5565b15611c9a575b5050505050615616565b611c803082613acb565b928310610b1c5761044e9560806104319601359416613bb9565b610101851015611cd45784611cb795611cc3575b5050339061386a565b505b5f84828280611c6c565b611ccd91836137b8565b5f80611cae565b611cdf94915061371f565b611cb9565b339a50611c4f565b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60025416604051908152f35b611d4636610727565b9160ff60025460a01c166107df57611d5d846123f1565b93611d6a602082016123f1565b906040948582013590606083013595611d8560c085016123f1565b98611d9360e08601866124ee565b9033928a15611f625773ffffffffffffffffffffffffffffffffffffffff9796959493929190888e1615611f5a575b611dcc87826135f5565b611ed257611de3966101018710611ec1575b61563c565b82821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee03611eb157817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216611e306103ba3083613acb565b90803b1561026a5787517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101929092525f908290602490829084905af1801561049357611e9e575b5047945b8510610dc8579161044e9693916080610d04969401359416613bb9565b80610487611eab926124ae565b5f611e7d565b611ebb3084613acb565b94611e81565b8615611dde57611dde8787846137b8565b507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28816959250853b1561026a578b51957fd0e30db00000000000000000000000000000000000000000000000000000000087525f8760048187855af196871561049357611de397611f47575b50309361563c565b80610487611f54926124ae565b5f611f3f565b339d50611dc2565b60048c517f8570bedf000000000000000000000000000000000000000000000000000000008152fd5b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576020604051600b8152f35b3461026a575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a57602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b3461026a5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a576004602435813560443567ffffffffffffffff811161026a5761206a903690850161023c565b91909260ff60025460a01c16612343576040517fff1f98431c8ad98523631ae4a59f267346ea31f98400000000000000000000008152610200841460158201605f6041880160168501376060812090527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460358301526055822080925273ffffffffffffffffffffffffffffffffffffffff809216331861231c5760a0851180612314575b15612148575061001b9550505f821315612138575061212d9061253f565b915b60a43592613f31565b612142915061253f565b9161212f565b935093905f915f93604051965f8213612302575b50505f82136122f2575b505060a435923084146001146122a8571561221257507f30f28b7a0000000000000000000000000000000000000000000000000000000083525f928392610164926101606101248885013733608484015260a483015260c4820152827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af1156121ec57005b7f6b836e6b000000000000000000000000000000000000000000000000000000005f525ffd5b92805f926020947f23b872dd000000000000000000000000000000000000000000000000000000006064945287830152336024830152604482015282855af19081612286575b501561226057005b7f1bbb4abe000000000000000000000000000000000000000000000000000000005f525ffd5b90503d156122a0575060015f5114601f3d11165b5f612258565b3b151561229a565b509260209250805f927fa9059cbb00000000000000000000000000000000000000000000000000000000604493523387830152602482015282855af1908161228657501561226057005b9092506060915001355f80612166565b90945060408201351692505f8061215c565b50801561210f565b867f48f5c3ed000000000000000000000000000000000000000000000000000000005f525ffd5b846040517fab35696f000000000000000000000000000000000000000000000000000000008152fd5b3461026a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261026a5760406004356bffffffffffffffffffffffff8251918060601c8352166020820152f35b6044356123ca8161065b565b90565b6064356123ca8161065b565b6084356123ca8161065b565b60a4356123ca8161065b565b356123ca8161065b565b610104356123ca8161065b565b610144356123ca8161065b565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820191821161246f57565b612415565b9190820391821161246f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff81116124c257604052565b612481565b6060810190811067ffffffffffffffff8211176124c257604052565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561026a570180359067ffffffffffffffff821161026a5760200191813603831361026a57565b7f8000000000000000000000000000000000000000000000000000000000000000811461246f575f0390565b91612575836123f1565b91612582602085016123f1565b9061258f60c086016123f1565b9161259d60e08701876124ee565b97606088013515610b8a5773ffffffffffffffffffffffffffffffffffffffff851615612a0c575b336125d460408a0135896135f5565b1515975f97895f146129a0575050505073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216946126263087613acb565b98863b1561026a5760405f8a60048351809481937fd0e30db000000000000000000000000000000000000000000000000000000000835201358c5af180156104935761298d575b503096925b73ffffffffffffffffffffffffffffffffffffffff851673ffffffffffffffffffffffffffffffffffffffff851614610bbc5787826126c5926126c06126bb60608f0135613ef9565b61253f565b613f31565b8099919360608c01358210610b1c5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee998a73ffffffffffffffffffffffffffffffffffffffff8916146128cc575b73ffffffffffffffffffffffffffffffffffffffff1630036128755750501561284257505050479361277573ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21698610af2308b613acb565b94600186116127b4575b506127ac97505b73ffffffffffffffffffffffffffffffffffffffff6040608089013598013594166144dd565b929391929091565b90946127bf90612442565b97803b1561026a576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101999099525f908990602490829084905af1908115610493576127ac986128289261282f575b5047906001811115610b465790612474565b935f61277f565b8061048761283c926124ae565b5f612816565b6127ac9992965060601015612866575061286090610af23087613acb565b93612786565b61286091506040880135612474565b9399509750506127ac99506060106128af575b5073ffffffffffffffffffffffffffffffffffffffff604060808901359801359416614144565b6128c59196506128bf3388613acb565b90612474565b945f612888565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2168073ffffffffffffffffffffffffffffffffffffffff891614610bbc57803b1561026a575f60405180927f2e1a7d4d0000000000000000000000000000000000000000000000000000000082528183816129658a600483019190602083019252565b03925af180156104935761297a575b50612708565b80610487612987926124ae565b5f612974565b8061048761299a926124ae565b5f61266d565b6129b29b9298939b9491943086613acb565b9b6101018110156129f157806129e0575b505060608211156126725791506129da3384613acb565b91612672565b6129ea91866137b8565b5f806129c3565b90612a0592995060408c013591309161371f565b3096612672565b3394506125c5565b801561246f577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b9392919060ff60025460a01c166107df5761079294612db2565b3560ff8116810361026a5790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9015612acd578035907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b612a67565b9190811015612acd5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818136030182121561026a570190565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561026a57016020813591019167ffffffffffffffff821161026a57813603831361026a57565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b929493919060609180606086016060875252608090608086019260808260051b8801019481945f925b848410612bfc575050505050505060409173ffffffffffffffffffffffffffffffffffffffff9195602085015216910152565b909192939495967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff808a820301835287357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe818336030181121561026a5782016101808135835260209182810135906fffffffffffffffffffffffffffffffff821680920361026a5784612d82612d5b8695612d188f612cf18e612da1998b60019e0152612cca6040612cae818c01610960565b73ffffffffffffffffffffffffffffffffffffffff16908a0152565b612cd5818a01610960565b73ffffffffffffffffffffffffffffffffffffffff1690880152565b612cfc818801610960565b73ffffffffffffffffffffffffffffffffffffffff1690860152565b612d2860a0612cfc818801610960565b60c0808601359085015260e080860135908501526101009080612d4d83880188612b12565b929093870152850191612b62565b6101208085013590840152610140612d7581860186612b12565b9185840390860152612b62565b91612d936101609182810190612b12565b929091818503910152612b62565b990193019401929195949390612bc9565b9493929192612dc3608087016123f1565b91863594602088013594612dda6040809a01612a59565b9060019473ffffffffffffffffffffffffffffffffffffffff9586881615613532575b8815611f62578415613509575f5b8581106134ed575050612e45612e2c6060612e268789612a94565b016123f1565b73ffffffffffffffffffffffffffffffffffffffff1690565b90612e57612e2c8d612e26888a612a94565b936003811693600185149283613437573461340e5760019291908d6101018210156133fc5750806133eb575b5050612e918c30338761386a565b505b818160021c166133ba575b60031c166130fb5750506002036130165750827f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a061691823b1561026a57612f1a92875f80948c51968795869485937f1c64b820000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613003575b50807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21692612f583085613acb565b908110612fda57612f6890612a14565b95833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101879052925f908490602490829084905af191821561049357612fc2938793612fc7575b5016615350565b509190565b80610487612fd4926124ae565b5f612fbb565b600487517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b80610487613010926124ae565b5f612f29565b9190838599969916916130298385613acb565b947f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0616803b1561026a575f92838a936130908b519a8b96879586947f1c64b82000000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af1918215610493576130b3946130ae936130e8575b50613acb565b612474565b9384106130bf57509190565b600490517fcea9e31d000000000000000000000000000000000000000000000000000000008152fd5b806104876130f5926124ae565b5f6130a8565b9793925099979593906002145f1461332757847f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0616803b1561026a57613175935f80948b51968795869485937f01fb36ba000000000000000000000000000000000000000000000000000000008552309260048601612ba0565b03925af1801561049357613314575b50817f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906131bb6131b63084613acb565b612a14565b91803b1561026a575f875180927f2e1a7d4d00000000000000000000000000000000000000000000000000000000825281838161320089600483019190602083019252565b03925af180156104935761321d9284928692612fc7575016615350565b50955b61322a3082613acb565b9360018511613245575b5050506132419250612474565b9190565b939493156132f3575061327a907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21693612a14565b91833b1561026a57517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815260048101839052925f908490602490829084905af192831561049357613241936132e0575b506132d68233615350565b505b5f8080613234565b806104876132ed926124ae565b5f6132cb565b92505061330e61330561324194612a14565b8093339061538b565b506132d8565b80610487613321926124ae565b5f613184565b84999392997f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0616803b1561026a575f92838c936133928c51978896879586947f01fb36ba00000000000000000000000000000000000000000000000000000000865260048601612ba0565b03925af18015610493576133a7575b50613220565b806104876133b4926124ae565b5f6133a1565b6133e6897f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a061685613660565b612e9e565b6133f591866137b8565b5f80612e83565b909161340992309161371f565b612e93565b60048f517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b5050348b036134c457877f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690813b1561026a575f8c928f60049051809581937fd0e30db00000000000000000000000000000000000000000000000000000000083525af1918215610493576001926134b1575b50612e93565b806104876134be926124ae565b5f6134ab565b60048d517f8b6ebb4d000000000000000000000000000000000000000000000000000000008152fd5b806135036134fd8493898b612ad2565b35615302565b01612e0b565b60048c517f91b3fafa000000000000000000000000000000000000000000000000000000008152fd5b339750612dfd565b333b1561026a57565b7fffffffff000000000000000000000000000000000000000000000000000000005f35165f527fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c60205273ffffffffffffffffffffffffffffffffffffffff60405f20541680156135cb575f8091368280378136915af43d5f803e156135c7573d5ff35b3d5ffd5b60046040517f7a2ee929000000000000000000000000000000000000000000000000000000008152fd5b91905f9273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9182821461364e575b501861361f57565b3461362657565b7f8b6ebb4d000000000000000000000000000000000000000000000000000000005f5260045ffd5b9093503418613626576001925f613617565b906014527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90816034526f095ea7b300000000000000000000000090815f5260205f6044601082855af13d1560015f51141716156136c2575b5050505f603452565b60105f60449260209582958360345283528238868683865af1506034525af13d1560015f51141716156136f7575f80806136b9565b7f8164f842000000000000000000000000000000000000000000000000000000005f5260045ffd5b906004905f94859482604051957f30f28b7a00000000000000000000000000000000000000000000000000000000875285870137608485015260a48401523360c48401520190827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af11561379057565b7f6b836e6b000000000000000000000000000000000000000000000000000000005f5260045ffd5b918060e01461382e57610100146137f1577fb78cb0dd000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101045f9182602094610100604051937f8fcbaf0c00000000000000000000000000000000000000000000000000000000855260048501375af150565b5060e45f918260209460e0604051937fd505accf00000000000000000000000000000000000000000000000000000000855260048501375af150565b93929091604051927f23b872dd00000000000000000000000000000000000000000000000000000000845260048401526024830152604482015260205f60648382875af192836138e7575b5082156138bf5750565b807f7939f4240000000000000000000000000000000000000000000000000000000060049252fd5b9092503d15613903575060015f5114601f3d1116915b5f6138b5565b3b1515916138fd565b919293906040519360018214613a57575b915f95608494928796946001146139fb5760031460011461399b577f3df02124000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b3d5f803e3d5ffd5b6fffffffffffffffffffffffffffffffff907f3df021240000000000000000000000000000000000000000000000000000000085528060801c600486015216602484015260448301526001606483015234905af161096b573d5f803e3d5ffd5b507fa6417ed6000000000000000000000000000000000000000000000000000000008452608081901c60048501526fffffffffffffffffffffffffffffffff16602484015260448301526001606483015283905af11561399357565b929093917fd0e30db00000000000000000000000000000000000000000000000000000000083525f806004853473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2165af115613993579193909261391d565b5f9291600173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831414613b2f576020906024604051809481937f70a0823100000000000000000000000000000000000000000000000000000000835260048301525afa613b2a575b50565b519150565b31925050565b90600b820180921161246f57565b9190820180921161246f57565b908160640291606483040361246f57565b906113889182810292818404149015171561246f57565b906109c49182810292818404149015171561246f57565b906121349182810292818404149015171561246f57565b8181029291811591840414171561246f57565b9091949392613bda5f96906bffffffffffffffffffffffff8260601c921690565b9490613be582613b35565b8311613eb3575b613bf68884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216613cb5575b505086159050613c9b57613c4e613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b5460ff1690565b613c9b57508482613c6785613c8f94613c959796615968565b613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b9061538b565b50612442565b91905f90565b9250613cae93945082906103ba92615968565b905f905f90565b6b2000000000000000000000008199949395979699161594851594613cfa613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680613eac575b613e93576b0800000000000000000000008316151597613d2084615995565b938415613da1575050948a94613d8c94613d6e613d66613d949b9686613d849f9e9c979b613d51816103ba9e613b43565b871115613d9957613d6191613b43565b613ba6565b612710900490565b90613d7b613d6683613b8f565b9c8d8093612474565b9d8e93615ad2565b928391615968565b929190565b505084613ba6565b80929c99969794506b400000000000000000000000919a989593501615155f14613e0f575089613dd857505050505b5f8080613c13565b8698975091613d949693918a6103ba96613d8c95613e07613dff613d669f613d6690613b61565b9e8f94613b78565b9c8d92615ad2565b939291906b8000000000000000000000008516613e31575b5050505050613dd0565b8a15613e27579092948a92949998506b040000000000000000000000613e5f613d66613e689d9a999a613b61565b9b8c8095612474565b9a16613e87575b926103ba95928a889693613d949a99613d8c97615ad2565b5f995060019550613e6f565b505050509390506103ba9250839150613cae9495615968565b5086613d01565b9650613ebf8183612474565b966b100000000000000000000000861615613bec5796613ee1613d6683613b50565b9081811115613ef257505b96613bec565b9050613eec565b7f8000000000000000000000000000000000000000000000000000000000000000811015613f245790565b6335278d125f526004601cfd5b939192908094845f92606082066140ee575b50505f6040949596855197889586947fff1f98431c8ad98523631ae4a59f267346ea31f9840000000000000000000000865260158601605f6001860160168901376060812090527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54603587015273ffffffffffffffffffffffffffffffffffffffff6055872016968030916140e6575b50843560ff1c861461405d577f128acb0800000000000000000000000000000000000000000000000000000000875260048701526001602487015260448601526401000276a4606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca60208301519251925b5f03615d3d565b7f128acb080000000000000000000000000000000000000000000000000000000087526004870152846024870152604486015273fffd8963efd1fc6a506488495d951d5263988d25606486015260a0608486015281880160a48601528560c486015260e485015280610104928386013701925af115613993576123ca6020835193015192614056565b90505f613fd3565b60a0821115613f435760a0810197507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6090910195503573ffffffffffffffffffffffffffffffffffffffff1691505f6040613f43565b919093979594979692965f9761417561415d8884612474565b91906bffffffffffffffffffffffff8260601c921690565b909286116144b3578a9561418889613b35565b811161446d575b73ffffffffffffffffffffffffffffffffffffffff8416614249575b50505050916141bc9187949361538b565b50816141d4575b506141cd91613b43565b9291905f90565b9050614200613c478273ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b61423f576141cd91614238858093614230612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b90339061386a565b50916141c3565b509291505f908190565b6b200000000000000000000000829b93949b9a99959796989a161591821591614292613c478d73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9380614466575b614448576b08000000000000000000000082161515946142b883615995565b92831561431d575050906142d6918088105f14613d61575086613ba6565b6127109004996142e58b613b8f565b61271090046142f581809d612474565b9c8d923361430298615d47565b9061430c91613b43565b926143169261538b565b5093929190565b909d9b9c979b979a9899979695949392508d91506b4000000000000000000000008116156143995750614363575050505050916141bc918794935b91939481935f6141ab565b909192939998949695979a61437781613b61565b61271090049b6143878d92613b78565b61271090049b8c913361430298615d47565b946b800000000000000000000000869b9a999897929b166143c6575b505050505050906141bc9291614358565b909192939495969798996143df578c99989796956143b5565b988c929394959b9a96996b04000000000000000000000061440661440f9f613d6690613b61565b9e8f8096612474565b9d1661443c575b928c6143169a99989693614431969361443699963390615d47565b613b43565b9361538b565b5f9c5060019650614416565b5050505050509450919061445d93955061538b565b5091905f905f90565b5083614299565b9950614479888b612474565b996b10000000000000000000000082161561418f579961449b613d668c613b50565b90818111156144ac57505b9961418f565b90506144a6565b60046040517fb1c349e8000000000000000000000000000000000000000000000000000000008152fd5b979590939492968315155f1461485c576144ff6144f985612442565b87612474565b5f98606081901c906bffffffffffffffffffffffff1690928881116144b35761452783613b35565b8111614816575b73ffffffffffffffffffffffffffffffffffffffff84166145f8575b505050509061455b61456392612a14565b97889161538b565b5084156145df57614594613c478373ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b6145df576141cd92916145d7866128bf936145d1826145cb612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8661538b565b50612474565b903390615fa1565b6145f09394506128bf913390615fa1565b91905f905f90565b6b20000000000000000000000082161591821591614636613c478b73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b938061480f575b6147e7576b080000000000000000000000821615159461465c83615995565b9182614798575050506b4000000000000000000000008116156146f757508a61469557505050509061455b614563925b91925f8061454a565b8a6146a78996989c9b9a99959b613b61565b61271090049b6146b78d92613b78565b61271090049b6146c8968d92615ad2565b336146d292615fa1565b916146dc90612a14565b80976146e79261538b565b506146f191612474565b93929190565b9291936b8000000000000000000000008416614720575b50505050509061455b6145639261468c565b8b1561470e578b919293999695976b04000000000000000000000061474e613d666147579f9e9c989e613b61565b9d8e8095612474565b9b1661478c575b938a9b61477e9487946146f19c9d6145d7956145d19b9a6147849a615ad2565b94612a14565b98899161538b565b5f9a506001945061475e565b8b989a9d9c9b979e506147b593508082105f146147e05750613ba6565b6127109004996147c48b613b8f565b61271090046147d481809d612474565b9c6146c8968e93615ad2565b9050613ba6565b50505050505093926145f095965061477e6145d193614807923390615fa1565b96879161538b565b508361463d565b9950614822828b612474565b996b10000000000000000000000082161561452e5799614844613d668c613b50565b908181111561485557505b9961452e565b905061484f565b6144ff846144f9565b91805f958695606493607c95608037608083015260a08201523360c0820152019134905af11561489157565b3d5f607c3e3d607cfd5b94919392935f925f945f93604051975f975b6060860489106148ca57505050505050505050506123ca90615d3d565b909192939495969798809b9a8915614afe575b6060880460018c0110614a71575b8a614a69575b8a60a061016492896001610100821114614a5b575b506060830288013560ff1c156149be579460608086945f946040997f128acb080000000000000000000000000000000000000000000000000000000088523060048901526001602489015260448801526401000276a4606488015260a0608488015260a48701528960e487015202890161010485013760016101008b11146149b1575b5af1156139935760208a0151945b600187965f0399019796959a98999a9493929190946148ad565b8989610164850137614989565b9460608086945f946020997f128acb08000000000000000000000000000000000000000000000000000000008852306004890152866024890152604488015273fffd8963efd1fc6a506488495d951d5263988d25606488015260a0608488015260a48701528960e487015202890161010485013760016101008b1114614a4e575b5af11561399357895194614997565b8989610164850137614a3f565b809192940193019089614906565b3093506148f1565b97505096507fff1f98431c8ad98523631ae4a59f267346ea31f98400000000000000000000008a5260158a01605f60016060818c010285010160168d01376060812090527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916976148eb565b985050507fff1f98431c8ad98523631ae4a59f267346ea31f98400000000000000000000008a5260158a01605f6001840160168d01376060812090527fe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b5460358b015260558a20968a73ffffffffffffffffffffffffffffffffffffffff8916916148dd565b9091949392614ba45f96906bffffffffffffffffffffffff8260601c921690565b9490614baf82613b35565b8311614e2b575b614bc08884612474565b9573ffffffffffffffffffffffffffffffffffffffff8216614c67575b505086159050614c5057614c11613c478473ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b614c50575081614c26848793614c499561538b565b50613c89612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b5091905f90565b939450614c5f9250839161538b565b50905f905f90565b6b2000000000000000000000008199949395979699161594851594614cac613c478c73ffffffffffffffffffffffffffffffffffffffff165f525f60205260405f2090565b9680614e24575b614e0e576b0800000000000000000000008316151597614cd284615995565b938415614d2c57505093614d1699989793614d00613d66614d269995858f9a9699613d5181614d1e9c613b43565b90614d0d613d6683613b8f565b9b8c8093612474565b9c8d93615ad2565b92839161538b565b50929190565b80929c99969794506b400000000000000000000000919a989593501615155f14614d97575089614d6357505050505b5f8080614bdd565b869897509089614d26969795614d1e959493614d8f614d87613d66613d669f613b61565b9d8e94613b78565b9b8c92615ad2565b939291906b8000000000000000000000008516614db9575b5050505050614d5b565b8a15614daf579092948a92949998506b040000000000000000000000613e5f613d66614de79d9a999a613b61565b9a16614e02575b918987969492614d2698614d1e9795615ad2565b5f995060019550614dee565b5050505094959050849250614c5f93915061538b565b5086614cb3565b9650614e378183612474565b966b100000000000000000000000861615614bb65796614e59613d6683613b50565b9081811115614e6a57505b96614bb6565b9050614e64565b92918352602860158401918237602881209052603582015273ffffffffffffffffffffffffffffffffffffffff60558220169052565b929190917fff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f00000000000000000000007f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f91604051917f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08260011c16935f97858501525f5b8360061c8110615169575050508060061c805b61507957505f935f965f955b8360061c8710614f5557505050505050505050565b8015615027575b308460061c600189011061500f575b60a46040925f928a6001810160051b8a01518b8b826020899560061b8d010135600116615004575b507f022c0d9f000000000000000000000000000000000000000000000000000000009082019091016020908101919091528b8d018d0160248101929092526044820192909252606481019290925260806084830152838201859052019083905af11561399357600188960195614f40565b935087925081614f93565b508486016001880160051b0160200151985088614f6b565b508385016020818101517fa9059cbb0000000000000000000000000000000000000000000000000000000092880180830193845260248101829052604490810185905290925f9190828c5af150614f5c565b8284017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201600590811b820160209081015184831b8701517f0902f1ac00000000000000000000000000000000000000000000000000000000948901909201938452919950929160409160049082905afa156139935760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92602087808801010151906040888089010101519182602087870160061b8c010135851661515e575b50906126f28161271093030292020204018098838301901b8501520180614f34565b9092506126f261513c565b80615187838560019460061b8b0160208560051b8c8c010101614e71565b01614f21565b9190918235807f52bbbe2900000000000000000000000000000000000000000000000000000000146152b0577f945bcec90000000000000000000000000000000000000000000000000000000014615207577f7352d91c000000000000000000000000000000000000000000000000000000005f5260045ffd5b60448301359283810193600485013594600183600401351460011461529d5760059590951b01016004013592602401355b8015615282575b8315615266575b929173ffffffffffffffffffffffffffffffffffffffff82169160ff1c90565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee9350615246565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee61523f565b602401359460051b010160040135615238565b50610144830135926101240135615238565b905f80918060405194853783347f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c85af1156152fa5750565b3d5f823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff16806153205750565b331861532857565b7f02a43f8b000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8080938193612710f190811561536357565b7f90b8ec18000000000000000000000000000000000000000000000000000000005f5260045ffd5b929173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee841460011461541d5760446020925f92604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083526004830152602482015282865af191826153f8575b505b811561536357565b9091503d15615414575060015f5114601f3d1116905b5f6153ee565b3b15159061540e565b5f809394508092918192612710f1906153f0565b929490936040519460019384821461559c575b905f988998979695949392806001146155515760021461550157506003146001146154b15790869392916084967f5b41b90800000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b60a4957f394747c5000000000000000000000000000000000000000000000000000000008652600486015260248501526044840152806064840152608483015234905af161096b573d5f803e3d5ffd5b889594939291509660a4977f64a14558000000000000000000000000000000000000000000000000000000008852600488015260248701526044860152606485015260848401525af11561399357565b50505090869392916084967f65b2489b00000000000000000000000000000000000000000000000000000000875260048701526024860152604485015260648401525af11561399357565b969594939291907fd0e30db00000000000000000000000000000000000000000000000000000000086525f806004883473ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2165af1156139935790919293949596615444565b906044835f958695607c9460803760808201523360a0820152019134905af11561489157565b9095929193955f955f945f985f965b8660061c8810615662575050505050505050505050565b89156157e3575b602090818960061b8b01013560011696604051927f0902f1ac000000000000000000000000000000000000000000000000000000008452604084600481865afa156139935783519084015190818a6157da575b506126f291612710838502910201920202049687905f906157d1575b30918a60061c60018d0110615744575b5f9360a493869386937f022c0d9f0000000000000000000000000000000000000000000000000000000060409952600486015260248501526044840152608060648401528160848401525af1156139935760018a97019661564b565b92939d509b50507fff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f00000000000000000000008c5260158c0160288060018c0160061b8d018337812090527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f60358d015260558c209a73ffffffffffffffffffffffffffffffffffffffff8c169c8d919093926156e8565b50505f876156d8565b9150905f6156bc565b506040517fff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000081529850601589016028808a8337812090527f96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f60358a0152605589209873ffffffffffffffffffffffffffffffffffffffff8a1690308314600114615929576101008411156158e4577f30f28b7a0000000000000000000000000000000000000000000000000000000081525f806004928688858301378460848201528960a48201528560c482015283870190827f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba35af1156121ec5750615669565b5f6064827f23b872dd000000000000000000000000000000000000000000000000000000006020945285600482015284602482015289604482015282895af150615669565b5f6044827fa9059cbb000000000000000000000000000000000000000000000000000000006020945284600482015289602482015282895af150615669565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613b2793019161538b565b613fff1660c881116159a45790565b5060c890565b6040519061096b826124c7565b604051906159c4826124c7565b600282526040366020840137565b805115612acd5760200190565b805160011015612acd5760400190565b9081518082526020808093019301915f5b828110615a0e575050505090565b835185529381019392810192600101615a00565b6020808252825160608284015280516080840181905293949360a084019392918201905f5b818110615aa8575050508473ffffffffffffffffffffffffffffffffffffffff6040926123ca96970151168284015201519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0828503019101526159ef565b825173ffffffffffffffffffffffffffffffffffffffff1686529483019491830191600101615a47565b96909591939294615ae38487613b43565b9283615af457505050505050505090565b9697959681615d35575b5015615b745750505082828111615b4a576123ca9481615b21575b505050612474565b73ffffffffffffffffffffffffffffffffffffffff615b4193169061538b565b505f8080615b19565b60046040517f3ff640db000000000000000000000000000000000000000000000000000000008152fd5b84829693979211615b4a5715615bcf57856123ca96615b9c575b5081615b2157505050612474565b615bc890615bc2612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b8361538b565b505f615b8e565b615cc09073ffffffffffffffffffffffffffffffffffffffff93929396877f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc1694615c1b88878561538b565b50615c56615c276159b7565b99615c306159b7565b9616615c3b8b6159d2565b9073ffffffffffffffffffffffffffffffffffffffff169052565b615c5f856159d2565b52615c8e615c85612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b8a6159df565b615c97846159df565b52615ca06159aa565b96875273ffffffffffffffffffffffffffffffffffffffff166020870152565b6040850152803b1561026a57615d095f949185926040519687809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1928315610493576123ca93615d225750612474565b80610487615d2f926124ae565b5f6145d1565b90505f615afe565b5f8112613f245790565b969790929594919394615d5a8387613b43565b9889615d6d575b50505050505050505090565b819994959697989991615f99575b5015615dcf5750505082948311615b4a5782615da5575b505050505b5f8080808080808080615d61565b73ffffffffffffffffffffffffffffffffffffffff615dc594169161386a565b505f808080615d92565b9091968711615b4a5715615e515780615e1d575b5082615df3575b50505050615d97565b73ffffffffffffffffffffffffffffffffffffffff615e1394169161386a565b505f808080615dea565b615e4a90615e43612e2c60015473ffffffffffffffffffffffffffffffffffffffff1690565b838561386a565b505f615de3565b91615f289193949273ffffffffffffffffffffffffffffffffffffffff95615e9e88887f00000000000000000000000000700052c0608f670705380a4900e0a8080010cc1680988661386a565b50615ebe615eaa6159b7565b97615eb36159b7565b9616615c3b896159d2565b615ec7856159d2565b52615ef6615eed612e2c60025473ffffffffffffffffffffffffffffffffffffffff1690565b615c3b886159df565b615eff846159df565b52615f086159aa565b94855273ffffffffffffffffffffffffffffffffffffffff166020850152565b6040830152803b1561026a57615f715f929183926040519485809481937f45f32b0b00000000000000000000000000000000000000000000000000000000835260048301615a22565b03925af1801561049357615f86575b50615d97565b80610487615f93926124ae565b5f615f80565b90505f615d7b565b91909160018211615fb3575050505f90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff615fe1920192839161538b565b509056fea164736f6c6343000816000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a290326330000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8ff1f98431c8ad98523631ae4a59f267346ea31f9840000000000000000000000e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54ff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f000000000000000000000096e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a0600000000000000000000000000700052c0608f670705380a4900e0a8080010cc000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
-----Decoded View---------------
Arg [0] : _owner (address): 0xD7e24A49944F7972cEb826C7557580658F9C3303
Arg [1] : _diamondCutFacet (address): 0xfa39c1c670b48956eeF9fd0BbD0E81A290326330
Arg [2] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : _balancerVault (address): 0xBA12222222228d8Ba445958a75a0704d566BF2C8
Arg [4] : _uniV3FactoryAndFF (uint256): 115395599522508061429428307120883023012361415493104242916976774162621891870720
Arg [5] : _uniswapV3PoolInitCodeHash (uint256): 102814774271675688723325049954498779091328469440286648861889194717372678376276
Arg [6] : _uniswapV2FactoryAndFF (uint256): 115503056148776755782671941100287934110443767449021598599426467662402456387584
Arg [7] : _uniswapV2PoolInitCodeHash (uint256): 68258024698789349894765071221733254692395760896603232917352833510768193864799
Arg [8] : _rfq (address): 0xe92b586627ccA7a83dC919cc7127196d70f55a06
Arg [9] : _feeVault (address): 0x00700052c0608F670705380a4900e0a8080010CC
Arg [10] : _permit2 (address): 0x000000000022D473030F116dDEE9F6B43aC78BA3
-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 000000000000000000000000d7e24a49944f7972ceb826c7557580658f9c3303
Arg [1] : 000000000000000000000000fa39c1c670b48956eef9fd0bbd0e81a290326330
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Arg [4] : ff1f98431c8ad98523631ae4a59f267346ea31f9840000000000000000000000
Arg [5] : e34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54
Arg [6] : ff5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f0000000000000000000000
Arg [7] : 96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f
Arg [8] : 000000000000000000000000e92b586627cca7a83dc919cc7127196d70f55a06
Arg [9] : 00000000000000000000000000700052c0608f670705380a4900e0a8080010cc
Arg [10] : 000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 68.34% | $4.08 | 1 | $4.08 | |
ETH | 28.50% | $1.7 | 1 | $1.7 | |
ETH | Ether (ETH) | <0.01% | $3,317.38 | 0.000000000000000001 | <$0.000001 |
OP | 3.17% | $0.188882 | 1 | $0.1888 | |
OP | <0.01% | $3,318.2 | 0.000000000000000001 | <$0.000001 | |
ARB | <0.01% | $3,317.38 | 0.000000000000000001 | <$0.000001 | |
BASE | <0.01% | $3,280.13 | 0.000000000000000001 | <$0.000001 | |
BSC | <0.01% | $629.67 | 0.000000000000000001 | <$0.000001 | |
AVAX | <0.01% | $43.35 | 0.000000000000000001 | <$0.000001 | |
GNO | <0.01% | $1 | 0.000000000000000001 | <$0.000001 | |
FTM | <0.01% | $0.742676 | 0.000000000000000001 | <$0.000001 | |
POL | <0.01% | $0.486906 | 0.000000000000000001 | <$0.000001 |
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.
Address QR Code
My Address - Private Name Tag or Note
My Name Tag:
Private Note:
Please DO NOT store any passwords or private keys here.
The compiled contract might be susceptible to VerbatimInvalidDeduplication (low-severity) Solidity Compiler Bugs.
Connect a Wallet
Connect a Wallet
Connect a Wallet
SignIn
Address Cards
Before You Copy
Transaction Private Note
This website uses cookies to improve your experience. By continuing to use this website, you agree to its Terms and Privacy Policy.