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