four-flap-meme-sdk 2.2.19 → 2.2.20
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/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 +240 -0
- package/dist/abis/contracts/TaxToken.json +969 -0
- package/dist/abis/contracts/TokenManager.json +836 -0
- package/dist/abis/contracts/TokenManager2.json +136 -0
- package/dist/abis/contracts/TokenManagerHelper3.json +993 -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 +5 -0
- package/dist/abis/flap/index.js +5 -0
- package/dist/abis/flap/portal-eip7702.d.ts +4 -0
- package/dist/abis/flap/portal-eip7702.js +39 -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-merkle.d.ts +4 -0
- package/dist/abis/flap/portal-merkle.js +25 -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 +37 -0
- package/dist/bundle-core/config-helpers.js +58 -0
- package/dist/bundle-core/errors.d.ts +50 -0
- package/dist/bundle-core/errors.js +38 -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 +41 -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 +456 -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 +28 -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 +16 -0
- package/dist/bundle-core/internal.js +160 -0
- package/dist/bundle-core/sign-context-helpers.d.ts +26 -0
- package/dist/bundle-core/sign-context-helpers.js +63 -0
- package/dist/bundle-core/submit.d.ts +293 -0
- package/dist/bundle-core/submit.js +731 -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 +155 -0
- package/dist/chains/bsc/pancake/bundle-buy-first-helpers.js +80 -0
- package/dist/chains/bsc/pancake/bundle-buy-first.d.ts +1 -91
- package/dist/chains/bsc/pancake/bundle-buy-first.js +98 -213
- 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 +287 -0
- package/dist/chains/index.d.ts +13 -0
- package/dist/chains/index.js +13 -0
- package/dist/chains/xlayer/constants.d.ts +5 -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 +104 -74
- 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 +11 -26
- package/dist/chains/xlayer/eip7702/constants.js +15 -68
- 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 +58 -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.d.ts +1 -1
- package/dist/chains/xlayer/eoa/constants.js +2 -16
- package/dist/chains/xlayer/eoa/dex-helpers.d.ts +1 -1
- package/dist/chains/xlayer/eoa/dex-helpers.js +7 -37
- 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 +5 -4
- 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 +73 -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 +120 -187
- package/dist/contracts/tm.d.ts +10 -8
- package/dist/contracts/tm.js +39 -35
- package/dist/contracts/tm1.js +9 -4
- package/dist/contracts/tm2.js +10 -5
- package/dist/dex/direct-router-helpers.d.ts +269 -0
- package/dist/dex/direct-router-helpers.js +536 -0
- package/dist/dex/direct-router.d.ts +3 -125
- package/dist/dex/direct-router.js +244 -672
- 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 +11 -0
- package/dist/flap/index.js +9 -0
- package/dist/flows/create.d.ts +2 -1
- package/dist/flows/create.js +6 -6
- package/dist/flows/index.d.ts +9 -0
- package/dist/flows/index.js +9 -0
- package/dist/index.d.ts +20 -88
- package/dist/index.js +20 -218
- 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 -254
- package/dist/shared/abis/index.d.ts +5 -6
- package/dist/shared/abis/index.js +5 -7
- package/dist/shared/clients/blockrazor.d.ts +4 -8
- package/dist/shared/clients/blockrazor.js +43 -35
- package/dist/shared/clients/club48.d.ts +2 -2
- package/dist/shared/clients/club48.js +36 -30
- 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.d.ts +4 -3
- package/dist/shared/clients/merkle.js +30 -47
- package/dist/shared/constants/addresses.d.ts +27 -1
- package/dist/shared/constants/addresses.js +39 -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 +4 -1
- package/dist/shared/constants/index.js +2 -1
- 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 +3 -4
- 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 +4 -5
- package/dist/shared/flap/index.js +4 -5
- package/dist/shared/flap/launch-v6.d.ts +0 -5
- package/dist/shared/flap/launch-v6.js +2 -16
- package/dist/shared/flap/meta.d.ts +22 -18
- package/dist/shared/flap/meta.js +32 -24
- package/dist/shared/flap/permit.js +10 -3
- 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 +168 -175
- package/dist/shared/flap/portal-bundle-merkle/create-to-dex.d.ts +17 -4
- package/dist/shared/flap/portal-bundle-merkle/create-to-dex.js +96 -117
- package/dist/shared/flap/portal-bundle-merkle/curve-to-dex.js +104 -122
- 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-batch.d.ts +8 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-batch.js +227 -0
- 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 +69 -288
- package/dist/shared/flap/portal-bundle-merkle/swap-cross.d.ts +9 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-cross.js +139 -0
- 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-planning.d.ts +70 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-planning.js +182 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-quick-batch.d.ts +16 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-quick-batch.js +327 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-single.d.ts +2 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-single.js +184 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-types.d.ts +101 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-types.js +1 -0
- package/dist/shared/flap/portal-bundle-merkle/swap.d.ts +6 -199
- package/dist/shared/flap/portal-bundle-merkle/swap.js +5 -1357
- package/dist/shared/flap/portal-bundle-merkle/types.d.ts +88 -12
- 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 +83 -78
- package/dist/shared/flap/portal-create-token.d.ts +39 -21
- package/dist/shared/flap/portal-create-token.js +121 -124
- package/dist/shared/flap/portal.d.ts +1 -2
- package/dist/shared/flap/portal.js +36 -28
- package/dist/shared/flap/vanity.d.ts +6 -6
- package/dist/shared/flap/vanity.js +15 -21
- package/dist/shared/flap/vault.d.ts +25 -93
- package/dist/shared/flap/vault.js +77 -127
- package/dist/shared/foundation/amount/split-by-weight.d.ts +5 -0
- package/dist/shared/foundation/amount/split-by-weight.js +35 -0
- 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/erc20/decimals.d.ts +7 -0
- package/dist/shared/foundation/erc20/decimals.js +24 -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 +15 -0
- package/dist/shared/foundation/index.js +14 -0
- package/dist/shared/foundation/nonce/nonce-manager.d.ts +19 -0
- package/dist/shared/foundation/nonce/nonce-manager.js +217 -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/foundation/wallet/generate.d.ts +5 -0
- package/dist/shared/foundation/wallet/generate.js +8 -0
- package/dist/shared/four/tax-token.d.ts +1 -1
- package/dist/shared/four/tax-token.js +31 -10
- 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 +98 -129
- 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 +50 -671
- 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 +78 -293
- package/dist/utils/mpcExclusive.d.ts +2 -5
- package/dist/utils/mpcExclusive.js +4 -3
- package/dist/utils/pcs-infinity-cl.js +18 -54
- 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 +39 -19
- 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 +4 -25
- package/dist/utils/wallet.js +27 -22
- package/dist/vanity/index.d.ts +5 -0
- package/dist/vanity/index.js +5 -0
- package/package.json +100 -2
|
@@ -1,552 +1,43 @@
|
|
|
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, CHAINS } from '../shared/constants/index.js';
|
|
13
7
|
import { GAS_LIMITS } from '../shared/constants/index.js';
|
|
14
|
-
import {
|
|
8
|
+
import { getErrorMessageFromUnknown } from '../shared/foundation/index.js';
|
|
9
|
+
import { ERC20_ABI } from '../abis/common.js';
|
|
15
10
|
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
|
-
*/
|
|
11
|
+
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';
|
|
12
|
+
export { DIRECT_ROUTERS } from './direct-router-helpers.js';
|
|
522
13
|
export async function directV2BatchBuy(params) {
|
|
523
14
|
const { chain, privateKeys, buyAmounts, tokenAddress, routerAddress, quoteToken, quoteTokenDecimals = 18, startNonces, config, } = params;
|
|
524
15
|
if (privateKeys.length !== buyAmounts.length) {
|
|
525
16
|
throw new Error('privateKeys 和 buyAmounts 长度必须相同');
|
|
526
17
|
}
|
|
527
|
-
const chainId = config.chainId || CHAIN_IDS[chain.toUpperCase()] ||
|
|
18
|
+
const chainId = config.chainId || CHAIN_IDS[chain.toUpperCase()] || CHAINS.BSC.chainId;
|
|
528
19
|
// ✅ 方案 C:使用缓存的 Provider
|
|
529
20
|
const provider = getCachedProvider(config.rpcUrl, chainId, chain);
|
|
530
21
|
const useNative = isNativeToken(quoteToken);
|
|
531
22
|
const wrappedNative = getWrappedNative(chain);
|
|
532
23
|
// 创建钱包
|
|
533
|
-
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
24
|
+
const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
|
|
534
25
|
// ✅ 预先计算所有金额(同步操作,无 RPC)
|
|
535
|
-
const flowAmounts = buyAmounts.map(amount => ethers.parseUnits(amount, quoteTokenDecimals));
|
|
26
|
+
const flowAmounts = buyAmounts.map((amount) => ethers.parseUnits(amount, quoteTokenDecimals));
|
|
536
27
|
const totalFlowWei = flowAmounts.reduce((sum, amt) => sum + amt, 0n);
|
|
537
28
|
const baseProfitWei = calculateProfitAmount(totalFlowWei);
|
|
538
29
|
// ✅ 方案 B:并行获取 nonces、gasPrice、ERC20 报价 以及(非原生代币时)授权额度
|
|
539
|
-
const quoteTokenContract =
|
|
30
|
+
const quoteTokenContract = !useNative && quoteToken ? new Contract(quoteToken, ERC20_ABI, provider) : null;
|
|
540
31
|
const [nonces, gasPrice, nativeProfitWei, buyAllowances] = await Promise.all([
|
|
541
32
|
startNonces && startNonces.length === wallets.length
|
|
542
33
|
? Promise.resolve(startNonces)
|
|
543
34
|
: new NonceManager(provider).getNextNoncesForWallets(wallets),
|
|
544
35
|
getGasPrice(provider, config),
|
|
545
|
-
|
|
36
|
+
!useNative && baseProfitWei > 0n && quoteToken
|
|
546
37
|
? getTokenToNativeQuote(provider, quoteToken, baseProfitWei, chain, 'v2')
|
|
547
38
|
: Promise.resolve(baseProfitWei),
|
|
548
39
|
quoteTokenContract
|
|
549
|
-
? Promise.all(wallets.map(w => quoteTokenContract.allowance(w.address, routerAddress).catch(() => 0n)))
|
|
40
|
+
? Promise.all(wallets.map((w) => quoteTokenContract.allowance(w.address, routerAddress).catch(() => 0n)))
|
|
550
41
|
: Promise.resolve(wallets.map(() => ethers.MaxUint256)),
|
|
551
42
|
]);
|
|
552
43
|
// 确定最终利润金额(ENI 链额外补偿利润转账的 gas 成本)
|
|
@@ -560,28 +51,8 @@ export async function directV2BatchBuy(params) {
|
|
|
560
51
|
// ENI 链原生代币买入:链上余额预检,跳过余额不足的钱包
|
|
561
52
|
let activeIndices = null;
|
|
562
53
|
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
|
-
}
|
|
54
|
+
const eniBalances = await Promise.all(wallets.map((w) => provider.getBalance(w.address)));
|
|
55
|
+
activeIndices = filterEniNativeBuyIndices(eniBalances, flowAmounts, gasLimit, gasPrice, profitWei);
|
|
585
56
|
}
|
|
586
57
|
// 如果有钱包被跳过,重建数组(仅 ENI)
|
|
587
58
|
let activeWallets = wallets;
|
|
@@ -589,10 +60,10 @@ export async function directV2BatchBuy(params) {
|
|
|
589
60
|
let activeNonces = nonces;
|
|
590
61
|
let activeBuyAllowances = buyAllowances;
|
|
591
62
|
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]);
|
|
63
|
+
activeWallets = activeIndices.map((i) => wallets[i]);
|
|
64
|
+
activeFlowAmounts = activeIndices.map((i) => flowAmounts[i]);
|
|
65
|
+
activeNonces = activeIndices.map((i) => nonces[i]);
|
|
66
|
+
activeBuyAllowances = activeIndices.map((i) => buyAllowances[i]);
|
|
596
67
|
}
|
|
597
68
|
const activeTotalFlowWei = activeFlowAmounts.reduce((sum, amt) => sum + amt, 0n);
|
|
598
69
|
const activeBaseProfitWei = calculateProfitAmount(activeTotalFlowWei);
|
|
@@ -610,8 +81,10 @@ export async function directV2BatchBuy(params) {
|
|
|
610
81
|
if (activeFlowAmounts[i] <= 0n)
|
|
611
82
|
continue;
|
|
612
83
|
if (activeBuyAllowances[i] < activeFlowAmounts[i]) {
|
|
613
|
-
|
|
614
|
-
|
|
84
|
+
const approveData = quoteTokenContract.interface.encodeFunctionData('approve', [
|
|
85
|
+
routerAddress,
|
|
86
|
+
ethers.MaxUint256,
|
|
87
|
+
]);
|
|
615
88
|
const approveTx = await activeWallets[i].signTransaction({
|
|
616
89
|
to: quoteToken,
|
|
617
90
|
data: approveData,
|
|
@@ -645,14 +118,39 @@ export async function directV2BatchBuy(params) {
|
|
|
645
118
|
return { txData: encodeDYORMulticall(BigInt(deadline), multicallData), txValue: useNative ? amountWei : 0n };
|
|
646
119
|
}
|
|
647
120
|
else if (useSwapRouter02) {
|
|
648
|
-
const swapData = routerIface.encodeFunctionData('swapExactTokensForTokens', [
|
|
649
|
-
|
|
121
|
+
const swapData = routerIface.encodeFunctionData('swapExactTokensForTokens', [
|
|
122
|
+
amountWei,
|
|
123
|
+
0n,
|
|
124
|
+
path,
|
|
125
|
+
wallet.address,
|
|
126
|
+
]);
|
|
127
|
+
return {
|
|
128
|
+
txData: routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, [swapData]]),
|
|
129
|
+
txValue: useNative ? amountWei : 0n,
|
|
130
|
+
};
|
|
650
131
|
}
|
|
651
132
|
else if (useNative) {
|
|
652
|
-
return {
|
|
133
|
+
return {
|
|
134
|
+
txData: routerIface.encodeFunctionData('swapExactETHForTokensSupportingFeeOnTransferTokens', [
|
|
135
|
+
0n,
|
|
136
|
+
path,
|
|
137
|
+
wallet.address,
|
|
138
|
+
deadline,
|
|
139
|
+
]),
|
|
140
|
+
txValue: amountWei,
|
|
141
|
+
};
|
|
653
142
|
}
|
|
654
143
|
else {
|
|
655
|
-
return {
|
|
144
|
+
return {
|
|
145
|
+
txData: routerIface.encodeFunctionData('swapExactTokensForTokensSupportingFeeOnTransferTokens', [
|
|
146
|
+
amountWei,
|
|
147
|
+
0n,
|
|
148
|
+
path,
|
|
149
|
+
wallet.address,
|
|
150
|
+
deadline,
|
|
151
|
+
]),
|
|
152
|
+
txValue: 0n,
|
|
153
|
+
};
|
|
656
154
|
}
|
|
657
155
|
};
|
|
658
156
|
// 选择金额最大的钱包支付贿赂和利润
|
|
@@ -662,18 +160,18 @@ export async function directV2BatchBuy(params) {
|
|
|
662
160
|
const hasBribe = bribeWei > 0n && activeWallets.length > 0;
|
|
663
161
|
const hasProfit = activeProfitWei > 0n;
|
|
664
162
|
// 计算 nonce 偏移
|
|
665
|
-
const nonceOffsets = activeWallets.map((_, i) => i === maxFlowIndex && hasBribe ? 1 : 0);
|
|
163
|
+
const nonceOffsets = activeWallets.map((_, i) => (i === maxFlowIndex && hasBribe ? 1 : 0));
|
|
666
164
|
// ✅ 方案 A:并行签名所有交易(贿赂、主交易、利润)
|
|
667
165
|
const signPromises = [];
|
|
668
166
|
// 贿赂交易
|
|
669
167
|
if (hasBribe) {
|
|
670
|
-
signPromises.push(buildBribeTransaction(activeWallets[maxFlowIndex], bribeWei, activeNonces[maxFlowIndex], gasPrice, chainId, txType)
|
|
671
|
-
.then(tx => ({ type: 'bribe', index: 0, tx })));
|
|
168
|
+
signPromises.push(buildBribeTransaction(activeWallets[maxFlowIndex], bribeWei, activeNonces[maxFlowIndex], gasPrice, chainId, txType).then((tx) => ({ type: 'bribe', index: 0, tx })));
|
|
672
169
|
}
|
|
673
170
|
// 主交易(并行签名)
|
|
674
171
|
activeWallets.forEach((wallet, i) => {
|
|
675
172
|
const { txData, txValue } = buildTxData(wallet, activeFlowAmounts[i]);
|
|
676
|
-
signPromises.push(wallet
|
|
173
|
+
signPromises.push(wallet
|
|
174
|
+
.signTransaction({
|
|
677
175
|
to: routerAddress,
|
|
678
176
|
data: txData,
|
|
679
177
|
value: txValue,
|
|
@@ -681,20 +179,23 @@ export async function directV2BatchBuy(params) {
|
|
|
681
179
|
gasLimit,
|
|
682
180
|
...buildGasFields(txType, gasPrice),
|
|
683
181
|
chainId,
|
|
684
|
-
})
|
|
182
|
+
})
|
|
183
|
+
.then((tx) => ({ type: 'swap', index: i, tx })));
|
|
685
184
|
});
|
|
686
185
|
// ✅ 并行执行所有签名
|
|
687
186
|
const signedResults = await Promise.all(signPromises);
|
|
688
187
|
// 按类型分组并按顺序组装: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
|
|
188
|
+
const approveSignedTxs = buyApproveTxs.sort((a, b) => a.walletIndex - b.walletIndex).map((a) => a.tx);
|
|
189
|
+
const bribeTxs = signedResults.filter((r) => r.type === 'bribe').map((r) => r.tx);
|
|
190
|
+
const swapTxs = signedResults
|
|
191
|
+
.filter((r) => r.type === 'swap')
|
|
192
|
+
.sort((a, b) => a.index - b.index)
|
|
193
|
+
.map((r) => r.tx);
|
|
692
194
|
const signedTxs = [...approveSignedTxs, ...bribeTxs, ...swapTxs];
|
|
693
195
|
// ✅ 检查是否使用分布式利润模式
|
|
694
196
|
const profitMode = config.profitMode || 'single';
|
|
695
197
|
const skipProfit = config.skipProfit === true;
|
|
696
198
|
const profitAddr = getProfitRecipient();
|
|
697
|
-
console.log('🔧 [SDK directV2BatchBuy] profitMode:', profitMode, 'skipProfit:', skipProfit, 'wallets:', activeWallets.length);
|
|
698
199
|
// 利润多跳转账
|
|
699
200
|
let profitHopWallets;
|
|
700
201
|
if (hasProfit && !skipProfit) {
|
|
@@ -707,9 +208,7 @@ export async function directV2BatchBuy(params) {
|
|
|
707
208
|
profitHopWallets = [];
|
|
708
209
|
// 计算每个钱包的利润(按比例分配)
|
|
709
210
|
for (let i = 0; i < activeWallets.length; i++) {
|
|
710
|
-
const walletProfit = activeTotalFlowWei > 0n
|
|
711
|
-
? (activeProfitWei * activeFlowAmounts[i]) / activeTotalFlowWei
|
|
712
|
-
: 0n;
|
|
211
|
+
const walletProfit = activeTotalFlowWei > 0n ? (activeProfitWei * activeFlowAmounts[i]) / activeTotalFlowWei : 0n;
|
|
713
212
|
if (walletProfit > 0n) {
|
|
714
213
|
const walletProfitNonce = activeNonces[i] + nonceOffsets[i] + 1;
|
|
715
214
|
const profitResult = await buildProfitHopTransactions({
|
|
@@ -721,7 +220,7 @@ export async function directV2BatchBuy(params) {
|
|
|
721
220
|
gasPrice,
|
|
722
221
|
chainId,
|
|
723
222
|
txType,
|
|
724
|
-
startNonce: walletProfitNonce
|
|
223
|
+
startNonce: walletProfitNonce,
|
|
725
224
|
});
|
|
726
225
|
signedTxs.push(...profitResult.signedTransactions);
|
|
727
226
|
if (profitResult.hopWallets) {
|
|
@@ -754,25 +253,28 @@ export async function directV2BatchBuy(params) {
|
|
|
754
253
|
*/
|
|
755
254
|
export async function directV2BatchSell(params) {
|
|
756
255
|
const { chain, privateKeys, sellPercentages, sellAmounts, tokenAddress, tokenDecimals: inputDecimals, routerAddress, quoteToken, startNonces, config, } = params;
|
|
757
|
-
const chainId = config.chainId || CHAIN_IDS[chain.toUpperCase()] ||
|
|
256
|
+
const chainId = config.chainId || CHAIN_IDS[chain.toUpperCase()] || CHAINS.BSC.chainId;
|
|
758
257
|
// ✅ 方案 C:使用缓存的 Provider
|
|
759
258
|
const provider = getCachedProvider(config.rpcUrl, chainId, chain);
|
|
760
259
|
const useNativeOutput = isNativeToken(quoteToken);
|
|
761
260
|
const wrappedNative = getWrappedNative(chain);
|
|
762
261
|
// 创建钱包
|
|
763
|
-
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
262
|
+
const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
|
|
764
263
|
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
765
264
|
// ✅ 性能优化:并行获取所有独立的 RPC 数据(包括授权额度)
|
|
766
265
|
const [fetchedDecimals, balances, allowances, nonces, gasPrice] = await Promise.all([
|
|
767
266
|
inputDecimals !== undefined
|
|
768
267
|
? Promise.resolve(inputDecimals)
|
|
769
|
-
: tokenContract
|
|
770
|
-
|
|
771
|
-
|
|
268
|
+
: tokenContract
|
|
269
|
+
.decimals()
|
|
270
|
+
.then((d) => Number(d))
|
|
271
|
+
.catch(() => 18),
|
|
272
|
+
Promise.all(wallets.map((w) => tokenContract.balanceOf(w.address))),
|
|
273
|
+
Promise.all(wallets.map((w) => tokenContract.allowance(w.address, routerAddress).catch(() => 0n))),
|
|
772
274
|
startNonces && startNonces.length === wallets.length
|
|
773
275
|
? Promise.resolve(startNonces)
|
|
774
276
|
: new NonceManager(provider).getNextNoncesForWallets(wallets),
|
|
775
|
-
getGasPrice(provider, config)
|
|
277
|
+
getGasPrice(provider, config),
|
|
776
278
|
]);
|
|
777
279
|
const tokenDecimals = fetchedDecimals;
|
|
778
280
|
const gasLimit = getGasLimit(config, 300000);
|
|
@@ -794,33 +296,17 @@ export async function directV2BatchSell(params) {
|
|
|
794
296
|
amount = balances[i];
|
|
795
297
|
}
|
|
796
298
|
if (amount > balances[i]) {
|
|
797
|
-
console.log(`⚠️ [V2 Sell] 钱包 ${i} 卖出量(${amount}) > 链上余额(${balances[i]}),已自动调整为实际余额`);
|
|
798
299
|
amount = balances[i];
|
|
799
300
|
}
|
|
800
301
|
sellAmountsWei.push(amount);
|
|
801
302
|
}
|
|
802
303
|
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
304
|
// ✅ 自动授权:检查每个钱包对 Router 的授权额度,不足则插入 approve 交易
|
|
818
305
|
const approveTxs = [];
|
|
819
306
|
for (let i = 0; i < wallets.length; i++) {
|
|
820
307
|
if (sellAmountsWei[i] <= 0n)
|
|
821
308
|
continue;
|
|
822
309
|
if (allowances[i] < sellAmountsWei[i]) {
|
|
823
|
-
console.log(`🔓 [V2 Sell] 钱包 ${i} 授权不足 (${allowances[i]} < ${sellAmountsWei[i]}), 自动 approve`);
|
|
824
310
|
const approveData = tokenContract.interface.encodeFunctionData('approve', [routerAddress, ethers.MaxUint256]);
|
|
825
311
|
const approveTx = await wallets[i].signTransaction({
|
|
826
312
|
to: tokenAddress,
|
|
@@ -842,11 +328,9 @@ export async function directV2BatchSell(params) {
|
|
|
842
328
|
if (useNativeOutput) {
|
|
843
329
|
// 卖出代币 → 得到 BNB:先获取 V2 报价,再计算利润(✅ 使用当前 Router 报价,IROSwap 池需用 IROSwap Router)
|
|
844
330
|
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
331
|
if (estimatedBNBOut <= 0n)
|
|
847
332
|
return 0n;
|
|
848
333
|
const profit = calculateProfitAmount(estimatedBNBOut);
|
|
849
|
-
console.log(`[V2 Sell Profit] profit: ${profit}, profitBNB: ${ethers.formatEther(profit)}`);
|
|
850
334
|
return profit;
|
|
851
335
|
}
|
|
852
336
|
else if (quoteToken) {
|
|
@@ -857,19 +341,16 @@ export async function directV2BatchSell(params) {
|
|
|
857
341
|
const step1 = await quoteV2(provider, tokenAddress, quoteToken, totalSellAmount, chain, routerAddress);
|
|
858
342
|
if (!step1.amountOut || step1.amountOut <= 0n)
|
|
859
343
|
return 0n;
|
|
860
|
-
console.log(`[V2 Sell Profit] step1 token→quoteToken: ${step1.amountOut} wei`);
|
|
861
344
|
const estimatedBNBOut = await getTokenToNativeQuote(provider, quoteToken, step1.amountOut, chain, 'v2');
|
|
862
|
-
console.log(`[V2 Sell Profit] step2 quoteToken→BNB: ${estimatedBNBOut} wei`);
|
|
863
345
|
if (estimatedBNBOut <= 0n)
|
|
864
346
|
return 0n;
|
|
865
347
|
const profit = calculateProfitAmount(estimatedBNBOut);
|
|
866
|
-
console.log(`[V2 Sell Profit] profit: ${profit}, profitBNB: ${ethers.formatEther(profit)}`);
|
|
867
348
|
return profit;
|
|
868
349
|
}
|
|
869
350
|
return 0n;
|
|
870
351
|
}
|
|
871
352
|
catch (error) {
|
|
872
|
-
console.error(`[V2 Sell Profit] Error:`, error);
|
|
353
|
+
console.error(`[V2 Sell Profit] Error:`, getErrorMessageFromUnknown(error));
|
|
873
354
|
return 0n;
|
|
874
355
|
}
|
|
875
356
|
})();
|
|
@@ -878,7 +359,9 @@ export async function directV2BatchSell(params) {
|
|
|
878
359
|
const path = [tokenAddress, outputToken];
|
|
879
360
|
const useSwapRouter02 = isSwapRouter02(chain, routerAddress);
|
|
880
361
|
const useDYORSwap = isDYORSwap(chain, routerAddress);
|
|
881
|
-
const routerIface = useSwapRouter02
|
|
362
|
+
const routerIface = useSwapRouter02
|
|
363
|
+
? new ethers.Interface(SWAP_ROUTER02_V2_ABI)
|
|
364
|
+
: new ethers.Interface(V2_ROUTER_ABI);
|
|
882
365
|
// 构建卖出交易数据的辅助函数
|
|
883
366
|
const buildSellTxData = (wallet, sellAmount) => {
|
|
884
367
|
if (useDYORSwap) {
|
|
@@ -900,32 +383,47 @@ export async function directV2BatchSell(params) {
|
|
|
900
383
|
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, multicallData]);
|
|
901
384
|
}
|
|
902
385
|
else {
|
|
903
|
-
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
386
|
+
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
387
|
+
deadline,
|
|
388
|
+
[routerIface.encodeFunctionData('swapExactTokensForTokens', [sellAmount, 0n, path, wallet.address])],
|
|
389
|
+
]);
|
|
904
390
|
}
|
|
905
391
|
}
|
|
906
392
|
else if (useNativeOutput) {
|
|
907
|
-
return routerIface.encodeFunctionData('swapExactTokensForETHSupportingFeeOnTransferTokens', [
|
|
393
|
+
return routerIface.encodeFunctionData('swapExactTokensForETHSupportingFeeOnTransferTokens', [
|
|
394
|
+
sellAmount,
|
|
395
|
+
0n,
|
|
396
|
+
path,
|
|
397
|
+
wallet.address,
|
|
398
|
+
deadline,
|
|
399
|
+
]);
|
|
908
400
|
}
|
|
909
401
|
else {
|
|
910
|
-
return routerIface.encodeFunctionData('swapExactTokensForTokensSupportingFeeOnTransferTokens', [
|
|
402
|
+
return routerIface.encodeFunctionData('swapExactTokensForTokensSupportingFeeOnTransferTokens', [
|
|
403
|
+
sellAmount,
|
|
404
|
+
0n,
|
|
405
|
+
path,
|
|
406
|
+
wallet.address,
|
|
407
|
+
deadline,
|
|
408
|
+
]);
|
|
911
409
|
}
|
|
912
410
|
};
|
|
913
411
|
const maxOutputIndex = findMaxFlowIndex(sellAmountsWei);
|
|
914
412
|
const bribeWei = getBribeAmount(config, chain);
|
|
915
413
|
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
916
|
-
const nonceOffsets = wallets.map((_, i) => i === maxOutputIndex && hasBribe ? 1 : 0);
|
|
414
|
+
const nonceOffsets = wallets.map((_, i) => (i === maxOutputIndex && hasBribe ? 1 : 0));
|
|
917
415
|
// ✅ 方案 A:并行签名所有交易(贿赂、卖出、利润)+ 并行获取 ERC20 报价
|
|
918
416
|
const signPromises = [];
|
|
919
417
|
// 贿赂交易
|
|
920
418
|
if (hasBribe) {
|
|
921
|
-
signPromises.push(buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], gasPrice, chainId, txType)
|
|
922
|
-
.then(tx => ({ type: 'bribe', index: 0, tx })));
|
|
419
|
+
signPromises.push(buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], gasPrice, chainId, txType).then((tx) => ({ type: 'bribe', index: 0, tx })));
|
|
923
420
|
}
|
|
924
421
|
// 卖出交易(并行签名)
|
|
925
422
|
wallets.forEach((wallet, i) => {
|
|
926
423
|
if (sellAmountsWei[i] <= 0n)
|
|
927
424
|
return;
|
|
928
|
-
signPromises.push(wallet
|
|
425
|
+
signPromises.push(wallet
|
|
426
|
+
.signTransaction({
|
|
929
427
|
to: routerAddress,
|
|
930
428
|
data: buildSellTxData(wallet, sellAmountsWei[i]),
|
|
931
429
|
value: 0n,
|
|
@@ -933,13 +431,11 @@ export async function directV2BatchSell(params) {
|
|
|
933
431
|
gasLimit,
|
|
934
432
|
...buildGasFields(txType, gasPrice),
|
|
935
433
|
chainId,
|
|
936
|
-
})
|
|
434
|
+
})
|
|
435
|
+
.then((tx) => ({ type: 'swap', index: i, tx })));
|
|
937
436
|
});
|
|
938
437
|
// ✅ 并行执行:签名 + ERC20 报价
|
|
939
|
-
const [signedResults, nativeProfitWei] = await Promise.all([
|
|
940
|
-
Promise.all(signPromises),
|
|
941
|
-
nativeProfitPromise
|
|
942
|
-
]);
|
|
438
|
+
const [signedResults, nativeProfitWei] = await Promise.all([Promise.all(signPromises), nativeProfitPromise]);
|
|
943
439
|
let profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
|
|
944
440
|
if (profitWei > 0n && chain.toUpperCase() === 'ENI') {
|
|
945
441
|
profitWei += GAS_LIMITS.NATIVE_TRANSFER * gasPrice;
|
|
@@ -948,7 +444,6 @@ export async function directV2BatchSell(params) {
|
|
|
948
444
|
const profitMode = config.profitMode || 'single';
|
|
949
445
|
const skipProfit = config.skipProfit === true;
|
|
950
446
|
const profitAddr = getProfitRecipient();
|
|
951
|
-
console.log('🔧 [SDK directV2BatchSell] profitMode:', profitMode, 'skipProfit:', skipProfit, 'wallets:', wallets.length);
|
|
952
447
|
// 利润多跳转账
|
|
953
448
|
let profitTxs = [];
|
|
954
449
|
let profitHopWallets;
|
|
@@ -962,9 +457,7 @@ export async function directV2BatchSell(params) {
|
|
|
962
457
|
profitHopWallets = [];
|
|
963
458
|
// 计算每个钱包的利润(按卖出金额比例分配)
|
|
964
459
|
for (let i = 0; i < wallets.length; i++) {
|
|
965
|
-
const walletProfit = totalSellAmount > 0n
|
|
966
|
-
? (profitWei * sellAmountsWei[i]) / totalSellAmount
|
|
967
|
-
: 0n;
|
|
460
|
+
const walletProfit = totalSellAmount > 0n ? (profitWei * sellAmountsWei[i]) / totalSellAmount : 0n;
|
|
968
461
|
if (walletProfit > 0n) {
|
|
969
462
|
const walletProfitNonce = nonces[i] + nonceOffsets[i] + 1;
|
|
970
463
|
const profitResult = await buildProfitHopTransactions({
|
|
@@ -976,7 +469,7 @@ export async function directV2BatchSell(params) {
|
|
|
976
469
|
gasPrice,
|
|
977
470
|
chainId,
|
|
978
471
|
txType,
|
|
979
|
-
startNonce: walletProfitNonce
|
|
472
|
+
startNonce: walletProfitNonce,
|
|
980
473
|
});
|
|
981
474
|
profitTxs.push(...profitResult.signedTransactions);
|
|
982
475
|
if (profitResult.hopWallets) {
|
|
@@ -995,9 +488,12 @@ export async function directV2BatchSell(params) {
|
|
|
995
488
|
}
|
|
996
489
|
// 按类型分组并按顺序组装:approve → bribe → swap → profit
|
|
997
490
|
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
|
-
|
|
491
|
+
const bribeTxs = validResults.filter((r) => r.type === 'bribe').map((r) => r.tx);
|
|
492
|
+
const swapTxs = validResults
|
|
493
|
+
.filter((r) => r.type === 'swap')
|
|
494
|
+
.sort((a, b) => a.index - b.index)
|
|
495
|
+
.map((r) => r.tx);
|
|
496
|
+
const approveSignedTxs = approveTxs.sort((a, b) => a.walletIndex - b.walletIndex).map((a) => a.tx);
|
|
1001
497
|
const signedTxs = [...approveSignedTxs, ...bribeTxs, ...swapTxs, ...profitTxs];
|
|
1002
498
|
return {
|
|
1003
499
|
signedTransactions: signedTxs,
|
|
@@ -1025,16 +521,16 @@ export async function directV3BatchBuy(params) {
|
|
|
1025
521
|
if (privateKeys.length !== buyAmounts.length) {
|
|
1026
522
|
throw new Error('privateKeys 和 buyAmounts 长度必须相同');
|
|
1027
523
|
}
|
|
1028
|
-
const chainId = config.chainId || CHAIN_IDS[chain.toUpperCase()] ||
|
|
524
|
+
const chainId = config.chainId || CHAIN_IDS[chain.toUpperCase()] || CHAINS.BSC.chainId;
|
|
1029
525
|
// ✅ 方案 C:使用缓存的 Provider
|
|
1030
526
|
const provider = getCachedProvider(config.rpcUrl, chainId, chain);
|
|
1031
527
|
const useNative = isNativeToken(quoteToken);
|
|
1032
528
|
const wrappedNative = getWrappedNative(chain);
|
|
1033
529
|
const useLegacyRouter = isLegacySwapRouter(chain, routerAddress);
|
|
1034
530
|
const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
|
|
1035
|
-
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
531
|
+
const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
|
|
1036
532
|
// ✅ 预先计算所有金额(同步操作,无 RPC)
|
|
1037
|
-
const flowAmounts = buyAmounts.map(amount => ethers.parseUnits(amount, quoteTokenDecimals));
|
|
533
|
+
const flowAmounts = buyAmounts.map((amount) => ethers.parseUnits(amount, quoteTokenDecimals));
|
|
1038
534
|
const totalFlowWei = flowAmounts.reduce((sum, amt) => sum + amt, 0n);
|
|
1039
535
|
const baseProfitWei = calculateProfitAmount(totalFlowWei);
|
|
1040
536
|
// ✅ 方案 B:并行获取 nonces、gasPrice 和 ERC20 报价
|
|
@@ -1044,11 +540,11 @@ export async function directV3BatchBuy(params) {
|
|
|
1044
540
|
: new NonceManager(provider).getNextNoncesForWallets(wallets),
|
|
1045
541
|
getGasPrice(provider, config),
|
|
1046
542
|
// ERC20 报价(V3 买入用 V3 报价)
|
|
1047
|
-
|
|
1048
|
-
?
|
|
543
|
+
!useNative && baseProfitWei > 0n && quoteToken
|
|
544
|
+
? chain.toUpperCase() === 'XLAYER'
|
|
1049
545
|
? quoteXLayerV3TokenToNativeBySlot0({ provider, tokenIn: quoteToken, amountIn: baseProfitWei, fee })
|
|
1050
|
-
: getTokenToNativeQuote(provider, quoteToken, baseProfitWei, chain, 'v3', fee)
|
|
1051
|
-
: Promise.resolve(baseProfitWei)
|
|
546
|
+
: getTokenToNativeQuote(provider, quoteToken, baseProfitWei, chain, 'v3', fee)
|
|
547
|
+
: Promise.resolve(baseProfitWei),
|
|
1052
548
|
]);
|
|
1053
549
|
let profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
|
|
1054
550
|
if (profitWei > 0n && chain.toUpperCase() === 'ENI') {
|
|
@@ -1062,51 +558,87 @@ export async function directV3BatchBuy(params) {
|
|
|
1062
558
|
// 构建交易数据的辅助函数
|
|
1063
559
|
const buildV3BuyTxData = (wallet, amountWei) => {
|
|
1064
560
|
if (useLegacyRouter) {
|
|
1065
|
-
const swapParams = {
|
|
561
|
+
const swapParams = {
|
|
562
|
+
tokenIn: inputToken,
|
|
563
|
+
tokenOut: tokenAddress,
|
|
564
|
+
fee,
|
|
565
|
+
recipient: wallet.address,
|
|
566
|
+
deadline,
|
|
567
|
+
amountIn: amountWei,
|
|
568
|
+
amountOutMinimum: 0n,
|
|
569
|
+
sqrtPriceLimitX96: 0n,
|
|
570
|
+
};
|
|
1066
571
|
if (useNative) {
|
|
1067
572
|
const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
|
|
1068
573
|
const refundData = routerIface.encodeFunctionData('refundETH', []);
|
|
1069
|
-
return {
|
|
574
|
+
return {
|
|
575
|
+
txData: routerIface.encodeFunctionData('multicall(bytes[])', [[swapData, refundData]]),
|
|
576
|
+
txValue: amountWei,
|
|
577
|
+
};
|
|
1070
578
|
}
|
|
1071
579
|
return { txData: routerIface.encodeFunctionData('exactInputSingle', [swapParams]), txValue: 0n };
|
|
1072
580
|
}
|
|
1073
581
|
else {
|
|
1074
|
-
const swapParams = {
|
|
582
|
+
const swapParams = {
|
|
583
|
+
tokenIn: inputToken,
|
|
584
|
+
tokenOut: tokenAddress,
|
|
585
|
+
fee,
|
|
586
|
+
recipient: wallet.address,
|
|
587
|
+
amountIn: amountWei,
|
|
588
|
+
amountOutMinimum: 0n,
|
|
589
|
+
sqrtPriceLimitX96: 0n,
|
|
590
|
+
};
|
|
1075
591
|
if (useNative) {
|
|
1076
592
|
const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
|
|
1077
593
|
const refundData = routerIface.encodeFunctionData('refundETH', []);
|
|
1078
|
-
return {
|
|
594
|
+
return {
|
|
595
|
+
txData: routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, [swapData, refundData]]),
|
|
596
|
+
txValue: amountWei,
|
|
597
|
+
};
|
|
1079
598
|
}
|
|
1080
599
|
// ✅ 修复:ERC20 买入也需要使用 multicall 包装以传递 deadline
|
|
1081
600
|
// SwapRouter02 的 exactInputSingle 不包含 deadline 参数,必须通过 multicall 传递
|
|
1082
601
|
const swapData = routerIface.encodeFunctionData('exactInputSingle', [swapParams]);
|
|
1083
|
-
return {
|
|
602
|
+
return {
|
|
603
|
+
txData: routerIface.encodeFunctionData('multicall(uint256,bytes[])', [deadline, [swapData]]),
|
|
604
|
+
txValue: 0n,
|
|
605
|
+
};
|
|
1084
606
|
}
|
|
1085
607
|
};
|
|
1086
608
|
const maxFlowIndex = findMaxFlowIndex(flowAmounts);
|
|
1087
609
|
const bribeWei = getBribeAmount(config, chain);
|
|
1088
610
|
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
1089
611
|
const hasProfit = profitWei > 0n;
|
|
1090
|
-
const nonceOffsets = wallets.map((_, i) => i === maxFlowIndex && hasBribe ? 1 : 0);
|
|
612
|
+
const nonceOffsets = wallets.map((_, i) => (i === maxFlowIndex && hasBribe ? 1 : 0));
|
|
1091
613
|
// ✅ 方案 A:并行签名所有交易(贿赂、主交易、利润)
|
|
1092
614
|
const signPromises = [];
|
|
1093
615
|
if (hasBribe) {
|
|
1094
|
-
signPromises.push(buildBribeTransaction(wallets[maxFlowIndex], bribeWei, nonces[maxFlowIndex], gasPrice, chainId, txType)
|
|
1095
|
-
.then(tx => ({ type: 'bribe', index: 0, tx })));
|
|
616
|
+
signPromises.push(buildBribeTransaction(wallets[maxFlowIndex], bribeWei, nonces[maxFlowIndex], gasPrice, chainId, txType).then((tx) => ({ type: 'bribe', index: 0, tx })));
|
|
1096
617
|
}
|
|
1097
618
|
wallets.forEach((wallet, i) => {
|
|
1098
619
|
const { txData, txValue } = buildV3BuyTxData(wallet, flowAmounts[i]);
|
|
1099
|
-
signPromises.push(wallet
|
|
1100
|
-
.
|
|
620
|
+
signPromises.push(wallet
|
|
621
|
+
.signTransaction({
|
|
622
|
+
to: routerAddress,
|
|
623
|
+
data: txData,
|
|
624
|
+
value: txValue,
|
|
625
|
+
nonce: nonces[i] + nonceOffsets[i],
|
|
626
|
+
gasLimit,
|
|
627
|
+
...buildGasFields(txType, gasPrice),
|
|
628
|
+
chainId,
|
|
629
|
+
})
|
|
630
|
+
.then((tx) => ({ type: 'swap', index: i, tx })));
|
|
1101
631
|
});
|
|
1102
632
|
const signedResults = await Promise.all(signPromises);
|
|
1103
|
-
const bribeTxs = signedResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
1104
|
-
const swapTxs = signedResults
|
|
633
|
+
const bribeTxs = signedResults.filter((r) => r.type === 'bribe').map((r) => r.tx);
|
|
634
|
+
const swapTxs = signedResults
|
|
635
|
+
.filter((r) => r.type === 'swap')
|
|
636
|
+
.sort((a, b) => a.index - b.index)
|
|
637
|
+
.map((r) => r.tx);
|
|
1105
638
|
// ✅ 检查是否使用分布式利润模式
|
|
1106
639
|
const profitMode = config.profitMode || 'single';
|
|
1107
640
|
const skipProfit = config.skipProfit === true;
|
|
1108
641
|
const profitAddr = getProfitRecipient();
|
|
1109
|
-
console.log('🔧 [SDK directV3BatchBuy] profitMode:', profitMode, 'skipProfit:', skipProfit, 'wallets:', wallets.length);
|
|
1110
642
|
// 利润多跳转账
|
|
1111
643
|
let profitTxs = [];
|
|
1112
644
|
let profitHopWallets;
|
|
@@ -1120,9 +652,7 @@ export async function directV3BatchBuy(params) {
|
|
|
1120
652
|
profitHopWallets = [];
|
|
1121
653
|
// 计算每个钱包的利润(按比例分配)
|
|
1122
654
|
for (let i = 0; i < wallets.length; i++) {
|
|
1123
|
-
const walletProfit = totalFlowWei > 0n
|
|
1124
|
-
? (profitWei * flowAmounts[i]) / totalFlowWei
|
|
1125
|
-
: 0n;
|
|
655
|
+
const walletProfit = totalFlowWei > 0n ? (profitWei * flowAmounts[i]) / totalFlowWei : 0n;
|
|
1126
656
|
if (walletProfit > 0n) {
|
|
1127
657
|
const walletProfitNonce = nonces[i] + nonceOffsets[i] + 1;
|
|
1128
658
|
const profitResult = await buildProfitHopTransactions({
|
|
@@ -1134,7 +664,7 @@ export async function directV3BatchBuy(params) {
|
|
|
1134
664
|
gasPrice,
|
|
1135
665
|
chainId,
|
|
1136
666
|
txType,
|
|
1137
|
-
startNonce: walletProfitNonce
|
|
667
|
+
startNonce: walletProfitNonce,
|
|
1138
668
|
});
|
|
1139
669
|
profitTxs.push(...profitResult.signedTransactions);
|
|
1140
670
|
if (profitResult.hopWallets) {
|
|
@@ -1172,22 +702,22 @@ export async function directV3BatchBuy(params) {
|
|
|
1172
702
|
*/
|
|
1173
703
|
export async function directV3BatchSell(params) {
|
|
1174
704
|
const { chain, privateKeys, sellPercentages, sellAmounts, tokenAddress, tokenDecimals = 18, routerAddress, fee, quoteToken, startNonces, config, } = params;
|
|
1175
|
-
const chainId = config.chainId || CHAIN_IDS[chain.toUpperCase()] ||
|
|
705
|
+
const chainId = config.chainId || CHAIN_IDS[chain.toUpperCase()] || CHAINS.BSC.chainId;
|
|
1176
706
|
// ✅ 方案 C:使用缓存的 Provider
|
|
1177
707
|
const provider = getCachedProvider(config.rpcUrl, chainId, chain);
|
|
1178
708
|
const useNativeOutput = isNativeToken(quoteToken);
|
|
1179
709
|
const wrappedNative = getWrappedNative(chain);
|
|
1180
710
|
const useLegacyRouter = isLegacySwapRouter(chain, routerAddress);
|
|
1181
711
|
const routerAbi = useLegacyRouter ? V3_ROUTER_LEGACY_ABI : V3_ROUTER02_ABI;
|
|
1182
|
-
const wallets = privateKeys.map(pk => new Wallet(pk, provider));
|
|
712
|
+
const wallets = privateKeys.map((pk) => new Wallet(pk, provider));
|
|
1183
713
|
const tokenContract = new Contract(tokenAddress, ERC20_ABI, provider);
|
|
1184
714
|
// ✅ 并行获取所有 RPC 数据
|
|
1185
715
|
const [balances, nonces, gasPrice] = await Promise.all([
|
|
1186
|
-
Promise.all(wallets.map(w => tokenContract.balanceOf(w.address))),
|
|
716
|
+
Promise.all(wallets.map((w) => tokenContract.balanceOf(w.address))),
|
|
1187
717
|
startNonces && startNonces.length === wallets.length
|
|
1188
718
|
? Promise.resolve(startNonces)
|
|
1189
719
|
: new NonceManager(provider).getNextNoncesForWallets(wallets),
|
|
1190
|
-
getGasPrice(provider, config)
|
|
720
|
+
getGasPrice(provider, config),
|
|
1191
721
|
]);
|
|
1192
722
|
const gasLimit = getGasLimit(config, 350000);
|
|
1193
723
|
const txType = config.txType ?? 0;
|
|
@@ -1213,19 +743,27 @@ export async function directV3BatchSell(params) {
|
|
|
1213
743
|
if (useNativeOutput) {
|
|
1214
744
|
// 卖出代币 → 得到 BNB:先获取 V3 报价,再计算利润
|
|
1215
745
|
const estimatedBNBOut = chain.toUpperCase() === 'XLAYER'
|
|
1216
|
-
? await quoteXLayerV3TokenToNativeBySlot0({
|
|
746
|
+
? await quoteXLayerV3TokenToNativeBySlot0({
|
|
747
|
+
provider,
|
|
748
|
+
tokenIn: tokenAddress,
|
|
749
|
+
amountIn: totalSellAmount,
|
|
750
|
+
fee,
|
|
751
|
+
})
|
|
1217
752
|
: await getTokenToNativeQuote(provider, tokenAddress, totalSellAmount, chain, 'v3', fee);
|
|
1218
|
-
console.log(`[V3 Sell Profit] totalSellAmount: ${totalSellAmount}, estimatedBNBOut: ${estimatedBNBOut}, estimatedBNB: ${ethers.formatEther(estimatedBNBOut)}`);
|
|
1219
753
|
if (estimatedBNBOut <= 0n)
|
|
1220
754
|
return 0n;
|
|
1221
755
|
const profit = calculateProfitAmount(estimatedBNBOut);
|
|
1222
|
-
console.log(`[V3 Sell Profit] profit: ${profit}, profitBNB: ${ethers.formatEther(profit)}`);
|
|
1223
756
|
return profit;
|
|
1224
757
|
}
|
|
1225
758
|
else if (quoteToken) {
|
|
1226
759
|
// 卖出代币 → 得到 ERC20:先获取 V3 报价,再计算利润
|
|
1227
760
|
const estimatedBNBOut = chain.toUpperCase() === 'XLAYER'
|
|
1228
|
-
? await quoteXLayerV3TokenToNativeBySlot0({
|
|
761
|
+
? await quoteXLayerV3TokenToNativeBySlot0({
|
|
762
|
+
provider,
|
|
763
|
+
tokenIn: tokenAddress,
|
|
764
|
+
amountIn: totalSellAmount,
|
|
765
|
+
fee,
|
|
766
|
+
})
|
|
1229
767
|
: await getTokenToNativeQuote(provider, tokenAddress, totalSellAmount, chain, 'v3', fee);
|
|
1230
768
|
if (estimatedBNBOut <= 0n)
|
|
1231
769
|
return 0n;
|
|
@@ -1235,7 +773,7 @@ export async function directV3BatchSell(params) {
|
|
|
1235
773
|
return 0n;
|
|
1236
774
|
}
|
|
1237
775
|
catch (error) {
|
|
1238
|
-
console.error(`[V3 Sell Profit] Error:`, error);
|
|
776
|
+
console.error(`[V3 Sell Profit] Error:`, getErrorMessageFromUnknown(error));
|
|
1239
777
|
return 0n;
|
|
1240
778
|
}
|
|
1241
779
|
})();
|
|
@@ -1245,36 +783,69 @@ export async function directV3BatchSell(params) {
|
|
|
1245
783
|
// 构建卖出交易数据的辅助函数
|
|
1246
784
|
const buildV3SellTxData = (wallet, sellAmount) => {
|
|
1247
785
|
if (useLegacyRouter) {
|
|
1248
|
-
const swapParams = {
|
|
786
|
+
const swapParams = {
|
|
787
|
+
tokenIn: tokenAddress,
|
|
788
|
+
tokenOut: outputToken,
|
|
789
|
+
fee,
|
|
790
|
+
recipient: useNativeOutput ? routerAddress : wallet.address,
|
|
791
|
+
deadline,
|
|
792
|
+
amountIn: sellAmount,
|
|
793
|
+
amountOutMinimum: 0n,
|
|
794
|
+
sqrtPriceLimitX96: 0n,
|
|
795
|
+
};
|
|
1249
796
|
if (useNativeOutput) {
|
|
1250
|
-
return routerIface.encodeFunctionData('multicall(bytes[])', [
|
|
797
|
+
return routerIface.encodeFunctionData('multicall(bytes[])', [
|
|
798
|
+
[
|
|
799
|
+
routerIface.encodeFunctionData('exactInputSingle', [swapParams]),
|
|
800
|
+
routerIface.encodeFunctionData('unwrapWETH9', [0n, wallet.address]),
|
|
801
|
+
],
|
|
802
|
+
]);
|
|
1251
803
|
}
|
|
1252
804
|
// ✅ 修复:ERC20 输出也需要使用 multicall 包装以传递 deadline
|
|
1253
|
-
return routerIface.encodeFunctionData('multicall(bytes[])', [
|
|
805
|
+
return routerIface.encodeFunctionData('multicall(bytes[])', [
|
|
806
|
+
[routerIface.encodeFunctionData('exactInputSingle', [swapParams])],
|
|
807
|
+
]);
|
|
1254
808
|
}
|
|
1255
809
|
else {
|
|
1256
|
-
const swapParams = {
|
|
810
|
+
const swapParams = {
|
|
811
|
+
tokenIn: tokenAddress,
|
|
812
|
+
tokenOut: outputToken,
|
|
813
|
+
fee,
|
|
814
|
+
recipient: useNativeOutput ? routerAddress : wallet.address,
|
|
815
|
+
amountIn: sellAmount,
|
|
816
|
+
amountOutMinimum: 0n,
|
|
817
|
+
sqrtPriceLimitX96: 0n,
|
|
818
|
+
};
|
|
1257
819
|
if (useNativeOutput) {
|
|
1258
|
-
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
820
|
+
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
821
|
+
deadline,
|
|
822
|
+
[
|
|
823
|
+
routerIface.encodeFunctionData('exactInputSingle', [swapParams]),
|
|
824
|
+
routerIface.encodeFunctionData('unwrapWETH9', [0n, wallet.address]),
|
|
825
|
+
],
|
|
826
|
+
]);
|
|
1259
827
|
}
|
|
1260
828
|
// ✅ 修复:ERC20 输出也需要使用 multicall 包装以传递 deadline
|
|
1261
|
-
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
829
|
+
return routerIface.encodeFunctionData('multicall(uint256,bytes[])', [
|
|
830
|
+
deadline,
|
|
831
|
+
[routerIface.encodeFunctionData('exactInputSingle', [swapParams])],
|
|
832
|
+
]);
|
|
1262
833
|
}
|
|
1263
834
|
};
|
|
1264
835
|
const maxOutputIndex = findMaxFlowIndex(sellAmountsWei);
|
|
1265
836
|
const bribeWei = getBribeAmount(config, chain);
|
|
1266
837
|
const hasBribe = bribeWei > 0n && wallets.length > 0;
|
|
1267
|
-
const nonceOffsets = wallets.map((_, i) => i === maxOutputIndex && hasBribe ? 1 : 0);
|
|
838
|
+
const nonceOffsets = wallets.map((_, i) => (i === maxOutputIndex && hasBribe ? 1 : 0));
|
|
1268
839
|
// ✅ 方案 A:并行签名所有交易(贿赂、卖出)+ 并行获取 ERC20 报价
|
|
1269
840
|
const signPromises = [];
|
|
1270
841
|
if (hasBribe) {
|
|
1271
|
-
signPromises.push(buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], gasPrice, chainId, txType)
|
|
1272
|
-
.then(tx => ({ type: 'bribe', index: 0, tx })));
|
|
842
|
+
signPromises.push(buildBribeTransaction(wallets[maxOutputIndex], bribeWei, nonces[maxOutputIndex], gasPrice, chainId, txType).then((tx) => ({ type: 'bribe', index: 0, tx })));
|
|
1273
843
|
}
|
|
1274
844
|
wallets.forEach((wallet, i) => {
|
|
1275
845
|
if (sellAmountsWei[i] <= 0n)
|
|
1276
846
|
return;
|
|
1277
|
-
signPromises.push(wallet
|
|
847
|
+
signPromises.push(wallet
|
|
848
|
+
.signTransaction({
|
|
1278
849
|
to: routerAddress,
|
|
1279
850
|
data: buildV3SellTxData(wallet, sellAmountsWei[i]),
|
|
1280
851
|
value: 0n,
|
|
@@ -1282,13 +853,11 @@ export async function directV3BatchSell(params) {
|
|
|
1282
853
|
gasLimit,
|
|
1283
854
|
...buildGasFields(txType, gasPrice),
|
|
1284
855
|
chainId,
|
|
1285
|
-
})
|
|
856
|
+
})
|
|
857
|
+
.then((tx) => ({ type: 'swap', index: i, tx })));
|
|
1286
858
|
});
|
|
1287
859
|
// ✅ 并行执行:签名 + ERC20 报价
|
|
1288
|
-
const [signedResults, nativeProfitWei] = await Promise.all([
|
|
1289
|
-
Promise.all(signPromises),
|
|
1290
|
-
nativeProfitPromise
|
|
1291
|
-
]);
|
|
860
|
+
const [signedResults, nativeProfitWei] = await Promise.all([Promise.all(signPromises), nativeProfitPromise]);
|
|
1292
861
|
let profitWei = nativeProfitWei > 0n ? nativeProfitWei : 0n;
|
|
1293
862
|
if (profitWei > 0n && chain.toUpperCase() === 'ENI') {
|
|
1294
863
|
profitWei += GAS_LIMITS.NATIVE_TRANSFER * gasPrice;
|
|
@@ -1297,7 +866,6 @@ export async function directV3BatchSell(params) {
|
|
|
1297
866
|
const profitMode = config.profitMode || 'single';
|
|
1298
867
|
const skipProfit = config.skipProfit === true;
|
|
1299
868
|
const profitAddr = getProfitRecipient();
|
|
1300
|
-
console.log('🔧 [SDK directV3BatchSell] profitMode:', profitMode, 'skipProfit:', skipProfit, 'wallets:', wallets.length);
|
|
1301
869
|
// 利润多跳转账
|
|
1302
870
|
let profitTxs = [];
|
|
1303
871
|
let profitHopWallets;
|
|
@@ -1311,9 +879,7 @@ export async function directV3BatchSell(params) {
|
|
|
1311
879
|
profitHopWallets = [];
|
|
1312
880
|
// 计算每个钱包的利润(按卖出金额比例分配)
|
|
1313
881
|
for (let i = 0; i < wallets.length; i++) {
|
|
1314
|
-
const walletProfit = totalSellAmount > 0n
|
|
1315
|
-
? (profitWei * sellAmountsWei[i]) / totalSellAmount
|
|
1316
|
-
: 0n;
|
|
882
|
+
const walletProfit = totalSellAmount > 0n ? (profitWei * sellAmountsWei[i]) / totalSellAmount : 0n;
|
|
1317
883
|
if (walletProfit > 0n) {
|
|
1318
884
|
const walletProfitNonce = nonces[i] + nonceOffsets[i] + 1;
|
|
1319
885
|
const profitResult = await buildProfitHopTransactions({
|
|
@@ -1325,7 +891,7 @@ export async function directV3BatchSell(params) {
|
|
|
1325
891
|
gasPrice,
|
|
1326
892
|
chainId,
|
|
1327
893
|
txType,
|
|
1328
|
-
startNonce: walletProfitNonce
|
|
894
|
+
startNonce: walletProfitNonce,
|
|
1329
895
|
});
|
|
1330
896
|
profitTxs.push(...profitResult.signedTransactions);
|
|
1331
897
|
if (profitResult.hopWallets) {
|
|
@@ -1344,8 +910,11 @@ export async function directV3BatchSell(params) {
|
|
|
1344
910
|
}
|
|
1345
911
|
// 按类型分组并按顺序组装
|
|
1346
912
|
const validResults = signedResults.filter((r) => r !== null);
|
|
1347
|
-
const bribeTxs = validResults.filter(r => r.type === 'bribe').map(r => r.tx);
|
|
1348
|
-
const swapTxs = validResults
|
|
913
|
+
const bribeTxs = validResults.filter((r) => r.type === 'bribe').map((r) => r.tx);
|
|
914
|
+
const swapTxs = validResults
|
|
915
|
+
.filter((r) => r.type === 'swap')
|
|
916
|
+
.sort((a, b) => a.index - b.index)
|
|
917
|
+
.map((r) => r.tx);
|
|
1349
918
|
const signedTxs = [...bribeTxs, ...swapTxs, ...profitTxs];
|
|
1350
919
|
return {
|
|
1351
920
|
signedTransactions: signedTxs,
|
|
@@ -1358,6 +927,9 @@ export async function directV3BatchSell(params) {
|
|
|
1358
927
|
},
|
|
1359
928
|
};
|
|
1360
929
|
}
|
|
930
|
+
// ============================================================================
|
|
931
|
+
// 辅助函数:获取 Router 地址
|
|
932
|
+
// ============================================================================
|
|
1361
933
|
/**
|
|
1362
934
|
* 根据链和 DEX 获取 Router 地址
|
|
1363
935
|
*/
|