four-flap-meme-sdk 2.2.2 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +41 -6
- package/README.md +31 -0
- package/README.zh-CN.md +41 -6
- package/dist/__tests__/subpath-exports.test.d.ts +1 -0
- package/dist/__tests__/subpath-exports.test.js +64 -0
- package/dist/abis/common.d.ts +85 -0
- package/dist/abis/common.js +264 -0
- package/dist/abis/contracts/TaxToken.json +969 -0
- package/dist/abis/contracts/TokenManager2.json +136 -0
- package/dist/abis/contracts/index.d.ts +5 -0
- package/dist/abis/contracts/index.js +5 -0
- package/dist/abis/flap/index.d.ts +3 -0
- package/dist/abis/flap/index.js +3 -0
- package/dist/abis/flap/portal-events.d.ts +6 -0
- package/dist/abis/flap/portal-events.js +17 -0
- package/dist/abis/flap/portal.d.ts +6 -0
- package/dist/abis/flap/portal.js +37 -0
- package/dist/abis/flap/vault.d.ts +171 -0
- package/dist/abis/flap/vault.js +91 -0
- package/dist/abis/index.d.ts +8 -0
- package/dist/abis/index.js +11 -0
- package/dist/bundle-core/__tests__/config-helpers.test.d.ts +1 -0
- package/dist/bundle-core/__tests__/config-helpers.test.js +28 -0
- package/dist/bundle-core/__tests__/facade-parity.test.d.ts +1 -0
- package/dist/bundle-core/__tests__/facade-parity.test.js +33 -0
- package/dist/bundle-core/__tests__/sign-context-helpers.test.d.ts +1 -0
- package/dist/bundle-core/__tests__/sign-context-helpers.test.js +60 -0
- package/dist/bundle-core/__tests__/sign-fixture.test.d.ts +1 -0
- package/dist/bundle-core/__tests__/sign-fixture.test.js +220 -0
- package/dist/bundle-core/__tests__/sign-fixtures.d.ts +10 -0
- package/dist/bundle-core/__tests__/sign-fixtures.js +16 -0
- package/dist/bundle-core/config-helpers.d.ts +36 -0
- package/dist/bundle-core/config-helpers.js +57 -0
- package/dist/bundle-core/errors.d.ts +50 -0
- package/dist/bundle-core/errors.js +35 -0
- package/dist/bundle-core/four-meme/approve-tokenmanager.d.ts +7 -0
- package/dist/bundle-core/four-meme/approve-tokenmanager.js +99 -0
- package/dist/bundle-core/four-meme/core-helpers.d.ts +8 -0
- package/dist/bundle-core/four-meme/core-helpers.js +40 -0
- package/dist/bundle-core/four-meme/core.d.ts +4 -0
- package/dist/bundle-core/four-meme/core.js +515 -0
- package/dist/bundle-core/four-meme/pancake-proxy.d.ts +28 -0
- package/dist/bundle-core/four-meme/pancake-proxy.js +679 -0
- package/dist/bundle-core/four-meme/private.d.ts +27 -0
- package/dist/bundle-core/four-meme/private.js +465 -0
- package/dist/bundle-core/four-meme/sign-context-helpers.d.ts +2 -0
- package/dist/bundle-core/four-meme/sign-context-helpers.js +2 -0
- package/dist/bundle-core/four-meme/swap-buy-first.d.ts +8 -0
- package/dist/bundle-core/four-meme/swap-buy-first.js +493 -0
- package/dist/bundle-core/four-meme/swap-hop-helpers.d.ts +6 -0
- package/dist/bundle-core/four-meme/swap-hop-helpers.js +63 -0
- package/dist/bundle-core/four-meme/swap-internal.d.ts +3 -0
- package/dist/bundle-core/four-meme/swap-internal.js +18 -0
- package/dist/bundle-core/four-meme/swap-sign-helpers.d.ts +27 -0
- package/dist/bundle-core/four-meme/swap-sign-helpers.js +105 -0
- package/dist/bundle-core/four-meme/swap.d.ts +17 -0
- package/dist/bundle-core/four-meme/swap.js +505 -0
- package/dist/bundle-core/four-meme/types/buy-first.d.ts +50 -0
- package/dist/bundle-core/four-meme/types/buy-first.js +1 -0
- package/dist/bundle-core/four-meme/types/core-flow.d.ts +63 -0
- package/dist/bundle-core/four-meme/types/core-flow.js +1 -0
- package/dist/bundle-core/four-meme/types/index.d.ts +600 -0
- package/dist/bundle-core/four-meme/types/index.js +1 -0
- package/dist/bundle-core/four-meme/types/swap-internal.d.ts +19 -0
- package/dist/bundle-core/four-meme/types/swap-internal.js +1 -0
- package/dist/bundle-core/four-meme/types.d.ts +1 -0
- package/dist/bundle-core/four-meme/types.js +1 -0
- package/dist/bundle-core/four-meme/utils-disperse.d.ts +7 -0
- package/dist/bundle-core/four-meme/utils-disperse.js +396 -0
- package/dist/bundle-core/four-meme/utils-pairwise.d.ts +8 -0
- package/dist/bundle-core/four-meme/utils-pairwise.js +328 -0
- package/dist/bundle-core/four-meme/utils-sweep.d.ts +8 -0
- package/dist/bundle-core/four-meme/utils-sweep.js +744 -0
- package/dist/bundle-core/index.d.ts +8 -0
- package/dist/bundle-core/index.js +8 -0
- package/dist/bundle-core/internal.d.ts +21 -0
- package/dist/bundle-core/internal.js +182 -0
- package/dist/bundle-core/sign-context-helpers.d.ts +25 -0
- package/dist/bundle-core/sign-context-helpers.js +67 -0
- package/dist/bundle-core/submit.d.ts +293 -0
- package/dist/bundle-core/submit.js +727 -0
- package/dist/bundle-core/types/index.d.ts +8 -0
- package/dist/bundle-core/types/index.js +1 -0
- package/dist/bundle-core/types.d.ts +1 -0
- package/dist/bundle-core/types.js +1 -0
- package/dist/chains/bsc/four/approve-tokenmanager.d.ts +1 -26
- package/dist/chains/bsc/four/approve-tokenmanager.js +1 -113
- package/dist/chains/bsc/four/config.d.ts +5 -67
- package/dist/chains/bsc/four/config.js +2 -114
- package/dist/chains/bsc/four/core.d.ts +1 -4
- package/dist/chains/bsc/four/core.js +1 -592
- package/dist/chains/bsc/four/index.d.ts +6 -6
- package/dist/chains/bsc/four/index.js +6 -6
- package/dist/chains/bsc/four/internal.d.ts +2 -46
- package/dist/chains/bsc/four/internal.js +2 -239
- package/dist/chains/bsc/four/pancake-proxy.d.ts +1 -28
- package/dist/chains/bsc/four/pancake-proxy.js +1 -687
- package/dist/chains/bsc/four/private.d.ts +1 -27
- package/dist/chains/bsc/four/private.js +1 -477
- package/dist/chains/bsc/four/submit.d.ts +2 -315
- package/dist/chains/bsc/four/submit.js +2 -752
- package/dist/chains/bsc/four/swap-buy-first.d.ts +2 -55
- package/dist/chains/bsc/four/swap-buy-first.js +2 -507
- package/dist/chains/bsc/four/swap-internal.d.ts +1 -3
- package/dist/chains/bsc/four/swap-internal.js +1 -18
- package/dist/chains/bsc/four/swap.d.ts +2 -144
- package/dist/chains/bsc/four/swap.js +2 -766
- package/dist/chains/bsc/four/types.d.ts +1 -476
- package/dist/chains/bsc/four/utils-disperse.d.ts +1 -0
- package/dist/chains/bsc/four/utils-disperse.js +1 -0
- package/dist/chains/bsc/four/utils-pairwise.d.ts +1 -0
- package/dist/chains/bsc/four/utils-pairwise.js +1 -0
- package/dist/chains/bsc/four/utils-sweep.d.ts +1 -0
- package/dist/chains/bsc/four/utils-sweep.js +1 -0
- package/dist/chains/bsc/four/utils.d.ts +5 -18
- package/dist/chains/bsc/four/utils.js +5 -1552
- package/dist/chains/bsc/iro.d.ts +5 -0
- package/dist/chains/bsc/iro.js +4 -0
- package/dist/chains/bsc/pancake/bundle-buy-first-helpers.d.ts +159 -0
- package/dist/chains/bsc/pancake/bundle-buy-first-helpers.js +117 -0
- package/dist/chains/bsc/pancake/bundle-buy-first.d.ts +1 -91
- package/dist/chains/bsc/pancake/bundle-buy-first.js +97 -212
- package/dist/chains/bsc/pancake/bundle-swap-helpers.d.ts +241 -0
- package/dist/chains/bsc/pancake/bundle-swap-helpers.js +565 -0
- package/dist/chains/bsc/pancake/bundle-swap.d.ts +1 -79
- package/dist/chains/bsc/pancake/bundle-swap.js +114 -726
- package/dist/chains/bsc/pancake/index.d.ts +4 -2
- package/dist/chains/bsc/pancake/index.js +1 -3
- package/dist/chains/bsc/platforms/iro/factory.d.ts +2 -2
- package/dist/chains/bsc/platforms/iro/factory.js +3 -1
- package/dist/chains/bsc/platforms/iro/index.d.ts +5 -5
- package/dist/chains/bsc/platforms/iro/index.js +3 -3
- package/dist/chains/bsc/platforms/iro/pool.js +31 -10
- package/dist/chains/bsc/platforms/iro/token.js +4 -1
- package/dist/chains/eni/batch-router/bundle-approve.js +4 -3
- package/dist/chains/eni/batch-router/transfer.js +55 -25
- package/dist/chains/eni/batch-router/utils.js +32 -6
- package/dist/chains/eni/bundler/sign.js +5 -6
- package/dist/chains/eni/bundler/submit.js +1 -4
- package/dist/chains/eni/constants.js +1 -1
- package/dist/chains/eni/flat-aliases.d.ts +10 -0
- package/dist/chains/eni/flat-aliases.js +8 -0
- package/dist/chains/eni/index.d.ts +2 -1
- package/dist/chains/eni/index.js +1 -0
- package/dist/chains/eni/platforms/daoaas/create.js +2 -2
- package/dist/chains/eni/platforms/daoaas/index.d.ts +3 -3
- package/dist/chains/eni/platforms/daoaas/index.js +3 -3
- package/dist/chains/eni/platforms/daoaas/meta.js +9 -6
- package/dist/chains/eni/platforms/daoaas/portal-direct.js +28 -44
- package/dist/chains/eni/platforms/daoaas/portal.js +10 -6
- package/dist/chains/eni/platforms/dswap/liquidity.js +58 -26
- package/dist/chains/eni/platforms/fair-launch/index.d.ts +2 -2
- package/dist/chains/eni/platforms/fair-launch/index.js +1 -1
- package/dist/chains/eni/platforms/fair-launch/launcher.js +87 -46
- package/dist/chains/eni/platforms/fair-launch/pool.js +4 -1
- package/dist/chains/eni/platforms/fair-launch/presets.js +2 -2
- package/dist/chains/eni/platforms/iro/factory.d.ts +2 -2
- package/dist/chains/eni/platforms/iro/factory.js +3 -1
- package/dist/chains/eni/platforms/iro/index.d.ts +6 -6
- package/dist/chains/eni/platforms/iro/index.js +4 -4
- package/dist/chains/eni/platforms/iro/pool.js +90 -26
- package/dist/chains/eni/platforms/iro/token.js +107 -31
- package/dist/chains/eni/platforms/iro/whitelist.js +6 -18
- package/dist/chains/eni/submit.d.ts +43 -0
- package/dist/chains/eni/submit.js +286 -0
- package/dist/chains/index.d.ts +13 -0
- package/dist/chains/index.js +13 -0
- package/dist/chains/xlayer/eip7702/bundle-approve.d.ts +2 -26
- package/dist/chains/xlayer/eip7702/bundle-approve.js +11 -21
- package/dist/chains/xlayer/eip7702/bundle-buy.d.ts +2 -6
- package/dist/chains/xlayer/eip7702/bundle-buy.js +13 -51
- package/dist/chains/xlayer/eip7702/bundle-create.js +93 -59
- package/dist/chains/xlayer/eip7702/bundle-sell.d.ts +2 -6
- package/dist/chains/xlayer/eip7702/bundle-sell.js +29 -111
- package/dist/chains/xlayer/eip7702/bundle-swap.d.ts +3 -65
- package/dist/chains/xlayer/eip7702/bundle-swap.js +51 -245
- package/dist/chains/xlayer/eip7702/constants.d.ts +1 -16
- package/dist/chains/xlayer/eip7702/constants.js +3 -21
- package/dist/chains/xlayer/eip7702/flat-aliases.d.ts +13 -0
- package/dist/chains/xlayer/eip7702/flat-aliases.js +10 -0
- package/dist/chains/xlayer/eip7702/index.d.ts +28 -46
- package/dist/chains/xlayer/eip7702/index.js +28 -81
- package/dist/chains/xlayer/eip7702/multi-hop-transfer-helpers.d.ts +79 -0
- package/dist/chains/xlayer/eip7702/multi-hop-transfer-helpers.js +1 -0
- package/dist/chains/xlayer/eip7702/multi-hop-transfer.d.ts +2 -203
- package/dist/chains/xlayer/eip7702/multi-hop-transfer.js +63 -307
- package/dist/chains/xlayer/eip7702/transfer-context-helpers.d.ts +26 -0
- package/dist/chains/xlayer/eip7702/transfer-context-helpers.js +57 -0
- package/dist/chains/xlayer/eip7702/types.d.ts +88 -0
- package/dist/chains/xlayer/eip7702/utils.d.ts +0 -3
- package/dist/chains/xlayer/eip7702/utils.js +23 -28
- package/dist/chains/xlayer/eip7702/volume-helpers.d.ts +148 -0
- package/dist/chains/xlayer/eip7702/volume-helpers.js +48 -0
- package/dist/chains/xlayer/eip7702/volume.d.ts +6 -184
- package/dist/chains/xlayer/eip7702/volume.js +89 -164
- package/dist/chains/xlayer/eoa/constants.js +1 -1
- package/dist/chains/xlayer/eoa/dex-helpers.js +5 -5
- package/dist/chains/xlayer/eoa/eoa-bundle-swap-helpers.d.ts +126 -0
- package/dist/chains/xlayer/eoa/eoa-bundle-swap-helpers.js +228 -0
- package/dist/chains/xlayer/eoa/eoa-bundle-swap.d.ts +1 -95
- package/dist/chains/xlayer/eoa/eoa-bundle-swap.js +66 -299
- package/dist/chains/xlayer/eoa/eoa-wash-volume.d.ts +1 -1
- package/dist/chains/xlayer/eoa/eoa-wash-volume.js +18 -23
- package/dist/chains/xlayer/eoa/index.d.ts +10 -6
- package/dist/chains/xlayer/eoa/index.js +8 -23
- package/dist/chains/xlayer/eoa/portal-ops.js +7 -2
- package/dist/chains/xlayer/eoa/router-manager.js +3 -3
- package/dist/chains/xlayer/eoa/types.d.ts +2 -2
- package/dist/chains/xlayer/eoa/types.js +1 -3
- package/dist/chains/xlayer/index.d.ts +3 -2
- package/dist/chains/xlayer/index.js +4 -7
- package/dist/contracts/helper3.d.ts +20 -5
- package/dist/contracts/helper3.js +56 -20
- package/dist/contracts/tm-bundle-helpers.d.ts +88 -0
- package/dist/contracts/tm-bundle-helpers.js +72 -0
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.d.ts +1 -26
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.js +1 -113
- package/dist/contracts/tm-bundle-merkle/config.d.ts +5 -67
- package/dist/contracts/tm-bundle-merkle/config.js +2 -114
- package/dist/contracts/tm-bundle-merkle/core.d.ts +1 -4
- package/dist/contracts/tm-bundle-merkle/core.js +1 -591
- package/dist/contracts/tm-bundle-merkle/index.d.ts +5 -5
- package/dist/contracts/tm-bundle-merkle/index.js +5 -5
- package/dist/contracts/tm-bundle-merkle/internal.d.ts +2 -46
- package/dist/contracts/tm-bundle-merkle/internal.js +2 -238
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.d.ts +1 -28
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +1 -686
- package/dist/contracts/tm-bundle-merkle/private.d.ts +1 -27
- package/dist/contracts/tm-bundle-merkle/private.js +1 -476
- package/dist/contracts/tm-bundle-merkle/submit.d.ts +3 -314
- package/dist/contracts/tm-bundle-merkle/submit.js +3 -928
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +2 -55
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +2 -506
- package/dist/contracts/tm-bundle-merkle/swap-internal.d.ts +1 -3
- package/dist/contracts/tm-bundle-merkle/swap-internal.js +1 -18
- package/dist/contracts/tm-bundle-merkle/swap.d.ts +2 -144
- package/dist/contracts/tm-bundle-merkle/swap.js +2 -764
- package/dist/contracts/tm-bundle-merkle/types.d.ts +1 -476
- package/dist/contracts/tm-bundle-merkle/utils-disperse.d.ts +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-disperse.js +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-pairwise.d.ts +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-pairwise.js +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-sweep.d.ts +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-sweep.js +1 -0
- package/dist/contracts/tm-bundle-merkle/utils.d.ts +6 -18
- package/dist/contracts/tm-bundle-merkle/utils.js +6 -1501
- package/dist/contracts/tm-bundle.d.ts +3 -51
- package/dist/contracts/tm-bundle.js +108 -177
- package/dist/contracts/tm.d.ts +3 -2
- package/dist/contracts/tm.js +37 -32
- package/dist/contracts/tm1.js +9 -4
- package/dist/contracts/tm2.js +9 -4
- package/dist/dex/direct-router-helpers.d.ts +264 -0
- package/dist/dex/direct-router-helpers.js +539 -0
- package/dist/dex/direct-router.d.ts +3 -125
- package/dist/dex/direct-router.js +237 -666
- package/dist/dex/types.d.ts +81 -0
- package/dist/dex/types.js +1 -0
- package/dist/exports/root-bundle-and-tooling.d.ts +27 -0
- package/dist/exports/root-bundle-and-tooling.js +30 -0
- package/dist/exports/root-eni-and-bsc-iro.d.ts +26 -0
- package/dist/exports/root-eni-and-bsc-iro.js +66 -0
- package/dist/exports/root-foundations.d.ts +35 -0
- package/dist/exports/root-foundations.js +70 -0
- package/dist/exports/root-swap-dex-and-xlayer.d.ts +30 -0
- package/dist/exports/root-swap-dex-and-xlayer.js +78 -0
- package/dist/flap/index.d.ts +10 -0
- package/dist/flap/index.js +8 -0
- package/dist/flows/create.d.ts +2 -1
- package/dist/flows/create.js +6 -6
- package/dist/flows/index.d.ts +1 -0
- package/dist/flows/index.js +1 -0
- package/dist/index.d.ts +20 -85
- package/dist/index.js +20 -215
- package/dist/merkle/index.d.ts +12 -0
- package/dist/merkle/index.js +11 -0
- package/dist/shared/abis/common.d.ts +2 -83
- package/dist/shared/abis/common.js +2 -252
- package/dist/shared/abis/index.d.ts +5 -6
- package/dist/shared/abis/index.js +5 -7
- package/dist/shared/clients/blockrazor.js +39 -25
- package/dist/shared/clients/club48.d.ts +2 -2
- package/dist/shared/clients/club48.js +34 -29
- package/dist/shared/clients/emitservice.js +2 -0
- package/dist/shared/clients/four.d.ts +21 -6
- package/dist/shared/clients/four.js +29 -24
- package/dist/shared/clients/index.d.ts +8 -0
- package/dist/shared/clients/index.js +8 -0
- package/dist/shared/clients/merkle.js +27 -34
- package/dist/shared/constants/addresses.d.ts +1 -1
- package/dist/shared/constants/addresses.js +11 -2
- package/dist/shared/constants/chains.d.ts +1 -1
- package/dist/shared/constants/chains.js +1 -1
- package/dist/shared/constants/gas.d.ts +1 -1
- package/dist/shared/constants/gas.js +2 -6
- package/dist/shared/constants/index.d.ts +3 -0
- package/dist/shared/constants/index.js +1 -0
- package/dist/shared/constants/quote.d.ts +30 -0
- package/dist/shared/constants/quote.js +37 -0
- package/dist/shared/flap/abi.js +1 -1
- package/dist/shared/flap/constants.d.ts +1 -2
- package/dist/shared/flap/constants.js +2 -3
- package/dist/shared/flap/curve.js +3 -0
- package/dist/shared/flap/errors.d.ts +1 -4
- package/dist/shared/flap/errors.js +20 -1
- package/dist/shared/flap/index.d.ts +5 -4
- package/dist/shared/flap/index.js +5 -4
- package/dist/shared/flap/meta.d.ts +22 -18
- package/dist/shared/flap/meta.js +12 -17
- package/dist/shared/flap/permit.js +5 -2
- package/dist/shared/flap/pinata.d.ts +22 -6
- package/dist/shared/flap/pinata.js +21 -26
- package/dist/shared/flap/portal-bundle-merkle/config.d.ts +3 -72
- package/dist/shared/flap/portal-bundle-merkle/config.js +4 -124
- package/dist/shared/flap/portal-bundle-merkle/core-helpers.d.ts +32 -0
- package/dist/shared/flap/portal-bundle-merkle/core-helpers.js +83 -0
- package/dist/shared/flap/portal-bundle-merkle/core.d.ts +0 -4
- package/dist/shared/flap/portal-bundle-merkle/core.js +86 -278
- package/dist/shared/flap/portal-bundle-merkle/create-to-dex.d.ts +7 -2
- package/dist/shared/flap/portal-bundle-merkle/create-to-dex.js +101 -206
- package/dist/shared/flap/portal-bundle-merkle/curve-to-dex.js +100 -92
- package/dist/shared/flap/portal-bundle-merkle/index.d.ts +11 -7
- package/dist/shared/flap/portal-bundle-merkle/index.js +4 -7
- package/dist/shared/flap/portal-bundle-merkle/pancake-proxy.js +71 -68
- package/dist/shared/flap/portal-bundle-merkle/private.js +61 -114
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first-helpers.d.ts +125 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first-helpers.js +113 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first.d.ts +1 -64
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first.js +66 -247
- package/dist/shared/flap/portal-bundle-merkle/swap-helpers.d.ts +149 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-helpers.js +259 -0
- package/dist/shared/flap/portal-bundle-merkle/swap.d.ts +2 -71
- package/dist/shared/flap/portal-bundle-merkle/swap.js +103 -410
- package/dist/shared/flap/portal-bundle-merkle/types.d.ts +83 -9
- package/dist/shared/flap/portal-bundle-merkle/utils.d.ts +1 -80
- package/dist/shared/flap/portal-bundle-merkle/utils.js +145 -265
- package/dist/shared/flap/portal-bundle.js +55 -56
- package/dist/shared/flap/portal-create-token.d.ts +77 -0
- package/dist/shared/flap/portal-create-token.js +214 -0
- package/dist/shared/flap/portal.d.ts +14 -3
- package/dist/shared/flap/portal.js +50 -25
- package/dist/shared/flap/vanity.d.ts +1 -5
- package/dist/shared/flap/vanity.js +6 -17
- package/dist/shared/flap/vault.d.ts +17 -124
- package/dist/shared/flap/vault.js +67 -148
- package/dist/shared/foundation/dex/v3-path.d.ts +6 -0
- package/dist/shared/foundation/dex/v3-path.js +35 -0
- package/dist/shared/foundation/gas/bundle-gas.d.ts +49 -0
- package/dist/shared/foundation/gas/bundle-gas.js +93 -0
- package/dist/shared/foundation/gas/profit-hop.d.ts +20 -0
- package/dist/shared/foundation/gas/profit-hop.js +72 -0
- package/dist/shared/foundation/index.d.ts +13 -0
- package/dist/shared/foundation/index.js +12 -0
- package/dist/shared/foundation/nonce/nonce-manager.d.ts +17 -0
- package/dist/shared/foundation/nonce/nonce-manager.js +183 -0
- package/dist/shared/foundation/normalize-unknown.d.ts +9 -0
- package/dist/shared/foundation/normalize-unknown.js +29 -0
- package/dist/shared/foundation/sdk-logger.d.ts +13 -0
- package/dist/shared/foundation/sdk-logger.js +12 -0
- package/dist/shared/foundation/tx/build-request.d.ts +17 -0
- package/dist/shared/foundation/tx/build-request.js +25 -0
- package/dist/shared/foundation/tx/sign-batch.d.ts +5 -0
- package/dist/shared/foundation/tx/sign-batch.js +26 -0
- package/dist/shared/foundation/tx/wallet-sign-patch.d.ts +1 -0
- package/dist/shared/foundation/tx/wallet-sign-patch.js +18 -0
- package/dist/shared/foundation/types/airdrop-sweep.d.ts +79 -0
- package/dist/shared/foundation/types/airdrop-sweep.js +1 -0
- package/dist/shared/foundation/types/erc20.d.ts +65 -0
- package/dist/shared/foundation/types/erc20.js +1 -0
- package/dist/shared/foundation/types/holders-maker.d.ts +64 -0
- package/dist/shared/foundation/types/holders-maker.js +1 -0
- package/dist/shared/foundation/types/index.d.ts +7 -0
- package/dist/shared/foundation/types/index.js +1 -0
- package/dist/shared/foundation/types/lp-inspect.d.ts +102 -0
- package/dist/shared/foundation/types/lp-inspect.js +1 -0
- package/dist/shared/foundation/types/multicall.d.ts +5 -0
- package/dist/shared/foundation/types/multicall.js +1 -0
- package/dist/shared/foundation/types/private-sale.d.ts +35 -0
- package/dist/shared/foundation/types/private-sale.js +1 -0
- package/dist/shared/foundation/types/quote-helpers.d.ts +17 -0
- package/dist/shared/foundation/types/quote-helpers.js +1 -0
- package/dist/shared/four/tax-token.d.ts +1 -1
- package/dist/shared/four/tax-token.js +27 -7
- package/dist/shared/index.d.ts +6 -0
- package/dist/shared/index.js +4 -0
- package/dist/types/errors.d.ts +27 -0
- package/dist/types/errors.js +34 -0
- package/dist/utils/airdrop-sweep.d.ts +4 -76
- package/dist/utils/airdrop-sweep.js +42 -55
- package/dist/utils/bundle-helpers.d.ts +9 -243
- package/dist/utils/bundle-helpers.js +10 -584
- package/dist/utils/constants.d.ts +5 -61
- package/dist/utils/constants.js +5 -80
- package/dist/utils/contract-factory.d.ts +2 -4
- package/dist/utils/contract-factory.js +25 -18
- package/dist/utils/erc20.d.ts +7 -89
- package/dist/utils/erc20.js +94 -125
- package/dist/utils/errors.d.ts +12 -1
- package/dist/utils/errors.js +60 -1
- package/dist/utils/holders-maker/addresses.d.ts +12 -0
- package/dist/utils/holders-maker/addresses.js +15 -0
- package/dist/utils/holders-maker/buy-tx.d.ts +44 -0
- package/dist/utils/holders-maker/buy-tx.js +278 -0
- package/dist/utils/holders-maker/constants.d.ts +6 -0
- package/dist/utils/holders-maker/constants.js +7 -0
- package/dist/utils/holders-maker/disperse.d.ts +18 -0
- package/dist/utils/holders-maker/disperse.js +90 -0
- package/dist/utils/holders-maker/routing.d.ts +4 -0
- package/dist/utils/holders-maker/routing.js +45 -0
- package/dist/utils/holders-maker/transfer-tx.d.ts +4 -0
- package/dist/utils/holders-maker/transfer-tx.js +67 -0
- package/dist/utils/holders-maker-helpers.d.ts +9 -0
- package/dist/utils/holders-maker-helpers.js +9 -0
- package/dist/utils/holders-maker.d.ts +2 -138
- package/dist/utils/holders-maker.js +26 -661
- package/dist/utils/hop-chains.d.ts +35 -0
- package/dist/utils/hop-chains.js +215 -0
- package/dist/utils/lp-inspect-helpers.d.ts +9 -0
- package/dist/utils/lp-inspect-helpers.js +109 -0
- package/dist/utils/lp-inspect.d.ts +2 -112
- package/dist/utils/lp-inspect.js +73 -223
- package/dist/utils/mpcExclusive.d.ts +2 -5
- package/dist/utils/mpcExclusive.js +4 -3
- package/dist/utils/private-sale.d.ts +2 -58
- package/dist/utils/private-sale.js +4 -15
- package/dist/utils/provider-factory.d.ts +4 -0
- package/dist/utils/provider-factory.js +10 -0
- package/dist/utils/quote-helpers.d.ts +4 -45
- package/dist/utils/quote-helpers.js +17 -74
- package/dist/utils/stealth-transfer.d.ts +2 -28
- package/dist/utils/stealth-transfer.js +31 -15
- package/dist/utils/swap-helpers.d.ts +2 -15
- package/dist/utils/swap-helpers.js +6 -11
- package/dist/utils/types/airdrop-sweep.d.ts +1 -0
- package/dist/utils/types/airdrop-sweep.js +1 -0
- package/dist/utils/types/contract-factory.d.ts +1 -0
- package/dist/utils/types/contract-factory.js +1 -0
- package/dist/utils/types/erc20.d.ts +1 -0
- package/dist/utils/types/erc20.js +1 -0
- package/dist/utils/types/errors.d.ts +1 -0
- package/dist/utils/types/errors.js +1 -0
- package/dist/utils/types/holders-maker.d.ts +1 -0
- package/dist/utils/types/holders-maker.js +1 -0
- package/dist/utils/types/hop-chains.d.ts +8 -0
- package/dist/utils/types/hop-chains.js +1 -0
- package/dist/utils/types/index.d.ts +13 -0
- package/dist/utils/types/index.js +1 -0
- package/dist/utils/types/lp-inspect.d.ts +1 -0
- package/dist/utils/types/lp-inspect.js +1 -0
- package/dist/utils/types/mpc-exclusive.d.ts +5 -0
- package/dist/utils/types/mpc-exclusive.js +1 -0
- package/dist/utils/types/private-sale.d.ts +1 -0
- package/dist/utils/types/private-sale.js +1 -0
- package/dist/utils/types/quote-helpers.d.ts +1 -0
- package/dist/utils/types/quote-helpers.js +1 -0
- package/dist/utils/types/stealth-transfer.d.ts +44 -0
- package/dist/utils/types/stealth-transfer.js +1 -0
- package/dist/utils/types/wallet.d.ts +25 -0
- package/dist/utils/types/wallet.js +1 -0
- package/dist/utils/wallet.d.ts +2 -25
- package/dist/utils/wallet.js +13 -10
- package/dist/vanity/index.d.ts +5 -0
- package/dist/vanity/index.js +5 -0
- package/package.json +160 -4
- package/src/abis/contracts/TaxToken.json +969 -0
- package/src/abis/contracts/TokenManager.json +836 -0
- package/src/abis/contracts/TokenManager2.json +136 -0
- package/src/abis/contracts/TokenManagerHelper3.json +993 -0
- package/dist/shared/abis/TaxToken.json +0 -105
- package/dist/shared/abis/TokenManager2.json +0 -60
- /package/dist/{shared/abis → abis/contracts}/TokenManager.json +0 -0
- /package/dist/{shared/abis → abis/contracts}/TokenManagerHelper3.json +0 -0
|
@@ -1,524 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* 支持:
|
|
5
|
-
* - BSC: PancakeSwap V2/V3
|
|
6
|
-
* - Monad: PancakeSwap V2, Uniswap V2/V3
|
|
7
|
-
*
|
|
8
|
-
* 收费方式:交易末尾附加利润提取交易(和内盘一致)
|
|
2
|
+
* direct-router - 主函数
|
|
9
3
|
*/
|
|
10
|
-
import { ethers, Wallet,
|
|
11
|
-
import { NonceManager,
|
|
12
|
-
import {
|
|
4
|
+
import { ethers, Wallet, Contract } from 'ethers';
|
|
5
|
+
import { NonceManager, buildProfitHopTransactions, PROFIT_HOP_COUNT, buildGasFields } from '../utils/bundle-helpers.js';
|
|
6
|
+
import { getProfitRecipient } from '../shared/constants/index.js';
|
|
13
7
|
import { GAS_LIMITS } from '../shared/constants/index.js';
|
|
14
|
-
import {
|
|
8
|
+
import { ERC20_ABI } from '../abis/common.js';
|
|
15
9
|
import { getTokenToNativeQuote, quoteV2 } from '../utils/quote-helpers.js';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// ============================================================================
|
|
19
|
-
const providerCache = new Map();
|
|
20
|
-
const PROVIDER_CACHE_TTL = 60000; // 60秒后过期
|
|
21
|
-
const providerCacheTimestamps = new Map();
|
|
22
|
-
/**
|
|
23
|
-
* 获取缓存的 Provider 实例
|
|
24
|
-
* - 根据 rpcUrl + chainId 作为缓存 key
|
|
25
|
-
* - 60秒后自动失效,避免长连接问题
|
|
26
|
-
*/
|
|
27
|
-
function getCachedProvider(rpcUrl, chainId, chainName) {
|
|
28
|
-
const cacheKey = `${rpcUrl}_${chainId}`;
|
|
29
|
-
const now = Date.now();
|
|
30
|
-
// 检查缓存是否存在且未过期
|
|
31
|
-
const cachedProvider = providerCache.get(cacheKey);
|
|
32
|
-
const timestamp = providerCacheTimestamps.get(cacheKey) || 0;
|
|
33
|
-
if (cachedProvider && (now - timestamp) < PROVIDER_CACHE_TTL) {
|
|
34
|
-
return cachedProvider;
|
|
35
|
-
}
|
|
36
|
-
// ✅ 禁用 ENS: 避免 "network does not support ENS" 错误
|
|
37
|
-
const provider = new JsonRpcProvider(rpcUrl, { chainId, name: chainName, ensAddress: undefined });
|
|
38
|
-
providerCache.set(cacheKey, provider);
|
|
39
|
-
providerCacheTimestamps.set(cacheKey, now);
|
|
40
|
-
return provider;
|
|
41
|
-
}
|
|
42
|
-
const DEFAULT_GAS_LIMIT = 300000;
|
|
43
|
-
const DEADLINE_MINUTES = 20;
|
|
44
|
-
// ✅ BLOCKRAZOR_BUILDER_EOA 从公共模块导入
|
|
45
|
-
/**
|
|
46
|
-
* 截断小数位数,避免超过代币精度导致 parseUnits 报错
|
|
47
|
-
* 例如:truncateDecimals("21906.025000000000000000", 1) => "21906.0"
|
|
48
|
-
*/
|
|
49
|
-
function truncateDecimals(value, decimals) {
|
|
50
|
-
if (!value || decimals < 0)
|
|
51
|
-
return value;
|
|
52
|
-
const parts = value.split('.');
|
|
53
|
-
if (parts.length === 1)
|
|
54
|
-
return value; // 没有小数点
|
|
55
|
-
const integerPart = parts[0];
|
|
56
|
-
const decimalPart = parts[1];
|
|
57
|
-
if (decimals === 0)
|
|
58
|
-
return integerPart;
|
|
59
|
-
// 截断到指定小数位数
|
|
60
|
-
const truncatedDecimal = decimalPart.slice(0, decimals);
|
|
61
|
-
// 如果截断后没有小数部分,只返回整数部分
|
|
62
|
-
if (!truncatedDecimal || truncatedDecimal === '')
|
|
63
|
-
return integerPart;
|
|
64
|
-
return `${integerPart}.${truncatedDecimal}`;
|
|
65
|
-
}
|
|
66
|
-
/** Router 地址配置 - ✅ 使用公共模块中的地址 */
|
|
67
|
-
export const DIRECT_ROUTERS = {
|
|
68
|
-
BSC: {
|
|
69
|
-
PANCAKESWAP_V2: ADDRESSES.BSC.PancakeV2Router,
|
|
70
|
-
PANCAKESWAP_V3: ADDRESSES.BSC.PancakeV3Router,
|
|
71
|
-
IROSWAP_V2: ADDRESSES.BSC.IroSwapV2Router,
|
|
72
|
-
WBNB: ADDRESSES.BSC.WBNB,
|
|
73
|
-
},
|
|
74
|
-
MONAD: {
|
|
75
|
-
// PancakeSwap
|
|
76
|
-
PANCAKESWAP_V2: '0xB1Bc24c34e88f7D43D5923034E3a14B24DaACfF9',
|
|
77
|
-
PANCAKESWAP_V3: '0x1b81D678ffb9C0263b24A97847620C99d213eB14',
|
|
78
|
-
// Uniswap
|
|
79
|
-
UNISWAP_V2: '0x4b2ab38dbf28d31d467aa8993f6c2585981d6804',
|
|
80
|
-
UNISWAP_V3: '0xd6145b2d3f379919e8cdeda7b97e37c4b2ca9c40',
|
|
81
|
-
// Wrapped Native
|
|
82
|
-
WMON: ADDRESSES.MONAD.WMON,
|
|
83
|
-
},
|
|
84
|
-
// XLayer (PotatoSwap + DYORSwap)
|
|
85
|
-
XLAYER: {
|
|
86
|
-
POTATOSWAP_V2: '0x881fb2f98c13d521009464e7d1cbf16e1b394e8e',
|
|
87
|
-
POTATOSWAP_V3: '0xB45D0149249488333E3F3f9F359807F4b810C1FC',
|
|
88
|
-
DYORSWAP_ROUTER: '0xfb001fbbace32f09cb6d3c449b935183de53ee96',
|
|
89
|
-
DYORSWAP_FACTORY: '0x2CcaDb1e437AA9cDc741574bDa154686B1F04C09',
|
|
90
|
-
V3_ROUTER: '0xBB069e9465BcabC4F488d21e793BDEf0F2d41D41',
|
|
91
|
-
V3_FACTORY: '0xa1415fAe79c4B196d087F02b8aD5a622B8A827E5',
|
|
92
|
-
WOKB: '0xe538905cf8410324e03a5a23c1c177a474d59b2b',
|
|
93
|
-
},
|
|
94
|
-
ENI: {
|
|
95
|
-
DSWAP_V2: ADDRESSES.ENI.DswapV2Router,
|
|
96
|
-
DSWAP_V3: ADDRESSES.ENI.DswapV3Router02,
|
|
97
|
-
IROSWAP_V2: ADDRESSES.ENI.IroSwapV2Router,
|
|
98
|
-
WEGAS: ADDRESSES.ENI.WEGAS,
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
/** 链 ID 映射 */
|
|
102
|
-
const CHAIN_IDS = {
|
|
103
|
-
BSC: 56,
|
|
104
|
-
MONAD: 143,
|
|
105
|
-
XLAYER: 196,
|
|
106
|
-
ENI: 173,
|
|
107
|
-
};
|
|
108
|
-
/** 获取原生包装代币地址 */
|
|
109
|
-
function getWrappedNative(chain) {
|
|
110
|
-
const chainUpper = chain.toUpperCase();
|
|
111
|
-
if (chainUpper === 'BSC')
|
|
112
|
-
return DIRECT_ROUTERS.BSC.WBNB;
|
|
113
|
-
if (chainUpper === 'MONAD')
|
|
114
|
-
return DIRECT_ROUTERS.MONAD.WMON;
|
|
115
|
-
if (chainUpper === 'XLAYER')
|
|
116
|
-
return DIRECT_ROUTERS.XLAYER.WOKB;
|
|
117
|
-
if (chainUpper === 'ENI')
|
|
118
|
-
return DIRECT_ROUTERS.ENI.WEGAS;
|
|
119
|
-
throw new Error(`Unsupported chain: ${chain}`);
|
|
120
|
-
}
|
|
121
|
-
// ============================================================================
|
|
122
|
-
// ABI - ✅ 使用公共模块
|
|
123
|
-
// ============================================================================
|
|
124
|
-
/** V2 Router ABI */
|
|
125
|
-
const V2_ROUTER_ABI = _V2_ROUTER_ABI;
|
|
126
|
-
/** SwapRouter02 的 V2 方法 ABI - ✅ 从公共模块导入 */
|
|
127
|
-
const SWAP_ROUTER02_V2_ABI = _SWAP_ROUTER02_V2_ABI;
|
|
128
|
-
/**
|
|
129
|
-
* DYORSwap SwapRouter02 的 V2 方法
|
|
130
|
-
*
|
|
131
|
-
* ⚠️ 重要:DYORSwap 使用自定义的函数签名,无法通过标准 ABI 编码
|
|
132
|
-
* 必须手动构造 calldata
|
|
133
|
-
*
|
|
134
|
-
* swapExactTokensForTokens selector: 0x3234fe0d
|
|
135
|
-
* unwrapWETH9 selector: 0x7342292e
|
|
136
|
-
* refundETH selector: 0x6d29fcf4
|
|
137
|
-
*/
|
|
138
|
-
const DYORSWAP_SELECTORS = {
|
|
139
|
-
swapExactTokensForTokens: '0x3234fe0d',
|
|
140
|
-
unwrapWETH9: '0x7342292e',
|
|
141
|
-
refundETH: '0x6d29fcf4',
|
|
142
|
-
multicall: '0x5ae401dc',
|
|
143
|
-
};
|
|
144
|
-
/**
|
|
145
|
-
* 手动编码 DYORSwap 的 swapExactTokensForTokens calldata
|
|
146
|
-
*
|
|
147
|
-
* 参数格式 (根据成功交易分析):
|
|
148
|
-
* - amountIn: uint256
|
|
149
|
-
* - amountOutMin: uint256
|
|
150
|
-
* - path: address[] (动态) - offset from start
|
|
151
|
-
* - pools: address[] (动态) - offset from start
|
|
152
|
-
* - to: uint256 (address(2) = 2)
|
|
153
|
-
* - flag: uint256 (1)
|
|
154
|
-
* - factory: address
|
|
155
|
-
*
|
|
156
|
-
* 成功交易的布局:
|
|
157
|
-
* [0] amountIn
|
|
158
|
-
* [1] amountOutMin
|
|
159
|
-
* [2] offset to path = 0xa0 = 160 = 5 * 32 (从参数区域开始)
|
|
160
|
-
* [3] offset to pools = 0xe0 = 224 = 7 * 32
|
|
161
|
-
* [4] to = 2
|
|
162
|
-
* [5] flag = 1
|
|
163
|
-
* [6] factory
|
|
164
|
-
* [7] path.length
|
|
165
|
-
* [8+] path elements
|
|
166
|
-
* [N] pools.length
|
|
167
|
-
* [N+1+] pools elements
|
|
168
|
-
*/
|
|
169
|
-
function encodeDYORSwapExactTokensForTokens(amountIn, amountOutMin, path, pools, to, // 可以是地址 (string) 或 address(2) 这样的特殊值 (bigint)
|
|
170
|
-
flag, factory) {
|
|
171
|
-
const pathOffset = 5n * 32n; // 160
|
|
172
|
-
// poolsOffset = pathOffset + (1 + path.length) * 32 = 160 + 96 = 256?
|
|
173
|
-
// 不对,成功交易是 224 = 160 + 64 = pathOffset + path.length * 32
|
|
174
|
-
const poolsOffset = pathOffset + BigInt(path.length) * 32n; // 160 + 2*32 = 224
|
|
175
|
-
// 手动构建数据
|
|
176
|
-
const pad32 = (hex) => hex.replace('0x', '').padStart(64, '0');
|
|
177
|
-
const toHex = (n) => n.toString(16).padStart(64, '0');
|
|
178
|
-
// 处理 to 参数:可以是地址 (string) 或特殊值 (bigint)
|
|
179
|
-
const toValue = typeof to === 'string' ? pad32(to) : toHex(to);
|
|
180
|
-
let data = DYORSWAP_SELECTORS.swapExactTokensForTokens;
|
|
181
|
-
data += toHex(amountIn);
|
|
182
|
-
data += toHex(amountOutMin);
|
|
183
|
-
data += toHex(pathOffset);
|
|
184
|
-
data += toHex(poolsOffset);
|
|
185
|
-
data += toValue;
|
|
186
|
-
data += toHex(flag);
|
|
187
|
-
data += pad32(factory);
|
|
188
|
-
// path 数据
|
|
189
|
-
data += toHex(BigInt(path.length));
|
|
190
|
-
for (const addr of path) {
|
|
191
|
-
data += pad32(addr);
|
|
192
|
-
}
|
|
193
|
-
// ⚠️ 不包含 pools 数据!成功交易中 pools 是空的
|
|
194
|
-
// poolsOffset 指向的位置没有数据
|
|
195
|
-
return '0x' + data.replace('0x', '');
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* 手动编码 DYORSwap 的 unwrapWETH9 calldata
|
|
199
|
-
*
|
|
200
|
-
* 参数格式 (根据成功交易分析):
|
|
201
|
-
* - amountMinimum: uint256
|
|
202
|
-
* - recipient: address
|
|
203
|
-
* - pools: bytes (动态) - 包含特殊的 pool 数据
|
|
204
|
-
*
|
|
205
|
-
* 成功交易的布局:
|
|
206
|
-
* [0] amountMinimum
|
|
207
|
-
* [1] recipient
|
|
208
|
-
* [2] offset to pools = 0x60 = 96 = 3 * 32
|
|
209
|
-
* [3] pools 数据 (bytes)
|
|
210
|
-
*/
|
|
211
|
-
function encodeDYORUnwrapWETH9(amountMinimum, recipient, poolsData = '' // hex bytes,可以为空
|
|
212
|
-
) {
|
|
213
|
-
const pad32 = (hex) => hex.slice(2).padStart(64, '0');
|
|
214
|
-
const toHex = (n) => n.toString(16).padStart(64, '0');
|
|
215
|
-
const poolsOffset = 3n * 32n; // 96
|
|
216
|
-
const poolsBytes = poolsData.startsWith('0x') ? poolsData.slice(2) : poolsData;
|
|
217
|
-
const poolsLength = poolsBytes.length / 2;
|
|
218
|
-
let data = DYORSWAP_SELECTORS.unwrapWETH9;
|
|
219
|
-
data += toHex(amountMinimum);
|
|
220
|
-
data += pad32(recipient);
|
|
221
|
-
data += toHex(poolsOffset);
|
|
222
|
-
data += toHex(BigInt(poolsLength));
|
|
223
|
-
// pools 数据需要 padding 到 32 字节的倍数
|
|
224
|
-
if (poolsBytes.length > 0) {
|
|
225
|
-
const paddedLength = Math.ceil(poolsBytes.length / 64) * 64;
|
|
226
|
-
data += poolsBytes.padEnd(paddedLength, '0');
|
|
227
|
-
}
|
|
228
|
-
return '0x' + data.replace('0x', '');
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* DYORSwap 的 ETHBack_Dividend 合约地址
|
|
232
|
-
* 这是 refundETH 中使用的固定地址,不是 LP 池地址
|
|
233
|
-
*/
|
|
234
|
-
const DYORSWAP_ETHBACK_DIVIDEND = '0x2e61b0b4410f6fb941d47205191cbb8963d44bcc';
|
|
235
|
-
/**
|
|
236
|
-
* 手动编码 DYORSwap 的 refundETH calldata
|
|
237
|
-
*
|
|
238
|
-
* 根据成功交易分析,refundETH 的格式是:
|
|
239
|
-
* refundETH(bytes pools)
|
|
240
|
-
*
|
|
241
|
-
* 成功交易的 refundETH (0x6d29fcf4):
|
|
242
|
-
* [0] offset to pools = 0x20 = 32
|
|
243
|
-
* [1] pools 数组长度 = 1
|
|
244
|
-
* [2] fee = 30 (0x1e)
|
|
245
|
-
* [3] ETHBack_Dividend 合约地址
|
|
246
|
-
*/
|
|
247
|
-
function encodeDYORRefundETH(fee = 30 // 默认 fee = 30 (0x1e)
|
|
248
|
-
) {
|
|
249
|
-
const toHex = (n) => n.toString(16).padStart(64, '0');
|
|
250
|
-
const pad32 = (hex) => hex.replace('0x', '').padStart(64, '0');
|
|
251
|
-
// 使用固定的 ETHBack_Dividend 合约地址
|
|
252
|
-
let data = DYORSWAP_SELECTORS.refundETH;
|
|
253
|
-
data += toHex(32n); // offset = 0x20
|
|
254
|
-
data += toHex(1n); // 数组长度 = 1
|
|
255
|
-
data += toHex(BigInt(fee)); // fee = 30
|
|
256
|
-
data += pad32(DYORSWAP_ETHBACK_DIVIDEND); // ETHBack_Dividend 地址
|
|
257
|
-
return '0x' + data.replace('0x', '');
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* 手动编码 DYORSwap 的 multicall calldata
|
|
261
|
-
*
|
|
262
|
-
* multicall(uint256 deadline, bytes[] data)
|
|
263
|
-
* 这个函数使用标准 ABI 编码
|
|
264
|
-
*/
|
|
265
|
-
function encodeDYORMulticall(deadline, data) {
|
|
266
|
-
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
|
|
267
|
-
// multicall(uint256 deadline, bytes[] data)
|
|
268
|
-
// 确保每个 data 元素都是正确的 hex 格式
|
|
269
|
-
const formattedData = data.map(d => d.startsWith('0x') ? d : '0x' + d);
|
|
270
|
-
const encoded = abiCoder.encode(['uint256', 'bytes[]'], [deadline, formattedData]).slice(2);
|
|
271
|
-
return '0x' + DYORSWAP_SELECTORS.multicall.slice(2) + encoded;
|
|
272
|
-
}
|
|
273
|
-
/** V3 SwapRouter02 ABI (PancakeSwap V3, 新版 Uniswap) */
|
|
274
|
-
const V3_ROUTER02_ABI = _V3_ROUTER02_ABI;
|
|
275
|
-
/** V3 SwapRouter(旧版)ABI (Monad Uniswap V3) */
|
|
276
|
-
const V3_ROUTER_LEGACY_ABI = _V3_ROUTER_LEGACY_ABI;
|
|
277
|
-
/**
|
|
278
|
-
* 判断是否使用旧版 SwapRouter
|
|
279
|
-
*
|
|
280
|
-
* 旧版特征:
|
|
281
|
-
* - exactInputSingle 包含 deadline 字段
|
|
282
|
-
* - multicall 只有 bytes[] 参数
|
|
283
|
-
*
|
|
284
|
-
* Monad 上的 V3 DEX 都使用旧版:
|
|
285
|
-
* - Uniswap V3: 0xd6145b2d3f379919e8cdeda7b97e37c4b2ca9c40
|
|
286
|
-
* - PancakeSwap V3: 0x1b81D678ffb9C0263b24A97847620C99d213eB14
|
|
287
|
-
*
|
|
288
|
-
* XLayer:
|
|
289
|
-
* - V3 Router (0xBB069e9465BcabC4F488d21e793BDEf0F2d41D41) - 旧版(exactInputSingle 包含 deadline)
|
|
290
|
-
* - SwapRouter02 (0xB45D0149249488333E3F3f9F359807F4b810C1FC) - 新版(V2+V3 混合)
|
|
291
|
-
*
|
|
292
|
-
* BSC PancakeSwap V3 使用新版 SwapRouter02
|
|
293
|
-
*/
|
|
294
|
-
function isLegacySwapRouter(chain, routerAddress) {
|
|
295
|
-
const chainUpper = chain.toUpperCase();
|
|
296
|
-
const routerLower = routerAddress.toLowerCase();
|
|
297
|
-
// ✅ Monad 上的所有 V3 Router 都是旧版
|
|
298
|
-
if (chainUpper === 'MONAD') {
|
|
299
|
-
if (routerLower === DIRECT_ROUTERS.MONAD.UNISWAP_V3.toLowerCase()) {
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
if (routerLower === DIRECT_ROUTERS.MONAD.PANCAKESWAP_V3.toLowerCase()) {
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
// ✅ XLayer V3 专用 Router 是旧版(exactInputSingle 包含 deadline)
|
|
307
|
-
if (chainUpper === 'XLAYER') {
|
|
308
|
-
if (routerLower === DIRECT_ROUTERS.XLAYER.V3_ROUTER.toLowerCase()) {
|
|
309
|
-
return true; // V3 Router 是旧版
|
|
310
|
-
}
|
|
311
|
-
// SwapRouter02 是新版
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
// ✅ BSC PancakeSwap V3 使用新版 SwapRouter02
|
|
315
|
-
return false;
|
|
316
|
-
}
|
|
317
|
-
// ============================================================================
|
|
318
|
-
// 工具函数
|
|
319
|
-
// ============================================================================
|
|
320
|
-
// ✅ 使用公共模块的 getDeadline
|
|
321
|
-
function getDeadline(minutes = DEADLINE_MINUTES) {
|
|
322
|
-
return _getDeadline(minutes);
|
|
323
|
-
}
|
|
324
|
-
function getGasLimit(config, defaultGas = DEFAULT_GAS_LIMIT) {
|
|
325
|
-
if (config.gasLimit !== undefined) {
|
|
326
|
-
return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
|
|
327
|
-
}
|
|
328
|
-
const multiplier = config.gasLimitMultiplier ?? 1.2;
|
|
329
|
-
return BigInt(Math.ceil(defaultGas * multiplier));
|
|
330
|
-
}
|
|
331
|
-
async function getGasPrice(provider, config) {
|
|
332
|
-
if (config.gasPrice)
|
|
333
|
-
return config.gasPrice;
|
|
334
|
-
// 转换 Gwei 配置为 Wei
|
|
335
|
-
const gasPriceConfig = {};
|
|
336
|
-
if (config.minGasPriceGwei !== undefined) {
|
|
337
|
-
gasPriceConfig.minGasPrice = ethers.parseUnits(String(config.minGasPriceGwei), 'gwei');
|
|
338
|
-
}
|
|
339
|
-
if (config.maxGasPriceGwei !== undefined) {
|
|
340
|
-
gasPriceConfig.maxGasPrice = ethers.parseUnits(String(config.maxGasPriceGwei), 'gwei');
|
|
341
|
-
}
|
|
342
|
-
return getOptimizedGasPrice(provider, gasPriceConfig);
|
|
343
|
-
}
|
|
344
|
-
function isNativeToken(quoteToken) {
|
|
345
|
-
return !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
346
|
-
}
|
|
347
|
-
// ============================================================================
|
|
348
|
-
// XLAYER V3 报价(slot0 现货价,纯 V3,不串 V2)
|
|
349
|
-
// - 只用于“利润换算”为原生币,避免 XLAYER 没有 v3Quoter 导致 profit=0
|
|
350
|
-
// - 不改 BSC 的任何配置/逻辑
|
|
351
|
-
// ============================================================================
|
|
352
|
-
const XLAYER_V3_FACTORY_ABI = [
|
|
353
|
-
'function getPool(address tokenA, address tokenB, uint24 fee) view returns (address pool)',
|
|
354
|
-
];
|
|
355
|
-
const XLAYER_V3_POOL_ABI = [
|
|
356
|
-
'function slot0() view returns (uint160 sqrtPriceX96,int24 tick,uint16 observationIndex,uint16 observationCardinality,uint16 observationCardinalityNext,uint8 feeProtocol,bool unlocked)',
|
|
357
|
-
'function token0() view returns (address)',
|
|
358
|
-
'function token1() view returns (address)',
|
|
359
|
-
];
|
|
360
|
-
const V3_FEE_DENOMINATOR = 1000000n; // UniswapV3 fee: X / 1e6
|
|
361
|
-
async function quoteXLayerV3TokenToNativeBySlot0(params) {
|
|
362
|
-
try {
|
|
363
|
-
const { provider, tokenIn, amountIn, fee } = params;
|
|
364
|
-
if (!tokenIn || amountIn <= 0n)
|
|
365
|
-
return 0n;
|
|
366
|
-
const chainFactory = DIRECT_ROUTERS.XLAYER.V3_FACTORY;
|
|
367
|
-
const wrappedNative = DIRECT_ROUTERS.XLAYER.WOKB;
|
|
368
|
-
if (!chainFactory || !wrappedNative)
|
|
369
|
-
return 0n;
|
|
370
|
-
const tokenInLower = tokenIn.toLowerCase();
|
|
371
|
-
const wrappedLower = wrappedNative.toLowerCase();
|
|
372
|
-
if (tokenInLower === wrappedLower)
|
|
373
|
-
return amountIn;
|
|
374
|
-
const factory = new Contract(chainFactory, XLAYER_V3_FACTORY_ABI, provider);
|
|
375
|
-
const poolAddr = await factory.getPool?.(tokenIn, wrappedNative, fee);
|
|
376
|
-
if (!poolAddr || poolAddr.toLowerCase() === ZERO_ADDRESS.toLowerCase())
|
|
377
|
-
return 0n;
|
|
378
|
-
const pool = new Contract(poolAddr, XLAYER_V3_POOL_ABI, provider);
|
|
379
|
-
const [t0, t1, slot0] = await Promise.all([pool.token0?.(), pool.token1?.(), pool.slot0?.()]);
|
|
380
|
-
if (!t0 || !t1 || !slot0)
|
|
381
|
-
return 0n;
|
|
382
|
-
const sqrtPriceX96 = BigInt(slot0[0]);
|
|
383
|
-
if (sqrtPriceX96 <= 0n)
|
|
384
|
-
return 0n;
|
|
385
|
-
const amountInLessFee = (amountIn * (V3_FEE_DENOMINATOR - BigInt(fee))) / V3_FEE_DENOMINATOR;
|
|
386
|
-
if (amountInLessFee <= 0n)
|
|
387
|
-
return 0n;
|
|
388
|
-
const Q192 = 2n ** 192n;
|
|
389
|
-
const num = sqrtPriceX96 * sqrtPriceX96;
|
|
390
|
-
const t0Lower = String(t0).toLowerCase();
|
|
391
|
-
const t1Lower = String(t1).toLowerCase();
|
|
392
|
-
// sqrtPriceX96 表示 token1/token0 的现货价(raw)
|
|
393
|
-
if (tokenInLower === t0Lower && wrappedLower === t1Lower) {
|
|
394
|
-
return (amountInLessFee * num) / Q192;
|
|
395
|
-
}
|
|
396
|
-
if (tokenInLower === t1Lower && wrappedLower === t0Lower) {
|
|
397
|
-
return (amountInLessFee * Q192) / num;
|
|
398
|
-
}
|
|
399
|
-
return 0n;
|
|
400
|
-
}
|
|
401
|
-
catch {
|
|
402
|
-
return 0n;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
// ✅ getTokenToNativeQuote 函数已移至 ../utils/quote-helpers.ts
|
|
406
|
-
/**
|
|
407
|
-
* ✅ 找到金额最大的钱包索引(和 core.ts 逻辑一致)
|
|
408
|
-
*/
|
|
409
|
-
function findMaxFlowIndex(amounts) {
|
|
410
|
-
if (amounts.length === 0)
|
|
411
|
-
return 0;
|
|
412
|
-
let maxIndex = 0;
|
|
413
|
-
let maxValue = amounts[0];
|
|
414
|
-
for (let i = 1; i < amounts.length; i++) {
|
|
415
|
-
if (amounts[i] > maxValue) {
|
|
416
|
-
maxValue = amounts[i];
|
|
417
|
-
maxIndex = i;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
return maxIndex;
|
|
421
|
-
}
|
|
422
|
-
/** 计算利润金额 */
|
|
423
|
-
function calculateProfitAmount(totalFlowWei) {
|
|
424
|
-
return (totalFlowWei * BigInt(PROFIT_CONFIG.RATE_BPS)) / 10000n;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* 构建利润多跳转账交易(强制 2 跳中转)
|
|
428
|
-
*/
|
|
429
|
-
async function buildProfitTransactionWithHops(provider, wallet, profitAmountWei, nonce, gasPrice, chainId, txType = 0, profitRecipient) {
|
|
430
|
-
if (profitAmountWei <= 0n)
|
|
431
|
-
return { signedTransactions: [], hopWallets: undefined };
|
|
432
|
-
const profitHopResult = await buildProfitHopTransactions({
|
|
433
|
-
provider,
|
|
434
|
-
payerWallet: wallet,
|
|
435
|
-
profitAmount: profitAmountWei,
|
|
436
|
-
profitRecipient: profitRecipient || getProfitRecipient(),
|
|
437
|
-
hopCount: PROFIT_HOP_COUNT,
|
|
438
|
-
gasPrice,
|
|
439
|
-
chainId,
|
|
440
|
-
txType,
|
|
441
|
-
startNonce: nonce
|
|
442
|
-
});
|
|
443
|
-
return { signedTransactions: profitHopResult.signedTransactions, hopWallets: profitHopResult.hopWallets };
|
|
444
|
-
}
|
|
445
|
-
/**
|
|
446
|
-
* 获取贿赂金额(wei)
|
|
447
|
-
* ✅ 仅 BSC 链支持贿赂
|
|
448
|
-
*/
|
|
449
|
-
function getBribeAmount(config, chain) {
|
|
450
|
-
// 只有 BSC 链支持贿赂
|
|
451
|
-
if (chain.toUpperCase() !== 'BSC')
|
|
452
|
-
return 0n;
|
|
453
|
-
const bribeAmount = config.bribeAmount;
|
|
454
|
-
if (typeof bribeAmount !== 'number' || bribeAmount <= 0)
|
|
455
|
-
return 0n;
|
|
456
|
-
// 转换为 wei
|
|
457
|
-
return ethers.parseEther(String(bribeAmount));
|
|
458
|
-
}
|
|
459
|
-
/** 构建贿赂交易(向 BlockRazor Builder EOA 转账 BNB) */
|
|
460
|
-
async function buildBribeTransaction(wallet, bribeAmountWei, nonce, gasPrice, chainId, txType = 0) {
|
|
461
|
-
const tx = {
|
|
462
|
-
to: BLOCKRAZOR_BUILDER_EOA,
|
|
463
|
-
value: bribeAmountWei,
|
|
464
|
-
nonce,
|
|
465
|
-
gasLimit: GAS_LIMITS.BRIBE,
|
|
466
|
-
...buildGasFields(txType, gasPrice),
|
|
467
|
-
chainId,
|
|
468
|
-
};
|
|
469
|
-
return wallet.signTransaction(tx);
|
|
470
|
-
}
|
|
471
|
-
// ============================================================================
|
|
472
|
-
// V2 直接交易
|
|
473
|
-
// ============================================================================
|
|
474
|
-
/**
|
|
475
|
-
* 判断是否是 SwapRouter02 (XLayer PotatoSwap V3)
|
|
476
|
-
*
|
|
477
|
-
* SwapRouter02 风格的 Router 使用 multicall 包装 V2 方法:
|
|
478
|
-
* - multicall(uint256 deadline, bytes[] data)
|
|
479
|
-
* - data 内包含 swapExactTokensForTokens 等 V2 方法
|
|
480
|
-
*
|
|
481
|
-
* SwapRouter02 的 V2 方法签名不同:
|
|
482
|
-
* - swapExactTokensForTokens(amountIn, amountOutMin, path[], to) - 没有 deadline
|
|
483
|
-
* - 需要通过 multicall 的 deadline 参数传递 deadline
|
|
484
|
-
*
|
|
485
|
-
* ⚠️ 注意:DYORSwap 虽然也用 multicall,但它的 ABI 完全不同,需要单独处理
|
|
486
|
-
*/
|
|
487
|
-
function isSwapRouter02(chain, routerAddress) {
|
|
488
|
-
const chainUpper = chain.toUpperCase();
|
|
489
|
-
const addrLower = routerAddress.toLowerCase();
|
|
490
|
-
if (chainUpper === 'XLAYER') {
|
|
491
|
-
// ✅ XLayer PotatoSwap SwapRouter02 (V3 风格)
|
|
492
|
-
if (addrLower === DIRECT_ROUTERS.XLAYER.POTATOSWAP_V3.toLowerCase()) {
|
|
493
|
-
return true;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
// ❌ XLayer POTATOSWAP_V2 (0x881fb...) 是标准 V2 Router,不走 SwapRouter02 逻辑
|
|
497
|
-
// ❌ XLayer DYORSWAP_ROUTER 有自己独特的 ABI,不走 SwapRouter02 逻辑
|
|
498
|
-
// ❌ BSC V2 交易使用传统 PancakeSwap V2 Router,不走 SwapRouter02 逻辑
|
|
499
|
-
// ❌ Monad V2 交易使用传统 V2 Router,不走 SwapRouter02 逻辑
|
|
500
|
-
return false;
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* 判断是否是 DYORSwap Router
|
|
504
|
-
*
|
|
505
|
-
* DYORSwap 使用 multicall,但它的 swapExactTokensForTokens 有特殊的 ABI:
|
|
506
|
-
* - swapExactTokensForTokens(amountIn, amountOutMin, path[], payer, flags, factory, pools)
|
|
507
|
-
* - unwrapWETH9(amountMinimum, recipient, pools)
|
|
508
|
-
*/
|
|
509
|
-
function isDYORSwap(chain, routerAddress) {
|
|
510
|
-
const chainUpper = chain.toUpperCase();
|
|
511
|
-
const addrLower = routerAddress.toLowerCase();
|
|
512
|
-
if (chainUpper === 'XLAYER') {
|
|
513
|
-
if (addrLower === DIRECT_ROUTERS.XLAYER.DYORSWAP_ROUTER.toLowerCase()) {
|
|
514
|
-
return true;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return false;
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* V2 批量买入(直接调用 Router)
|
|
521
|
-
*/
|
|
10
|
+
import { CHAIN_IDS, DIRECT_ROUTERS, SWAP_ROUTER02_V2_ABI, V2_ROUTER_ABI, V3_ROUTER02_ABI, V3_ROUTER_LEGACY_ABI, buildBribeTransaction, buildProfitTransactionWithHops, calculateProfitAmount, encodeDYORMulticall, encodeDYORRefundETH, encodeDYORSwapExactTokensForTokens, encodeDYORUnwrapWETH9, findMaxFlowIndex, filterEniNativeBuyIndices, getBribeAmount, getCachedProvider, getDeadline, getGasLimit, getGasPrice, getWrappedNative, isDYORSwap, isLegacySwapRouter, isNativeToken, isSwapRouter02, quoteXLayerV3TokenToNativeBySlot0, truncateDecimals, } from './direct-router-helpers.js';
|
|
11
|
+
export { DIRECT_ROUTERS } from './direct-router-helpers.js';
|
|
522
12
|
export async function directV2BatchBuy(params) {
|
|
523
13
|
const { chain, privateKeys, buyAmounts, tokenAddress, routerAddress, quoteToken, quoteTokenDecimals = 18, startNonces, config, } = params;
|
|
524
14
|
if (privateKeys.length !== buyAmounts.length) {
|
|
@@ -530,23 +20,23 @@ export async function directV2BatchBuy(params) {
|
|
|
530
20
|
const useNative = isNativeToken(quoteToken);
|
|
531
21
|
const wrappedNative = getWrappedNative(chain);
|
|
532
22
|
// 创建钱包
|
|
533
|
-
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
23
|
+
const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
|
|
534
24
|
// ✅ 预先计算所有金额(同步操作,无 RPC)
|
|
535
|
-
const flowAmounts = buyAmounts.map(amount => ethers.parseUnits(amount, quoteTokenDecimals));
|
|
25
|
+
const flowAmounts = buyAmounts.map((amount) => ethers.parseUnits(amount, quoteTokenDecimals));
|
|
536
26
|
const totalFlowWei = flowAmounts.reduce((sum, amt) => sum + amt, 0n);
|
|
537
27
|
const baseProfitWei = calculateProfitAmount(totalFlowWei);
|
|
538
28
|
// ✅ 方案 B:并行获取 nonces、gasPrice、ERC20 报价 以及(非原生代币时)授权额度
|
|
539
|
-
const quoteTokenContract =
|
|
29
|
+
const quoteTokenContract = !useNative && quoteToken ? new Contract(quoteToken, ERC20_ABI, provider) : null;
|
|
540
30
|
const [nonces, gasPrice, nativeProfitWei, buyAllowances] = await Promise.all([
|
|
541
31
|
startNonces && startNonces.length === wallets.length
|
|
542
32
|
? Promise.resolve(startNonces)
|
|
543
33
|
: new NonceManager(provider).getNextNoncesForWallets(wallets),
|
|
544
34
|
getGasPrice(provider, config),
|
|
545
|
-
|
|
35
|
+
!useNative && baseProfitWei > 0n && quoteToken
|
|
546
36
|
? getTokenToNativeQuote(provider, quoteToken, baseProfitWei, chain, 'v2')
|
|
547
37
|
: Promise.resolve(baseProfitWei),
|
|
548
38
|
quoteTokenContract
|
|
549
|
-
? Promise.all(wallets.map(w => quoteTokenContract.allowance(w.address, routerAddress).catch(() => 0n)))
|
|
39
|
+
? Promise.all(wallets.map((w) => quoteTokenContract.allowance(w.address, routerAddress).catch(() => 0n)))
|
|
550
40
|
: Promise.resolve(wallets.map(() => ethers.MaxUint256)),
|
|
551
41
|
]);
|
|
552
42
|
// 确定最终利润金额(ENI 链额外补偿利润转账的 gas 成本)
|
|
@@ -560,28 +50,8 @@ export async function directV2BatchBuy(params) {
|
|
|
560
50
|
// ENI 链原生代币买入:链上余额预检,跳过余额不足的钱包
|
|
561
51
|
let activeIndices = null;
|
|
562
52
|
if (useNative && chain.toUpperCase() === 'ENI') {
|
|
563
|
-
const eniBalances = await Promise.all(wallets.map(w => provider.getBalance(w.address)));
|
|
564
|
-
|
|
565
|
-
const profitGasCost = GAS_LIMITS.NATIVE_TRANSFER * gasPrice;
|
|
566
|
-
const validIdx = [];
|
|
567
|
-
for (let i = 0; i < wallets.length; i++) {
|
|
568
|
-
let required = flowAmounts[i] + BigInt(gasLimit) * gasPrice;
|
|
569
|
-
if (i === maxFlowIdx && profitWei > 0n) {
|
|
570
|
-
required += profitWei + profitGasCost;
|
|
571
|
-
}
|
|
572
|
-
if (eniBalances[i] < required) {
|
|
573
|
-
console.warn(`[V2 BatchBuy ENI] 钱包 ${wallets[i].address.slice(0, 10)} 余额不足: 余额=${eniBalances[i]}, 需要=${required},跳过`);
|
|
574
|
-
continue;
|
|
575
|
-
}
|
|
576
|
-
validIdx.push(i);
|
|
577
|
-
}
|
|
578
|
-
if (validIdx.length === 0) {
|
|
579
|
-
throw new Error('[V2 BatchBuy ENI] 所有钱包余额不足(含 swap + gas + 利润费),无法执行');
|
|
580
|
-
}
|
|
581
|
-
if (validIdx.length < wallets.length) {
|
|
582
|
-
console.log(`[V2 BatchBuy ENI] 余额预检: ${wallets.length - validIdx.length} 个钱包被跳过, 剩余 ${validIdx.length} 个`);
|
|
583
|
-
activeIndices = validIdx;
|
|
584
|
-
}
|
|
53
|
+
const eniBalances = await Promise.all(wallets.map((w) => provider.getBalance(w.address)));
|
|
54
|
+
activeIndices = filterEniNativeBuyIndices(eniBalances, flowAmounts, gasLimit, gasPrice, profitWei);
|
|
585
55
|
}
|
|
586
56
|
// 如果有钱包被跳过,重建数组(仅 ENI)
|
|
587
57
|
let activeWallets = wallets;
|
|
@@ -589,10 +59,10 @@ export async function directV2BatchBuy(params) {
|
|
|
589
59
|
let activeNonces = nonces;
|
|
590
60
|
let activeBuyAllowances = buyAllowances;
|
|
591
61
|
if (activeIndices) {
|
|
592
|
-
activeWallets = activeIndices.map(i => wallets[i]);
|
|
593
|
-
activeFlowAmounts = activeIndices.map(i => flowAmounts[i]);
|
|
594
|
-
activeNonces = activeIndices.map(i => nonces[i]);
|
|
595
|
-
activeBuyAllowances = activeIndices.map(i => buyAllowances[i]);
|
|
62
|
+
activeWallets = activeIndices.map((i) => wallets[i]);
|
|
63
|
+
activeFlowAmounts = activeIndices.map((i) => flowAmounts[i]);
|
|
64
|
+
activeNonces = activeIndices.map((i) => nonces[i]);
|
|
65
|
+
activeBuyAllowances = activeIndices.map((i) => buyAllowances[i]);
|
|
596
66
|
}
|
|
597
67
|
const activeTotalFlowWei = activeFlowAmounts.reduce((sum, amt) => sum + amt, 0n);
|
|
598
68
|
const activeBaseProfitWei = calculateProfitAmount(activeTotalFlowWei);
|
|
@@ -610,8 +80,10 @@ export async function directV2BatchBuy(params) {
|
|
|
610
80
|
if (activeFlowAmounts[i] <= 0n)
|
|
611
81
|
continue;
|
|
612
82
|
if (activeBuyAllowances[i] < activeFlowAmounts[i]) {
|
|
613
|
-
|
|
614
|
-
|
|
83
|
+
const approveData = quoteTokenContract.interface.encodeFunctionData('approve', [
|
|
84
|
+
routerAddress,
|
|
85
|
+
ethers.MaxUint256,
|
|
86
|
+
]);
|
|
615
87
|
const approveTx = await activeWallets[i].signTransaction({
|
|
616
88
|
to: quoteToken,
|
|
617
89
|
data: approveData,
|
|
@@ -645,14 +117,39 @@ export async function directV2BatchBuy(params) {
|
|
|
645
117
|
return { txData: encodeDYORMulticall(BigInt(deadline), multicallData), txValue: useNative ? amountWei : 0n };
|
|
646
118
|
}
|
|
647
119
|
else if (useSwapRouter02) {
|
|
648
|
-
const swapData = routerIface.encodeFunctionData('swapExactTokensForTokens', [
|
|
649
|
-
|
|
120
|
+
const swapData = routerIface.encodeFunctionData('swapExactTokensForTokens', [
|
|
121
|
+
amountWei,
|
|
122
|
+
0n,
|
|
123
|
+
path,
|
|
124
|
+
wallet.address,
|
|
125
|
+
]);
|
|
126
|
+
return {
|
|
127
|
+
txData: routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, [swapData]]),
|
|
128
|
+
txValue: useNative ? amountWei : 0n,
|
|
129
|
+
};
|
|
650
130
|
}
|
|
651
131
|
else if (useNative) {
|
|
652
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
txData: routerIface.encodeFunctionData('swapExactETHForTokensSupportingFeeOnTransferTokens', [
|
|
134
|
+
0n,
|
|
135
|
+
path,
|
|
136
|
+
wallet.address,
|
|
137
|
+
deadline,
|
|
138
|
+
]),
|
|
139
|
+
txValue: amountWei,
|
|
140
|
+
};
|
|
653
141
|
}
|
|
654
142
|
else {
|
|
655
|
-
return {
|
|
143
|
+
return {
|
|
144
|
+
txData: routerIface.encodeFunctionData('swapExactTokensForTokensSupportingFeeOnTransferTokens', [
|
|
145
|
+
amountWei,
|
|
146
|
+
0n,
|
|
147
|
+
path,
|
|
148
|
+
wallet.address,
|
|
149
|
+
deadline,
|
|
150
|
+
]),
|
|
151
|
+
txValue: 0n,
|
|
152
|
+
};
|
|
656
153
|
}
|
|
657
154
|
};
|
|
658
155
|
// 选择金额最大的钱包支付贿赂和利润
|
|
@@ -662,18 +159,18 @@ export async function directV2BatchBuy(params) {
|
|
|
662
159
|
const hasBribe = bribeWei > 0n && activeWallets.length > 0;
|
|
663
160
|
const hasProfit = activeProfitWei > 0n;
|
|
664
161
|
// 计算 nonce 偏移
|
|
665
|
-
const nonceOffsets = activeWallets.map((_, i) => i === maxFlowIndex && hasBribe ? 1 : 0);
|
|
162
|
+
const nonceOffsets = activeWallets.map((_, i) => (i === maxFlowIndex && hasBribe ? 1 : 0));
|
|
666
163
|
// ✅ 方案 A:并行签名所有交易(贿赂、主交易、利润)
|
|
667
164
|
const signPromises = [];
|
|
668
165
|
// 贿赂交易
|
|
669
166
|
if (hasBribe) {
|
|
670
|
-
signPromises.push(buildBribeTransaction(activeWallets[maxFlowIndex], bribeWei, activeNonces[maxFlowIndex], gasPrice, chainId, txType)
|
|
671
|
-
.then(tx => ({ type: 'bribe', index: 0, tx })));
|
|
167
|
+
signPromises.push(buildBribeTransaction(activeWallets[maxFlowIndex], bribeWei, activeNonces[maxFlowIndex], gasPrice, chainId, txType).then((tx) => ({ type: 'bribe', index: 0, tx })));
|
|
672
168
|
}
|
|
673
169
|
// 主交易(并行签名)
|
|
674
170
|
activeWallets.forEach((wallet, i) => {
|
|
675
171
|
const { txData, txValue } = buildTxData(wallet, activeFlowAmounts[i]);
|
|
676
|
-
signPromises.push(wallet
|
|
172
|
+
signPromises.push(wallet
|
|
173
|
+
.signTransaction({
|
|
677
174
|
to: routerAddress,
|
|
678
175
|
data: txData,
|
|
679
176
|
value: txValue,
|
|
@@ -681,20 +178,23 @@ export async function directV2BatchBuy(params) {
|
|
|
681
178
|
gasLimit,
|
|
682
179
|
...buildGasFields(txType, gasPrice),
|
|
683
180
|
chainId,
|
|
684
|
-
})
|
|
181
|
+
})
|
|
182
|
+
.then((tx) => ({ type: 'swap', index: i, tx })));
|
|
685
183
|
});
|
|
686
184
|
// ✅ 并行执行所有签名
|
|
687
185
|
const signedResults = await Promise.all(signPromises);
|
|
688
186
|
// 按类型分组并按顺序组装:approve → 贿赂 → 交易
|
|
689
|
-
const approveSignedTxs = buyApproveTxs.sort((a, b) => a.walletIndex - b.walletIndex).map(a => a.tx);
|
|
690
|
-
const bribeTxs = signedResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
691
|
-
const swapTxs = signedResults
|
|
187
|
+
const approveSignedTxs = buyApproveTxs.sort((a, b) => a.walletIndex - b.walletIndex).map((a) => a.tx);
|
|
188
|
+
const bribeTxs = signedResults.filter((r) => r.type === 'bribe').map((r) => r.tx);
|
|
189
|
+
const swapTxs = signedResults
|
|
190
|
+
.filter((r) => r.type === 'swap')
|
|
191
|
+
.sort((a, b) => a.index - b.index)
|
|
192
|
+
.map((r) => r.tx);
|
|
692
193
|
const signedTxs = [...approveSignedTxs, ...bribeTxs, ...swapTxs];
|
|
693
194
|
// ✅ 检查是否使用分布式利润模式
|
|
694
195
|
const profitMode = config.profitMode || 'single';
|
|
695
196
|
const skipProfit = config.skipProfit === true;
|
|
696
197
|
const profitAddr = getProfitRecipient();
|
|
697
|
-
console.log('🔧 [SDK directV2BatchBuy] profitMode:', profitMode, 'skipProfit:', skipProfit, 'wallets:', activeWallets.length);
|
|
698
198
|
// 利润多跳转账
|
|
699
199
|
let profitHopWallets;
|
|
700
200
|
if (hasProfit && !skipProfit) {
|
|
@@ -707,9 +207,7 @@ export async function directV2BatchBuy(params) {
|
|
|
707
207
|
profitHopWallets = [];
|
|
708
208
|
// 计算每个钱包的利润(按比例分配)
|
|
709
209
|
for (let i = 0; i < activeWallets.length; i++) {
|
|
710
|
-
const walletProfit = activeTotalFlowWei > 0n
|
|
711
|
-
? (activeProfitWei * activeFlowAmounts[i]) / activeTotalFlowWei
|
|
712
|
-
: 0n;
|
|
210
|
+
const walletProfit = activeTotalFlowWei > 0n ? (activeProfitWei * activeFlowAmounts[i]) / activeTotalFlowWei : 0n;
|
|
713
211
|
if (walletProfit > 0n) {
|
|
714
212
|
const walletProfitNonce = activeNonces[i] + nonceOffsets[i] + 1;
|
|
715
213
|
const profitResult = await buildProfitHopTransactions({
|
|
@@ -721,7 +219,7 @@ export async function directV2BatchBuy(params) {
|
|
|
721
219
|
gasPrice,
|
|
722
220
|
chainId,
|
|
723
221
|
txType,
|
|
724
|
-
startNonce: walletProfitNonce
|
|
222
|
+
startNonce: walletProfitNonce,
|
|
725
223
|
});
|
|
726
224
|
signedTxs.push(...profitResult.signedTransactions);
|
|
727
225
|
if (profitResult.hopWallets) {
|
|
@@ -760,19 +258,22 @@ export async function directV2BatchSell(params) {
|
|
|
760
258
|
const useNativeOutput = isNativeToken(quoteToken);
|
|
761
259
|
const wrappedNative = getWrappedNative(chain);
|
|
762
260
|
// 创建钱包
|
|
763
|
-
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
261
|
+
const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
|
|
764
262
|
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
765
263
|
// ✅ 性能优化:并行获取所有独立的 RPC 数据(包括授权额度)
|
|
766
264
|
const [fetchedDecimals, balances, allowances, nonces, gasPrice] = await Promise.all([
|
|
767
265
|
inputDecimals !== undefined
|
|
768
266
|
? Promise.resolve(inputDecimals)
|
|
769
|
-
: tokenContract
|
|
770
|
-
|
|
771
|
-
|
|
267
|
+
: tokenContract
|
|
268
|
+
.decimals()
|
|
269
|
+
.then((d) => Number(d))
|
|
270
|
+
.catch(() => 18),
|
|
271
|
+
Promise.all(wallets.map((w) => tokenContract.balanceOf(w.address))),
|
|
272
|
+
Promise.all(wallets.map((w) => tokenContract.allowance(w.address, routerAddress).catch(() => 0n))),
|
|
772
273
|
startNonces && startNonces.length === wallets.length
|
|
773
274
|
? Promise.resolve(startNonces)
|
|
774
275
|
: new NonceManager(provider).getNextNoncesForWallets(wallets),
|
|
775
|
-
getGasPrice(provider, config)
|
|
276
|
+
getGasPrice(provider, config),
|
|
776
277
|
]);
|
|
777
278
|
const tokenDecimals = fetchedDecimals;
|
|
778
279
|
const gasLimit = getGasLimit(config, 300000);
|
|
@@ -794,33 +295,17 @@ export async function directV2BatchSell(params) {
|
|
|
794
295
|
amount = balances[i];
|
|
795
296
|
}
|
|
796
297
|
if (amount > balances[i]) {
|
|
797
|
-
console.log(`⚠️ [V2 Sell] 钱包 ${i} 卖出量(${amount}) > 链上余额(${balances[i]}),已自动调整为实际余额`);
|
|
798
298
|
amount = balances[i];
|
|
799
299
|
}
|
|
800
300
|
sellAmountsWei.push(amount);
|
|
801
301
|
}
|
|
802
302
|
const totalSellAmount = sellAmountsWei.reduce((sum, o) => sum + o, 0n);
|
|
803
|
-
// ENI 链卖出:检查 maxPayer 的原生余额是否足够覆盖 gas(swap gas + profit hop gas)
|
|
804
|
-
if (chain.toUpperCase() === 'ENI') {
|
|
805
|
-
const maxSellIdx = findMaxFlowIndex(sellAmountsWei);
|
|
806
|
-
const nativeBalances = await Promise.all(wallets.map(w => provider.getBalance(w.address)));
|
|
807
|
-
for (let i = 0; i < wallets.length; i++) {
|
|
808
|
-
let requiredGas = BigInt(gasLimit) * gasPrice;
|
|
809
|
-
if (i === maxSellIdx) {
|
|
810
|
-
requiredGas += GAS_LIMITS.NATIVE_TRANSFER * gasPrice;
|
|
811
|
-
}
|
|
812
|
-
if (nativeBalances[i] < requiredGas) {
|
|
813
|
-
console.warn(`[V2 BatchSell ENI] 钱包 ${wallets[i].address.slice(0, 10)} 原生余额不足 gas: 余额=${nativeBalances[i]}, 需要=${requiredGas},交易可能失败`);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
303
|
// ✅ 自动授权:检查每个钱包对 Router 的授权额度,不足则插入 approve 交易
|
|
818
304
|
const approveTxs = [];
|
|
819
305
|
for (let i = 0; i < wallets.length; i++) {
|
|
820
306
|
if (sellAmountsWei[i] <= 0n)
|
|
821
307
|
continue;
|
|
822
308
|
if (allowances[i] < sellAmountsWei[i]) {
|
|
823
|
-
console.log(`🔓 [V2 Sell] 钱包 ${i} 授权不足 (${allowances[i]} < ${sellAmountsWei[i]}), 自动 approve`);
|
|
824
309
|
const approveData = tokenContract.interface.encodeFunctionData('approve', [routerAddress, ethers.MaxUint256]);
|
|
825
310
|
const approveTx = await wallets[i].signTransaction({
|
|
826
311
|
to: tokenAddress,
|
|
@@ -842,11 +327,9 @@ export async function directV2BatchSell(params) {
|
|
|
842
327
|
if (useNativeOutput) {
|
|
843
328
|
// 卖出代币 → 得到 BNB:先获取 V2 报价,再计算利润(✅ 使用当前 Router 报价,IROSwap 池需用 IROSwap Router)
|
|
844
329
|
const estimatedBNBOut = await getTokenToNativeQuote(provider, tokenAddress, totalSellAmount, chain, 'v2', undefined, routerAddress);
|
|
845
|
-
console.log(`[V2 Sell Profit] totalSellAmount: ${totalSellAmount}, estimatedBNBOut: ${estimatedBNBOut}, estimatedBNB: ${ethers.formatEther(estimatedBNBOut)}`);
|
|
846
330
|
if (estimatedBNBOut <= 0n)
|
|
847
331
|
return 0n;
|
|
848
332
|
const profit = calculateProfitAmount(estimatedBNBOut);
|
|
849
|
-
console.log(`[V2 Sell Profit] profit: ${profit}, profitBNB: ${ethers.formatEther(profit)}`);
|
|
850
333
|
return profit;
|
|
851
334
|
}
|
|
852
335
|
else if (quoteToken) {
|
|
@@ -857,13 +340,10 @@ export async function directV2BatchSell(params) {
|
|
|
857
340
|
const step1 = await quoteV2(provider, tokenAddress, quoteToken, totalSellAmount, chain, routerAddress);
|
|
858
341
|
if (!step1.amountOut || step1.amountOut <= 0n)
|
|
859
342
|
return 0n;
|
|
860
|
-
console.log(`[V2 Sell Profit] step1 token→quoteToken: ${step1.amountOut} wei`);
|
|
861
343
|
const estimatedBNBOut = await getTokenToNativeQuote(provider, quoteToken, step1.amountOut, chain, 'v2');
|
|
862
|
-
console.log(`[V2 Sell Profit] step2 quoteToken→BNB: ${estimatedBNBOut} wei`);
|
|
863
344
|
if (estimatedBNBOut <= 0n)
|
|
864
345
|
return 0n;
|
|
865
346
|
const profit = calculateProfitAmount(estimatedBNBOut);
|
|
866
|
-
console.log(`[V2 Sell Profit] profit: ${profit}, profitBNB: ${ethers.formatEther(profit)}`);
|
|
867
347
|
return profit;
|
|
868
348
|
}
|
|
869
349
|
return 0n;
|
|
@@ -878,7 +358,9 @@ export async function directV2BatchSell(params) {
|
|
|
878
358
|
const path = [tokenAddress, outputToken];
|
|
879
359
|
const useSwapRouter02 = isSwapRouter02(chain, routerAddress);
|
|
880
360
|
const useDYORSwap = isDYORSwap(chain, routerAddress);
|
|
881
|
-
const routerIface = useSwapRouter02
|
|
361
|
+
const routerIface = useSwapRouter02
|
|
362
|
+
? new ethers.Interface(SWAP_ROUTER02_V2_ABI)
|
|
363
|
+
: new ethers.Interface(V2_ROUTER_ABI);
|
|
882
364
|
// 构建卖出交易数据的辅助函数
|
|
883
365
|
const buildSellTxData = (wallet, sellAmount) => {
|
|
884
366
|
if (useDYORSwap) {
|
|
@@ -900,32 +382,47 @@ export async function directV2BatchSell(params) {
|
|
|
900
382
|
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, multicallData]);
|
|
901
383
|
}
|
|
902
384
|
else {
|
|
903
|
-
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
385
|
+
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
386
|
+
deadline,
|
|
387
|
+
[routerIface.encodeFunctionData('swapExactTokensForTokens', [sellAmount, 0n, path, wallet.address])],
|
|
388
|
+
]);
|
|
904
389
|
}
|
|
905
390
|
}
|
|
906
391
|
else if (useNativeOutput) {
|
|
907
|
-
return routerIface.encodeFunctionData('swapExactTokensForETHSupportingFeeOnTransferTokens', [
|
|
392
|
+
return routerIface.encodeFunctionData('swapExactTokensForETHSupportingFeeOnTransferTokens', [
|
|
393
|
+
sellAmount,
|
|
394
|
+
0n,
|
|
395
|
+
path,
|
|
396
|
+
wallet.address,
|
|
397
|
+
deadline,
|
|
398
|
+
]);
|
|
908
399
|
}
|
|
909
400
|
else {
|
|
910
|
-
return routerIface.encodeFunctionData('swapExactTokensForTokensSupportingFeeOnTransferTokens', [
|
|
401
|
+
return routerIface.encodeFunctionData('swapExactTokensForTokensSupportingFeeOnTransferTokens', [
|
|
402
|
+
sellAmount,
|
|
403
|
+
0n,
|
|
404
|
+
path,
|
|
405
|
+
wallet.address,
|
|
406
|
+
deadline,
|
|
407
|
+
]);
|
|
911
408
|
}
|
|
912
409
|
};
|
|
913
410
|
const maxOutputIndex = findMaxFlowIndex(sellAmountsWei);
|
|
914
411
|
const bribeWei = getBribeAmount(config, chain);
|
|
915
412
|
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
916
|
-
const nonceOffsets = wallets.map((_, i) => i === maxOutputIndex && hasBribe ? 1 : 0);
|
|
413
|
+
const nonceOffsets = wallets.map((_, i) => (i === maxOutputIndex && hasBribe ? 1 : 0));
|
|
917
414
|
// ✅ 方案 A:并行签名所有交易(贿赂、卖出、利润)+ 并行获取 ERC20 报价
|
|
918
415
|
const signPromises = [];
|
|
919
416
|
// 贿赂交易
|
|
920
417
|
if (hasBribe) {
|
|
921
|
-
signPromises.push(buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], gasPrice, chainId, txType)
|
|
922
|
-
.then(tx => ({ type: 'bribe', index: 0, tx })));
|
|
418
|
+
signPromises.push(buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], gasPrice, chainId, txType).then((tx) => ({ type: 'bribe', index: 0, tx })));
|
|
923
419
|
}
|
|
924
420
|
// 卖出交易(并行签名)
|
|
925
421
|
wallets.forEach((wallet, i) => {
|
|
926
422
|
if (sellAmountsWei[i] <= 0n)
|
|
927
423
|
return;
|
|
928
|
-
signPromises.push(wallet
|
|
424
|
+
signPromises.push(wallet
|
|
425
|
+
.signTransaction({
|
|
929
426
|
to: routerAddress,
|
|
930
427
|
data: buildSellTxData(wallet, sellAmountsWei[i]),
|
|
931
428
|
value: 0n,
|
|
@@ -933,13 +430,11 @@ export async function directV2BatchSell(params) {
|
|
|
933
430
|
gasLimit,
|
|
934
431
|
...buildGasFields(txType, gasPrice),
|
|
935
432
|
chainId,
|
|
936
|
-
})
|
|
433
|
+
})
|
|
434
|
+
.then((tx) => ({ type: 'swap', index: i, tx })));
|
|
937
435
|
});
|
|
938
436
|
// ✅ 并行执行:签名 + ERC20 报价
|
|
939
|
-
const [signedResults, nativeProfitWei] = await Promise.all([
|
|
940
|
-
Promise.all(signPromises),
|
|
941
|
-
nativeProfitPromise
|
|
942
|
-
]);
|
|
437
|
+
const [signedResults, nativeProfitWei] = await Promise.all([Promise.all(signPromises), nativeProfitPromise]);
|
|
943
438
|
let profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
|
|
944
439
|
if (profitWei > 0n && chain.toUpperCase() === 'ENI') {
|
|
945
440
|
profitWei += GAS_LIMITS.NATIVE_TRANSFER * gasPrice;
|
|
@@ -948,7 +443,6 @@ export async function directV2BatchSell(params) {
|
|
|
948
443
|
const profitMode = config.profitMode || 'single';
|
|
949
444
|
const skipProfit = config.skipProfit === true;
|
|
950
445
|
const profitAddr = getProfitRecipient();
|
|
951
|
-
console.log('🔧 [SDK directV2BatchSell] profitMode:', profitMode, 'skipProfit:', skipProfit, 'wallets:', wallets.length);
|
|
952
446
|
// 利润多跳转账
|
|
953
447
|
let profitTxs = [];
|
|
954
448
|
let profitHopWallets;
|
|
@@ -962,9 +456,7 @@ export async function directV2BatchSell(params) {
|
|
|
962
456
|
profitHopWallets = [];
|
|
963
457
|
// 计算每个钱包的利润(按卖出金额比例分配)
|
|
964
458
|
for (let i = 0; i < wallets.length; i++) {
|
|
965
|
-
const walletProfit = totalSellAmount > 0n
|
|
966
|
-
? (profitWei * sellAmountsWei[i]) / totalSellAmount
|
|
967
|
-
: 0n;
|
|
459
|
+
const walletProfit = totalSellAmount > 0n ? (profitWei * sellAmountsWei[i]) / totalSellAmount : 0n;
|
|
968
460
|
if (walletProfit > 0n) {
|
|
969
461
|
const walletProfitNonce = nonces[i] + nonceOffsets[i] + 1;
|
|
970
462
|
const profitResult = await buildProfitHopTransactions({
|
|
@@ -976,7 +468,7 @@ export async function directV2BatchSell(params) {
|
|
|
976
468
|
gasPrice,
|
|
977
469
|
chainId,
|
|
978
470
|
txType,
|
|
979
|
-
startNonce: walletProfitNonce
|
|
471
|
+
startNonce: walletProfitNonce,
|
|
980
472
|
});
|
|
981
473
|
profitTxs.push(...profitResult.signedTransactions);
|
|
982
474
|
if (profitResult.hopWallets) {
|
|
@@ -995,9 +487,12 @@ export async function directV2BatchSell(params) {
|
|
|
995
487
|
}
|
|
996
488
|
// 按类型分组并按顺序组装:approve → bribe → swap → profit
|
|
997
489
|
const validResults = signedResults.filter((r) => r !== null);
|
|
998
|
-
const bribeTxs = validResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
999
|
-
const swapTxs = validResults
|
|
1000
|
-
|
|
490
|
+
const bribeTxs = validResults.filter((r) => r.type === 'bribe').map((r) => r.tx);
|
|
491
|
+
const swapTxs = validResults
|
|
492
|
+
.filter((r) => r.type === 'swap')
|
|
493
|
+
.sort((a, b) => a.index - b.index)
|
|
494
|
+
.map((r) => r.tx);
|
|
495
|
+
const approveSignedTxs = approveTxs.sort((a, b) => a.walletIndex - b.walletIndex).map((a) => a.tx);
|
|
1001
496
|
const signedTxs = [...approveSignedTxs, ...bribeTxs, ...swapTxs, ...profitTxs];
|
|
1002
497
|
return {
|
|
1003
498
|
signedTransactions: signedTxs,
|
|
@@ -1032,9 +527,9 @@ export async function directV3BatchBuy(params) {
|
|
|
1032
527
|
const wrappedNative = getWrappedNative(chain);
|
|
1033
528
|
const useLegacyRouter = isLegacySwapRouter(chain, routerAddress);
|
|
1034
529
|
const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
|
|
1035
|
-
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
530
|
+
const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
|
|
1036
531
|
// ✅ 预先计算所有金额(同步操作,无 RPC)
|
|
1037
|
-
const flowAmounts = buyAmounts.map(amount => ethers.parseUnits(amount, quoteTokenDecimals));
|
|
532
|
+
const flowAmounts = buyAmounts.map((amount) => ethers.parseUnits(amount, quoteTokenDecimals));
|
|
1038
533
|
const totalFlowWei = flowAmounts.reduce((sum, amt) => sum + amt, 0n);
|
|
1039
534
|
const baseProfitWei = calculateProfitAmount(totalFlowWei);
|
|
1040
535
|
// ✅ 方案 B:并行获取 nonces、gasPrice 和 ERC20 报价
|
|
@@ -1044,11 +539,11 @@ export async function directV3BatchBuy(params) {
|
|
|
1044
539
|
: new NonceManager(provider).getNextNoncesForWallets(wallets),
|
|
1045
540
|
getGasPrice(provider, config),
|
|
1046
541
|
// ERC20 报价(V3 买入用 V3 报价)
|
|
1047
|
-
|
|
1048
|
-
?
|
|
542
|
+
!useNative && baseProfitWei > 0n && quoteToken
|
|
543
|
+
? chain.toUpperCase() === 'XLAYER'
|
|
1049
544
|
? quoteXLayerV3TokenToNativeBySlot0({ provider, tokenIn: quoteToken, amountIn: baseProfitWei, fee })
|
|
1050
|
-
: getTokenToNativeQuote(provider, quoteToken, baseProfitWei, chain, 'v3', fee)
|
|
1051
|
-
: Promise.resolve(baseProfitWei)
|
|
545
|
+
: getTokenToNativeQuote(provider, quoteToken, baseProfitWei, chain, 'v3', fee)
|
|
546
|
+
: Promise.resolve(baseProfitWei),
|
|
1052
547
|
]);
|
|
1053
548
|
let profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
|
|
1054
549
|
if (profitWei > 0n && chain.toUpperCase() === 'ENI') {
|
|
@@ -1062,51 +557,87 @@ export async function directV3BatchBuy(params) {
|
|
|
1062
557
|
// 构建交易数据的辅助函数
|
|
1063
558
|
const buildV3BuyTxData = (wallet, amountWei) => {
|
|
1064
559
|
if (useLegacyRouter) {
|
|
1065
|
-
const swapParams = {
|
|
560
|
+
const swapParams = {
|
|
561
|
+
tokenIn: inputToken,
|
|
562
|
+
tokenOut: tokenAddress,
|
|
563
|
+
fee,
|
|
564
|
+
recipient: wallet.address,
|
|
565
|
+
deadline,
|
|
566
|
+
amountIn: amountWei,
|
|
567
|
+
amountOutMinimum: 0n,
|
|
568
|
+
sqrtPriceLimitX96: 0n,
|
|
569
|
+
};
|
|
1066
570
|
if (useNative) {
|
|
1067
571
|
const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
|
|
1068
572
|
const refundData = routerIface.encodeFunctionData('refundETH', []);
|
|
1069
|
-
return {
|
|
573
|
+
return {
|
|
574
|
+
txData: routerIface.encodeFunctionData('multicall(bytes[])', [[swapData, refundData]]),
|
|
575
|
+
txValue: amountWei,
|
|
576
|
+
};
|
|
1070
577
|
}
|
|
1071
578
|
return { txData: routerIface.encodeFunctionData('exactInputSingle', [swapParams]), txValue: 0n };
|
|
1072
579
|
}
|
|
1073
580
|
else {
|
|
1074
|
-
const swapParams = {
|
|
581
|
+
const swapParams = {
|
|
582
|
+
tokenIn: inputToken,
|
|
583
|
+
tokenOut: tokenAddress,
|
|
584
|
+
fee,
|
|
585
|
+
recipient: wallet.address,
|
|
586
|
+
amountIn: amountWei,
|
|
587
|
+
amountOutMinimum: 0n,
|
|
588
|
+
sqrtPriceLimitX96: 0n,
|
|
589
|
+
};
|
|
1075
590
|
if (useNative) {
|
|
1076
591
|
const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
|
|
1077
592
|
const refundData = routerIface.encodeFunctionData('refundETH', []);
|
|
1078
|
-
return {
|
|
593
|
+
return {
|
|
594
|
+
txData: routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, [swapData, refundData]]),
|
|
595
|
+
txValue: amountWei,
|
|
596
|
+
};
|
|
1079
597
|
}
|
|
1080
598
|
// ✅ 修复:ERC20 买入也需要使用 multicall 包装以传递 deadline
|
|
1081
599
|
// SwapRouter02 的 exactInputSingle 不包含 deadline 参数,必须通过 multicall 传递
|
|
1082
600
|
const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
|
|
1083
|
-
return {
|
|
601
|
+
return {
|
|
602
|
+
txData: routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, [swapData]]),
|
|
603
|
+
txValue: 0n,
|
|
604
|
+
};
|
|
1084
605
|
}
|
|
1085
606
|
};
|
|
1086
607
|
const maxFlowIndex = findMaxFlowIndex(flowAmounts);
|
|
1087
608
|
const bribeWei = getBribeAmount(config, chain);
|
|
1088
609
|
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
1089
610
|
const hasProfit = profitWei > 0n;
|
|
1090
|
-
const nonceOffsets = wallets.map((_, i) => i === maxFlowIndex && hasBribe ? 1 : 0);
|
|
611
|
+
const nonceOffsets = wallets.map((_, i) => (i === maxFlowIndex && hasBribe ? 1 : 0));
|
|
1091
612
|
// ✅ 方案 A:并行签名所有交易(贿赂、主交易、利润)
|
|
1092
613
|
const signPromises = [];
|
|
1093
614
|
if (hasBribe) {
|
|
1094
|
-
signPromises.push(buildBribeTransaction(wallets[maxFlowIndex], bribeWei, nonces[maxFlowIndex], gasPrice, chainId, txType)
|
|
1095
|
-
.then(tx => ({ type: 'bribe', index: 0, tx })));
|
|
615
|
+
signPromises.push(buildBribeTransaction(wallets[maxFlowIndex], bribeWei, nonces[maxFlowIndex], gasPrice, chainId, txType).then((tx) => ({ type: 'bribe', index: 0, tx })));
|
|
1096
616
|
}
|
|
1097
617
|
wallets.forEach((wallet, i) => {
|
|
1098
618
|
const { txData, txValue } = buildV3BuyTxData(wallet, flowAmounts[i]);
|
|
1099
|
-
signPromises.push(wallet
|
|
1100
|
-
.
|
|
619
|
+
signPromises.push(wallet
|
|
620
|
+
.signTransaction({
|
|
621
|
+
to: routerAddress,
|
|
622
|
+
data: txData,
|
|
623
|
+
value: txValue,
|
|
624
|
+
nonce: nonces[i] + nonceOffsets[i],
|
|
625
|
+
gasLimit,
|
|
626
|
+
...buildGasFields(txType, gasPrice),
|
|
627
|
+
chainId,
|
|
628
|
+
})
|
|
629
|
+
.then((tx) => ({ type: 'swap', index: i, tx })));
|
|
1101
630
|
});
|
|
1102
631
|
const signedResults = await Promise.all(signPromises);
|
|
1103
|
-
const bribeTxs = signedResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
1104
|
-
const swapTxs = signedResults
|
|
632
|
+
const bribeTxs = signedResults.filter((r) => r.type === 'bribe').map((r) => r.tx);
|
|
633
|
+
const swapTxs = signedResults
|
|
634
|
+
.filter((r) => r.type === 'swap')
|
|
635
|
+
.sort((a, b) => a.index - b.index)
|
|
636
|
+
.map((r) => r.tx);
|
|
1105
637
|
// ✅ 检查是否使用分布式利润模式
|
|
1106
638
|
const profitMode = config.profitMode || 'single';
|
|
1107
639
|
const skipProfit = config.skipProfit === true;
|
|
1108
640
|
const profitAddr = getProfitRecipient();
|
|
1109
|
-
console.log('🔧 [SDK directV3BatchBuy] profitMode:', profitMode, 'skipProfit:', skipProfit, 'wallets:', wallets.length);
|
|
1110
641
|
// 利润多跳转账
|
|
1111
642
|
let profitTxs = [];
|
|
1112
643
|
let profitHopWallets;
|
|
@@ -1120,9 +651,7 @@ export async function directV3BatchBuy(params) {
|
|
|
1120
651
|
profitHopWallets = [];
|
|
1121
652
|
// 计算每个钱包的利润(按比例分配)
|
|
1122
653
|
for (let i = 0; i < wallets.length; i++) {
|
|
1123
|
-
const walletProfit = totalFlowWei > 0n
|
|
1124
|
-
? (profitWei * flowAmounts[i]) / totalFlowWei
|
|
1125
|
-
: 0n;
|
|
654
|
+
const walletProfit = totalFlowWei > 0n ? (profitWei * flowAmounts[i]) / totalFlowWei : 0n;
|
|
1126
655
|
if (walletProfit > 0n) {
|
|
1127
656
|
const walletProfitNonce = nonces[i] + nonceOffsets[i] + 1;
|
|
1128
657
|
const profitResult = await buildProfitHopTransactions({
|
|
@@ -1134,7 +663,7 @@ export async function directV3BatchBuy(params) {
|
|
|
1134
663
|
gasPrice,
|
|
1135
664
|
chainId,
|
|
1136
665
|
txType,
|
|
1137
|
-
startNonce: walletProfitNonce
|
|
666
|
+
startNonce: walletProfitNonce,
|
|
1138
667
|
});
|
|
1139
668
|
profitTxs.push(...profitResult.signedTransactions);
|
|
1140
669
|
if (profitResult.hopWallets) {
|
|
@@ -1179,15 +708,15 @@ export async function directV3BatchSell(params) {
|
|
|
1179
708
|
const wrappedNative = getWrappedNative(chain);
|
|
1180
709
|
const useLegacyRouter = isLegacySwapRouter(chain, routerAddress);
|
|
1181
710
|
const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
|
|
1182
|
-
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
711
|
+
const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
|
|
1183
712
|
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
1184
713
|
// ✅ 并行获取所有 RPC 数据
|
|
1185
714
|
const [balances, nonces, gasPrice] = await Promise.all([
|
|
1186
|
-
Promise.all(wallets.map(w => tokenContract.balanceOf(w.address))),
|
|
715
|
+
Promise.all(wallets.map((w) => tokenContract.balanceOf(w.address))),
|
|
1187
716
|
startNonces && startNonces.length === wallets.length
|
|
1188
717
|
? Promise.resolve(startNonces)
|
|
1189
718
|
: new NonceManager(provider).getNextNoncesForWallets(wallets),
|
|
1190
|
-
getGasPrice(provider, config)
|
|
719
|
+
getGasPrice(provider, config),
|
|
1191
720
|
]);
|
|
1192
721
|
const gasLimit = getGasLimit(config, 350000);
|
|
1193
722
|
const txType = config.txType ?? 0;
|
|
@@ -1213,19 +742,27 @@ export async function directV3BatchSell(params) {
|
|
|
1213
742
|
if (useNativeOutput) {
|
|
1214
743
|
// 卖出代币 → 得到 BNB:先获取 V3 报价,再计算利润
|
|
1215
744
|
const estimatedBNBOut = chain.toUpperCase() === 'XLAYER'
|
|
1216
|
-
? await quoteXLayerV3TokenToNativeBySlot0({
|
|
745
|
+
? await quoteXLayerV3TokenToNativeBySlot0({
|
|
746
|
+
provider,
|
|
747
|
+
tokenIn: tokenAddress,
|
|
748
|
+
amountIn: totalSellAmount,
|
|
749
|
+
fee,
|
|
750
|
+
})
|
|
1217
751
|
: await getTokenToNativeQuote(provider, tokenAddress, totalSellAmount, chain, 'v3', fee);
|
|
1218
|
-
console.log(`[V3 Sell Profit] totalSellAmount: ${totalSellAmount}, estimatedBNBOut: ${estimatedBNBOut}, estimatedBNB: ${ethers.formatEther(estimatedBNBOut)}`);
|
|
1219
752
|
if (estimatedBNBOut <= 0n)
|
|
1220
753
|
return 0n;
|
|
1221
754
|
const profit = calculateProfitAmount(estimatedBNBOut);
|
|
1222
|
-
console.log(`[V3 Sell Profit] profit: ${profit}, profitBNB: ${ethers.formatEther(profit)}`);
|
|
1223
755
|
return profit;
|
|
1224
756
|
}
|
|
1225
757
|
else if (quoteToken) {
|
|
1226
758
|
// 卖出代币 → 得到 ERC20:先获取 V3 报价,再计算利润
|
|
1227
759
|
const estimatedBNBOut = chain.toUpperCase() === 'XLAYER'
|
|
1228
|
-
? await quoteXLayerV3TokenToNativeBySlot0({
|
|
760
|
+
? await quoteXLayerV3TokenToNativeBySlot0({
|
|
761
|
+
provider,
|
|
762
|
+
tokenIn: tokenAddress,
|
|
763
|
+
amountIn: totalSellAmount,
|
|
764
|
+
fee,
|
|
765
|
+
})
|
|
1229
766
|
: await getTokenToNativeQuote(provider, tokenAddress, totalSellAmount, chain, 'v3', fee);
|
|
1230
767
|
if (estimatedBNBOut <= 0n)
|
|
1231
768
|
return 0n;
|
|
@@ -1245,36 +782,69 @@ export async function directV3BatchSell(params) {
|
|
|
1245
782
|
// 构建卖出交易数据的辅助函数
|
|
1246
783
|
const buildV3SellTxData = (wallet, sellAmount) => {
|
|
1247
784
|
if (useLegacyRouter) {
|
|
1248
|
-
const swapParams = {
|
|
785
|
+
const swapParams = {
|
|
786
|
+
tokenIn: tokenAddress,
|
|
787
|
+
tokenOut: outputToken,
|
|
788
|
+
fee,
|
|
789
|
+
recipient: useNativeOutput ? routerAddress : wallet.address,
|
|
790
|
+
deadline,
|
|
791
|
+
amountIn: sellAmount,
|
|
792
|
+
amountOutMinimum: 0n,
|
|
793
|
+
sqrtPriceLimitX96: 0n,
|
|
794
|
+
};
|
|
1249
795
|
if (useNativeOutput) {
|
|
1250
|
-
return routerIface.encodeFunctionData('multicall(bytes[])', [
|
|
796
|
+
return routerIface.encodeFunctionData('multicall(bytes[])', [
|
|
797
|
+
[
|
|
798
|
+
routerIface.encodeFunctionData('exactInputSingle', [swapParams]),
|
|
799
|
+
routerIface.encodeFunctionData('unwrapWETH9', [0n, wallet.address]),
|
|
800
|
+
],
|
|
801
|
+
]);
|
|
1251
802
|
}
|
|
1252
803
|
// ✅ 修复:ERC20 输出也需要使用 multicall 包装以传递 deadline
|
|
1253
|
-
return routerIface.encodeFunctionData('multicall(bytes[])', [
|
|
804
|
+
return routerIface.encodeFunctionData('multicall(bytes[])', [
|
|
805
|
+
[routerIface.encodeFunctionData('exactInputSingle', [swapParams])],
|
|
806
|
+
]);
|
|
1254
807
|
}
|
|
1255
808
|
else {
|
|
1256
|
-
const swapParams = {
|
|
809
|
+
const swapParams = {
|
|
810
|
+
tokenIn: tokenAddress,
|
|
811
|
+
tokenOut: outputToken,
|
|
812
|
+
fee,
|
|
813
|
+
recipient: useNativeOutput ? routerAddress : wallet.address,
|
|
814
|
+
amountIn: sellAmount,
|
|
815
|
+
amountOutMinimum: 0n,
|
|
816
|
+
sqrtPriceLimitX96: 0n,
|
|
817
|
+
};
|
|
1257
818
|
if (useNativeOutput) {
|
|
1258
|
-
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
819
|
+
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
820
|
+
deadline,
|
|
821
|
+
[
|
|
822
|
+
routerIface.encodeFunctionData('exactInputSingle', [swapParams]),
|
|
823
|
+
routerIface.encodeFunctionData('unwrapWETH9', [0n, wallet.address]),
|
|
824
|
+
],
|
|
825
|
+
]);
|
|
1259
826
|
}
|
|
1260
827
|
// ✅ 修复:ERC20 输出也需要使用 multicall 包装以传递 deadline
|
|
1261
|
-
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
828
|
+
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
829
|
+
deadline,
|
|
830
|
+
[routerIface.encodeFunctionData('exactInputSingle', [swapParams])],
|
|
831
|
+
]);
|
|
1262
832
|
}
|
|
1263
833
|
};
|
|
1264
834
|
const maxOutputIndex = findMaxFlowIndex(sellAmountsWei);
|
|
1265
835
|
const bribeWei = getBribeAmount(config, chain);
|
|
1266
836
|
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
1267
|
-
const nonceOffsets = wallets.map((_, i) => i === maxOutputIndex && hasBribe ? 1 : 0);
|
|
837
|
+
const nonceOffsets = wallets.map((_, i) => (i === maxOutputIndex && hasBribe ? 1 : 0));
|
|
1268
838
|
// ✅ 方案 A:并行签名所有交易(贿赂、卖出)+ 并行获取 ERC20 报价
|
|
1269
839
|
const signPromises = [];
|
|
1270
840
|
if (hasBribe) {
|
|
1271
|
-
signPromises.push(buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], gasPrice, chainId, txType)
|
|
1272
|
-
.then(tx => ({ type: 'bribe', index: 0, tx })));
|
|
841
|
+
signPromises.push(buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], gasPrice, chainId, txType).then((tx) => ({ type: 'bribe', index: 0, tx })));
|
|
1273
842
|
}
|
|
1274
843
|
wallets.forEach((wallet, i) => {
|
|
1275
844
|
if (sellAmountsWei[i] <= 0n)
|
|
1276
845
|
return;
|
|
1277
|
-
signPromises.push(wallet
|
|
846
|
+
signPromises.push(wallet
|
|
847
|
+
.signTransaction({
|
|
1278
848
|
to: routerAddress,
|
|
1279
849
|
data: buildV3SellTxData(wallet, sellAmountsWei[i]),
|
|
1280
850
|
value: 0n,
|
|
@@ -1282,13 +852,11 @@ export async function directV3BatchSell(params) {
|
|
|
1282
852
|
gasLimit,
|
|
1283
853
|
...buildGasFields(txType, gasPrice),
|
|
1284
854
|
chainId,
|
|
1285
|
-
})
|
|
855
|
+
})
|
|
856
|
+
.then((tx) => ({ type: 'swap', index: i, tx })));
|
|
1286
857
|
});
|
|
1287
858
|
// ✅ 并行执行:签名 + ERC20 报价
|
|
1288
|
-
const [signedResults, nativeProfitWei] = await Promise.all([
|
|
1289
|
-
Promise.all(signPromises),
|
|
1290
|
-
nativeProfitPromise
|
|
1291
|
-
]);
|
|
859
|
+
const [signedResults, nativeProfitWei] = await Promise.all([Promise.all(signPromises), nativeProfitPromise]);
|
|
1292
860
|
let profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
|
|
1293
861
|
if (profitWei > 0n && chain.toUpperCase() === 'ENI') {
|
|
1294
862
|
profitWei += GAS_LIMITS.NATIVE_TRANSFER * gasPrice;
|
|
@@ -1297,7 +865,6 @@ export async function directV3BatchSell(params) {
|
|
|
1297
865
|
const profitMode = config.profitMode || 'single';
|
|
1298
866
|
const skipProfit = config.skipProfit === true;
|
|
1299
867
|
const profitAddr = getProfitRecipient();
|
|
1300
|
-
console.log('🔧 [SDK directV3BatchSell] profitMode:', profitMode, 'skipProfit:', skipProfit, 'wallets:', wallets.length);
|
|
1301
868
|
// 利润多跳转账
|
|
1302
869
|
let profitTxs = [];
|
|
1303
870
|
let profitHopWallets;
|
|
@@ -1311,9 +878,7 @@ export async function directV3BatchSell(params) {
|
|
|
1311
878
|
profitHopWallets = [];
|
|
1312
879
|
// 计算每个钱包的利润(按卖出金额比例分配)
|
|
1313
880
|
for (let i = 0; i < wallets.length; i++) {
|
|
1314
|
-
const walletProfit = totalSellAmount > 0n
|
|
1315
|
-
? (profitWei * sellAmountsWei[i]) / totalSellAmount
|
|
1316
|
-
: 0n;
|
|
881
|
+
const walletProfit = totalSellAmount > 0n ? (profitWei * sellAmountsWei[i]) / totalSellAmount : 0n;
|
|
1317
882
|
if (walletProfit > 0n) {
|
|
1318
883
|
const walletProfitNonce = nonces[i] + nonceOffsets[i] + 1;
|
|
1319
884
|
const profitResult = await buildProfitHopTransactions({
|
|
@@ -1325,7 +890,7 @@ export async function directV3BatchSell(params) {
|
|
|
1325
890
|
gasPrice,
|
|
1326
891
|
chainId,
|
|
1327
892
|
txType,
|
|
1328
|
-
startNonce: walletProfitNonce
|
|
893
|
+
startNonce: walletProfitNonce,
|
|
1329
894
|
});
|
|
1330
895
|
profitTxs.push(...profitResult.signedTransactions);
|
|
1331
896
|
if (profitResult.hopWallets) {
|
|
@@ -1344,8 +909,11 @@ export async function directV3BatchSell(params) {
|
|
|
1344
909
|
}
|
|
1345
910
|
// 按类型分组并按顺序组装
|
|
1346
911
|
const validResults = signedResults.filter((r) => r !== null);
|
|
1347
|
-
const bribeTxs = validResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
1348
|
-
const swapTxs = validResults
|
|
912
|
+
const bribeTxs = validResults.filter((r) => r.type === 'bribe').map((r) => r.tx);
|
|
913
|
+
const swapTxs = validResults
|
|
914
|
+
.filter((r) => r.type === 'swap')
|
|
915
|
+
.sort((a, b) => a.index - b.index)
|
|
916
|
+
.map((r) => r.tx);
|
|
1349
917
|
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
1350
918
|
return {
|
|
1351
919
|
signedTransactions: signedTxs,
|
|
@@ -1358,6 +926,9 @@ export async function directV3BatchSell(params) {
|
|
|
1358
926
|
},
|
|
1359
927
|
};
|
|
1360
928
|
}
|
|
929
|
+
// ============================================================================
|
|
930
|
+
// 辅助函数:获取 Router 地址
|
|
931
|
+
// ============================================================================
|
|
1361
932
|
/**
|
|
1362
933
|
* 根据链和 DEX 获取 Router 地址
|
|
1363
934
|
*/
|