four-flap-meme-sdk 2.2.2 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +41 -6
- package/README.md +31 -0
- package/README.zh-CN.md +41 -6
- package/dist/__tests__/subpath-exports.test.d.ts +1 -0
- package/dist/__tests__/subpath-exports.test.js +64 -0
- package/dist/abis/common.d.ts +85 -0
- package/dist/abis/common.js +264 -0
- package/dist/abis/contracts/TaxToken.json +969 -0
- package/dist/abis/contracts/TokenManager2.json +136 -0
- package/dist/abis/contracts/index.d.ts +5 -0
- package/dist/abis/contracts/index.js +5 -0
- package/dist/abis/flap/index.d.ts +3 -0
- package/dist/abis/flap/index.js +3 -0
- package/dist/abis/flap/portal-events.d.ts +6 -0
- package/dist/abis/flap/portal-events.js +17 -0
- package/dist/abis/flap/portal.d.ts +6 -0
- package/dist/abis/flap/portal.js +37 -0
- package/dist/abis/flap/vault.d.ts +171 -0
- package/dist/abis/flap/vault.js +91 -0
- package/dist/abis/index.d.ts +8 -0
- package/dist/abis/index.js +11 -0
- package/dist/bundle-core/__tests__/config-helpers.test.d.ts +1 -0
- package/dist/bundle-core/__tests__/config-helpers.test.js +28 -0
- package/dist/bundle-core/__tests__/facade-parity.test.d.ts +1 -0
- package/dist/bundle-core/__tests__/facade-parity.test.js +33 -0
- package/dist/bundle-core/__tests__/sign-context-helpers.test.d.ts +1 -0
- package/dist/bundle-core/__tests__/sign-context-helpers.test.js +60 -0
- package/dist/bundle-core/__tests__/sign-fixture.test.d.ts +1 -0
- package/dist/bundle-core/__tests__/sign-fixture.test.js +220 -0
- package/dist/bundle-core/__tests__/sign-fixtures.d.ts +10 -0
- package/dist/bundle-core/__tests__/sign-fixtures.js +16 -0
- package/dist/bundle-core/config-helpers.d.ts +36 -0
- package/dist/bundle-core/config-helpers.js +57 -0
- package/dist/bundle-core/errors.d.ts +50 -0
- package/dist/bundle-core/errors.js +35 -0
- package/dist/bundle-core/four-meme/approve-tokenmanager.d.ts +7 -0
- package/dist/bundle-core/four-meme/approve-tokenmanager.js +99 -0
- package/dist/bundle-core/four-meme/core-helpers.d.ts +8 -0
- package/dist/bundle-core/four-meme/core-helpers.js +40 -0
- package/dist/bundle-core/four-meme/core.d.ts +4 -0
- package/dist/bundle-core/four-meme/core.js +515 -0
- package/dist/bundle-core/four-meme/pancake-proxy.d.ts +28 -0
- package/dist/bundle-core/four-meme/pancake-proxy.js +679 -0
- package/dist/bundle-core/four-meme/private.d.ts +27 -0
- package/dist/bundle-core/four-meme/private.js +465 -0
- package/dist/bundle-core/four-meme/sign-context-helpers.d.ts +2 -0
- package/dist/bundle-core/four-meme/sign-context-helpers.js +2 -0
- package/dist/bundle-core/four-meme/swap-buy-first.d.ts +8 -0
- package/dist/bundle-core/four-meme/swap-buy-first.js +493 -0
- package/dist/bundle-core/four-meme/swap-hop-helpers.d.ts +6 -0
- package/dist/bundle-core/four-meme/swap-hop-helpers.js +63 -0
- package/dist/bundle-core/four-meme/swap-internal.d.ts +3 -0
- package/dist/bundle-core/four-meme/swap-internal.js +18 -0
- package/dist/bundle-core/four-meme/swap-sign-helpers.d.ts +27 -0
- package/dist/bundle-core/four-meme/swap-sign-helpers.js +105 -0
- package/dist/bundle-core/four-meme/swap.d.ts +17 -0
- package/dist/bundle-core/four-meme/swap.js +505 -0
- package/dist/bundle-core/four-meme/types/buy-first.d.ts +50 -0
- package/dist/bundle-core/four-meme/types/buy-first.js +1 -0
- package/dist/bundle-core/four-meme/types/core-flow.d.ts +63 -0
- package/dist/bundle-core/four-meme/types/core-flow.js +1 -0
- package/dist/bundle-core/four-meme/types/index.d.ts +600 -0
- package/dist/bundle-core/four-meme/types/index.js +1 -0
- package/dist/bundle-core/four-meme/types/swap-internal.d.ts +19 -0
- package/dist/bundle-core/four-meme/types/swap-internal.js +1 -0
- package/dist/bundle-core/four-meme/types.d.ts +1 -0
- package/dist/bundle-core/four-meme/types.js +1 -0
- package/dist/bundle-core/four-meme/utils-disperse.d.ts +7 -0
- package/dist/bundle-core/four-meme/utils-disperse.js +396 -0
- package/dist/bundle-core/four-meme/utils-pairwise.d.ts +8 -0
- package/dist/bundle-core/four-meme/utils-pairwise.js +328 -0
- package/dist/bundle-core/four-meme/utils-sweep.d.ts +8 -0
- package/dist/bundle-core/four-meme/utils-sweep.js +744 -0
- package/dist/bundle-core/index.d.ts +8 -0
- package/dist/bundle-core/index.js +8 -0
- package/dist/bundle-core/internal.d.ts +21 -0
- package/dist/bundle-core/internal.js +182 -0
- package/dist/bundle-core/sign-context-helpers.d.ts +25 -0
- package/dist/bundle-core/sign-context-helpers.js +67 -0
- package/dist/bundle-core/submit.d.ts +293 -0
- package/dist/bundle-core/submit.js +727 -0
- package/dist/bundle-core/types/index.d.ts +8 -0
- package/dist/bundle-core/types/index.js +1 -0
- package/dist/bundle-core/types.d.ts +1 -0
- package/dist/bundle-core/types.js +1 -0
- package/dist/chains/bsc/four/approve-tokenmanager.d.ts +1 -26
- package/dist/chains/bsc/four/approve-tokenmanager.js +1 -113
- package/dist/chains/bsc/four/config.d.ts +5 -67
- package/dist/chains/bsc/four/config.js +2 -114
- package/dist/chains/bsc/four/core.d.ts +1 -4
- package/dist/chains/bsc/four/core.js +1 -592
- package/dist/chains/bsc/four/index.d.ts +6 -6
- package/dist/chains/bsc/four/index.js +6 -6
- package/dist/chains/bsc/four/internal.d.ts +2 -46
- package/dist/chains/bsc/four/internal.js +2 -239
- package/dist/chains/bsc/four/pancake-proxy.d.ts +1 -28
- package/dist/chains/bsc/four/pancake-proxy.js +1 -687
- package/dist/chains/bsc/four/private.d.ts +1 -27
- package/dist/chains/bsc/four/private.js +1 -477
- package/dist/chains/bsc/four/submit.d.ts +2 -315
- package/dist/chains/bsc/four/submit.js +2 -752
- package/dist/chains/bsc/four/swap-buy-first.d.ts +2 -55
- package/dist/chains/bsc/four/swap-buy-first.js +2 -507
- package/dist/chains/bsc/four/swap-internal.d.ts +1 -3
- package/dist/chains/bsc/four/swap-internal.js +1 -18
- package/dist/chains/bsc/four/swap.d.ts +2 -144
- package/dist/chains/bsc/four/swap.js +2 -766
- package/dist/chains/bsc/four/types.d.ts +1 -476
- package/dist/chains/bsc/four/utils-disperse.d.ts +1 -0
- package/dist/chains/bsc/four/utils-disperse.js +1 -0
- package/dist/chains/bsc/four/utils-pairwise.d.ts +1 -0
- package/dist/chains/bsc/four/utils-pairwise.js +1 -0
- package/dist/chains/bsc/four/utils-sweep.d.ts +1 -0
- package/dist/chains/bsc/four/utils-sweep.js +1 -0
- package/dist/chains/bsc/four/utils.d.ts +5 -18
- package/dist/chains/bsc/four/utils.js +5 -1552
- package/dist/chains/bsc/iro.d.ts +5 -0
- package/dist/chains/bsc/iro.js +4 -0
- package/dist/chains/bsc/pancake/bundle-buy-first-helpers.d.ts +159 -0
- package/dist/chains/bsc/pancake/bundle-buy-first-helpers.js +117 -0
- package/dist/chains/bsc/pancake/bundle-buy-first.d.ts +1 -91
- package/dist/chains/bsc/pancake/bundle-buy-first.js +97 -212
- package/dist/chains/bsc/pancake/bundle-swap-helpers.d.ts +241 -0
- package/dist/chains/bsc/pancake/bundle-swap-helpers.js +565 -0
- package/dist/chains/bsc/pancake/bundle-swap.d.ts +1 -79
- package/dist/chains/bsc/pancake/bundle-swap.js +114 -726
- package/dist/chains/bsc/pancake/index.d.ts +4 -2
- package/dist/chains/bsc/pancake/index.js +1 -3
- package/dist/chains/bsc/platforms/iro/factory.d.ts +2 -2
- package/dist/chains/bsc/platforms/iro/factory.js +3 -1
- package/dist/chains/bsc/platforms/iro/index.d.ts +5 -5
- package/dist/chains/bsc/platforms/iro/index.js +3 -3
- package/dist/chains/bsc/platforms/iro/pool.js +31 -10
- package/dist/chains/bsc/platforms/iro/token.js +4 -1
- package/dist/chains/eni/batch-router/bundle-approve.js +4 -3
- package/dist/chains/eni/batch-router/transfer.js +55 -25
- package/dist/chains/eni/batch-router/utils.js +32 -6
- package/dist/chains/eni/bundler/sign.js +5 -6
- package/dist/chains/eni/bundler/submit.js +1 -4
- package/dist/chains/eni/constants.js +1 -1
- package/dist/chains/eni/flat-aliases.d.ts +10 -0
- package/dist/chains/eni/flat-aliases.js +8 -0
- package/dist/chains/eni/index.d.ts +2 -1
- package/dist/chains/eni/index.js +1 -0
- package/dist/chains/eni/platforms/daoaas/create.js +2 -2
- package/dist/chains/eni/platforms/daoaas/index.d.ts +3 -3
- package/dist/chains/eni/platforms/daoaas/index.js +3 -3
- package/dist/chains/eni/platforms/daoaas/meta.js +9 -6
- package/dist/chains/eni/platforms/daoaas/portal-direct.js +28 -44
- package/dist/chains/eni/platforms/daoaas/portal.js +10 -6
- package/dist/chains/eni/platforms/dswap/liquidity.js +58 -26
- package/dist/chains/eni/platforms/fair-launch/index.d.ts +2 -2
- package/dist/chains/eni/platforms/fair-launch/index.js +1 -1
- package/dist/chains/eni/platforms/fair-launch/launcher.js +87 -46
- package/dist/chains/eni/platforms/fair-launch/pool.js +4 -1
- package/dist/chains/eni/platforms/fair-launch/presets.js +2 -2
- package/dist/chains/eni/platforms/iro/factory.d.ts +2 -2
- package/dist/chains/eni/platforms/iro/factory.js +3 -1
- package/dist/chains/eni/platforms/iro/index.d.ts +6 -6
- package/dist/chains/eni/platforms/iro/index.js +4 -4
- package/dist/chains/eni/platforms/iro/pool.js +90 -26
- package/dist/chains/eni/platforms/iro/token.js +107 -31
- package/dist/chains/eni/platforms/iro/whitelist.js +6 -18
- package/dist/chains/eni/submit.d.ts +43 -0
- package/dist/chains/eni/submit.js +286 -0
- package/dist/chains/index.d.ts +13 -0
- package/dist/chains/index.js +13 -0
- package/dist/chains/xlayer/eip7702/bundle-approve.d.ts +2 -26
- package/dist/chains/xlayer/eip7702/bundle-approve.js +11 -21
- package/dist/chains/xlayer/eip7702/bundle-buy.d.ts +2 -6
- package/dist/chains/xlayer/eip7702/bundle-buy.js +13 -51
- package/dist/chains/xlayer/eip7702/bundle-create.js +93 -59
- package/dist/chains/xlayer/eip7702/bundle-sell.d.ts +2 -6
- package/dist/chains/xlayer/eip7702/bundle-sell.js +29 -111
- package/dist/chains/xlayer/eip7702/bundle-swap.d.ts +3 -65
- package/dist/chains/xlayer/eip7702/bundle-swap.js +51 -245
- package/dist/chains/xlayer/eip7702/constants.d.ts +1 -16
- package/dist/chains/xlayer/eip7702/constants.js +3 -21
- package/dist/chains/xlayer/eip7702/flat-aliases.d.ts +13 -0
- package/dist/chains/xlayer/eip7702/flat-aliases.js +10 -0
- package/dist/chains/xlayer/eip7702/index.d.ts +28 -46
- package/dist/chains/xlayer/eip7702/index.js +28 -81
- package/dist/chains/xlayer/eip7702/multi-hop-transfer-helpers.d.ts +79 -0
- package/dist/chains/xlayer/eip7702/multi-hop-transfer-helpers.js +1 -0
- package/dist/chains/xlayer/eip7702/multi-hop-transfer.d.ts +2 -203
- package/dist/chains/xlayer/eip7702/multi-hop-transfer.js +63 -307
- package/dist/chains/xlayer/eip7702/transfer-context-helpers.d.ts +26 -0
- package/dist/chains/xlayer/eip7702/transfer-context-helpers.js +57 -0
- package/dist/chains/xlayer/eip7702/types.d.ts +88 -0
- package/dist/chains/xlayer/eip7702/utils.d.ts +0 -3
- package/dist/chains/xlayer/eip7702/utils.js +23 -28
- package/dist/chains/xlayer/eip7702/volume-helpers.d.ts +148 -0
- package/dist/chains/xlayer/eip7702/volume-helpers.js +48 -0
- package/dist/chains/xlayer/eip7702/volume.d.ts +6 -184
- package/dist/chains/xlayer/eip7702/volume.js +89 -164
- package/dist/chains/xlayer/eoa/constants.js +1 -1
- package/dist/chains/xlayer/eoa/dex-helpers.js +5 -5
- package/dist/chains/xlayer/eoa/eoa-bundle-swap-helpers.d.ts +126 -0
- package/dist/chains/xlayer/eoa/eoa-bundle-swap-helpers.js +228 -0
- package/dist/chains/xlayer/eoa/eoa-bundle-swap.d.ts +1 -95
- package/dist/chains/xlayer/eoa/eoa-bundle-swap.js +66 -299
- package/dist/chains/xlayer/eoa/eoa-wash-volume.d.ts +1 -1
- package/dist/chains/xlayer/eoa/eoa-wash-volume.js +18 -23
- package/dist/chains/xlayer/eoa/index.d.ts +10 -6
- package/dist/chains/xlayer/eoa/index.js +8 -23
- package/dist/chains/xlayer/eoa/portal-ops.js +7 -2
- package/dist/chains/xlayer/eoa/router-manager.js +3 -3
- package/dist/chains/xlayer/eoa/types.d.ts +2 -2
- package/dist/chains/xlayer/eoa/types.js +1 -3
- package/dist/chains/xlayer/index.d.ts +3 -2
- package/dist/chains/xlayer/index.js +4 -7
- package/dist/contracts/helper3.d.ts +20 -5
- package/dist/contracts/helper3.js +56 -20
- package/dist/contracts/tm-bundle-helpers.d.ts +88 -0
- package/dist/contracts/tm-bundle-helpers.js +72 -0
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.d.ts +1 -26
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.js +1 -113
- package/dist/contracts/tm-bundle-merkle/config.d.ts +5 -67
- package/dist/contracts/tm-bundle-merkle/config.js +2 -114
- package/dist/contracts/tm-bundle-merkle/core.d.ts +1 -4
- package/dist/contracts/tm-bundle-merkle/core.js +1 -591
- package/dist/contracts/tm-bundle-merkle/index.d.ts +5 -5
- package/dist/contracts/tm-bundle-merkle/index.js +5 -5
- package/dist/contracts/tm-bundle-merkle/internal.d.ts +2 -46
- package/dist/contracts/tm-bundle-merkle/internal.js +2 -238
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.d.ts +1 -28
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +1 -686
- package/dist/contracts/tm-bundle-merkle/private.d.ts +1 -27
- package/dist/contracts/tm-bundle-merkle/private.js +1 -476
- package/dist/contracts/tm-bundle-merkle/submit.d.ts +3 -314
- package/dist/contracts/tm-bundle-merkle/submit.js +3 -928
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +2 -55
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +2 -506
- package/dist/contracts/tm-bundle-merkle/swap-internal.d.ts +1 -3
- package/dist/contracts/tm-bundle-merkle/swap-internal.js +1 -18
- package/dist/contracts/tm-bundle-merkle/swap.d.ts +2 -144
- package/dist/contracts/tm-bundle-merkle/swap.js +2 -764
- package/dist/contracts/tm-bundle-merkle/types.d.ts +1 -476
- package/dist/contracts/tm-bundle-merkle/utils-disperse.d.ts +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-disperse.js +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-pairwise.d.ts +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-pairwise.js +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-sweep.d.ts +1 -0
- package/dist/contracts/tm-bundle-merkle/utils-sweep.js +1 -0
- package/dist/contracts/tm-bundle-merkle/utils.d.ts +6 -18
- package/dist/contracts/tm-bundle-merkle/utils.js +6 -1501
- package/dist/contracts/tm-bundle.d.ts +3 -51
- package/dist/contracts/tm-bundle.js +108 -177
- package/dist/contracts/tm.d.ts +3 -2
- package/dist/contracts/tm.js +37 -32
- package/dist/contracts/tm1.js +9 -4
- package/dist/contracts/tm2.js +9 -4
- package/dist/dex/direct-router-helpers.d.ts +264 -0
- package/dist/dex/direct-router-helpers.js +539 -0
- package/dist/dex/direct-router.d.ts +3 -125
- package/dist/dex/direct-router.js +237 -666
- package/dist/dex/types.d.ts +81 -0
- package/dist/dex/types.js +1 -0
- package/dist/exports/root-bundle-and-tooling.d.ts +27 -0
- package/dist/exports/root-bundle-and-tooling.js +30 -0
- package/dist/exports/root-eni-and-bsc-iro.d.ts +26 -0
- package/dist/exports/root-eni-and-bsc-iro.js +66 -0
- package/dist/exports/root-foundations.d.ts +35 -0
- package/dist/exports/root-foundations.js +70 -0
- package/dist/exports/root-swap-dex-and-xlayer.d.ts +30 -0
- package/dist/exports/root-swap-dex-and-xlayer.js +78 -0
- package/dist/flap/index.d.ts +10 -0
- package/dist/flap/index.js +8 -0
- package/dist/flows/create.d.ts +2 -1
- package/dist/flows/create.js +6 -6
- package/dist/flows/index.d.ts +1 -0
- package/dist/flows/index.js +1 -0
- package/dist/index.d.ts +20 -85
- package/dist/index.js +20 -215
- package/dist/merkle/index.d.ts +12 -0
- package/dist/merkle/index.js +11 -0
- package/dist/shared/abis/common.d.ts +2 -83
- package/dist/shared/abis/common.js +2 -252
- package/dist/shared/abis/index.d.ts +5 -6
- package/dist/shared/abis/index.js +5 -7
- package/dist/shared/clients/blockrazor.js +39 -25
- package/dist/shared/clients/club48.d.ts +2 -2
- package/dist/shared/clients/club48.js +34 -29
- package/dist/shared/clients/emitservice.js +2 -0
- package/dist/shared/clients/four.d.ts +21 -6
- package/dist/shared/clients/four.js +29 -24
- package/dist/shared/clients/index.d.ts +8 -0
- package/dist/shared/clients/index.js +8 -0
- package/dist/shared/clients/merkle.js +27 -34
- package/dist/shared/constants/addresses.d.ts +1 -1
- package/dist/shared/constants/addresses.js +11 -2
- package/dist/shared/constants/chains.d.ts +1 -1
- package/dist/shared/constants/chains.js +1 -1
- package/dist/shared/constants/gas.d.ts +1 -1
- package/dist/shared/constants/gas.js +2 -6
- package/dist/shared/constants/index.d.ts +3 -0
- package/dist/shared/constants/index.js +1 -0
- package/dist/shared/constants/quote.d.ts +30 -0
- package/dist/shared/constants/quote.js +37 -0
- package/dist/shared/flap/abi.js +1 -1
- package/dist/shared/flap/constants.d.ts +1 -2
- package/dist/shared/flap/constants.js +2 -3
- package/dist/shared/flap/curve.js +3 -0
- package/dist/shared/flap/errors.d.ts +1 -4
- package/dist/shared/flap/errors.js +20 -1
- package/dist/shared/flap/index.d.ts +5 -4
- package/dist/shared/flap/index.js +5 -4
- package/dist/shared/flap/meta.d.ts +22 -18
- package/dist/shared/flap/meta.js +12 -17
- package/dist/shared/flap/permit.js +5 -2
- package/dist/shared/flap/pinata.d.ts +22 -6
- package/dist/shared/flap/pinata.js +21 -26
- package/dist/shared/flap/portal-bundle-merkle/config.d.ts +3 -72
- package/dist/shared/flap/portal-bundle-merkle/config.js +4 -124
- package/dist/shared/flap/portal-bundle-merkle/core-helpers.d.ts +32 -0
- package/dist/shared/flap/portal-bundle-merkle/core-helpers.js +83 -0
- package/dist/shared/flap/portal-bundle-merkle/core.d.ts +0 -4
- package/dist/shared/flap/portal-bundle-merkle/core.js +86 -278
- package/dist/shared/flap/portal-bundle-merkle/create-to-dex.d.ts +7 -2
- package/dist/shared/flap/portal-bundle-merkle/create-to-dex.js +101 -206
- package/dist/shared/flap/portal-bundle-merkle/curve-to-dex.js +100 -92
- package/dist/shared/flap/portal-bundle-merkle/index.d.ts +11 -7
- package/dist/shared/flap/portal-bundle-merkle/index.js +4 -7
- package/dist/shared/flap/portal-bundle-merkle/pancake-proxy.js +71 -68
- package/dist/shared/flap/portal-bundle-merkle/private.js +61 -114
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first-helpers.d.ts +125 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first-helpers.js +113 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first.d.ts +1 -64
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first.js +66 -247
- package/dist/shared/flap/portal-bundle-merkle/swap-helpers.d.ts +149 -0
- package/dist/shared/flap/portal-bundle-merkle/swap-helpers.js +259 -0
- package/dist/shared/flap/portal-bundle-merkle/swap.d.ts +2 -71
- package/dist/shared/flap/portal-bundle-merkle/swap.js +103 -410
- package/dist/shared/flap/portal-bundle-merkle/types.d.ts +83 -9
- package/dist/shared/flap/portal-bundle-merkle/utils.d.ts +1 -80
- package/dist/shared/flap/portal-bundle-merkle/utils.js +145 -265
- package/dist/shared/flap/portal-bundle.js +55 -56
- package/dist/shared/flap/portal-create-token.d.ts +77 -0
- package/dist/shared/flap/portal-create-token.js +214 -0
- package/dist/shared/flap/portal.d.ts +14 -3
- package/dist/shared/flap/portal.js +50 -25
- package/dist/shared/flap/vanity.d.ts +1 -5
- package/dist/shared/flap/vanity.js +6 -17
- package/dist/shared/flap/vault.d.ts +17 -124
- package/dist/shared/flap/vault.js +67 -148
- package/dist/shared/foundation/dex/v3-path.d.ts +6 -0
- package/dist/shared/foundation/dex/v3-path.js +35 -0
- package/dist/shared/foundation/gas/bundle-gas.d.ts +49 -0
- package/dist/shared/foundation/gas/bundle-gas.js +93 -0
- package/dist/shared/foundation/gas/profit-hop.d.ts +20 -0
- package/dist/shared/foundation/gas/profit-hop.js +72 -0
- package/dist/shared/foundation/index.d.ts +13 -0
- package/dist/shared/foundation/index.js +12 -0
- package/dist/shared/foundation/nonce/nonce-manager.d.ts +17 -0
- package/dist/shared/foundation/nonce/nonce-manager.js +183 -0
- package/dist/shared/foundation/normalize-unknown.d.ts +9 -0
- package/dist/shared/foundation/normalize-unknown.js +29 -0
- package/dist/shared/foundation/sdk-logger.d.ts +13 -0
- package/dist/shared/foundation/sdk-logger.js +12 -0
- package/dist/shared/foundation/tx/build-request.d.ts +17 -0
- package/dist/shared/foundation/tx/build-request.js +25 -0
- package/dist/shared/foundation/tx/sign-batch.d.ts +5 -0
- package/dist/shared/foundation/tx/sign-batch.js +26 -0
- package/dist/shared/foundation/tx/wallet-sign-patch.d.ts +1 -0
- package/dist/shared/foundation/tx/wallet-sign-patch.js +18 -0
- package/dist/shared/foundation/types/airdrop-sweep.d.ts +79 -0
- package/dist/shared/foundation/types/airdrop-sweep.js +1 -0
- package/dist/shared/foundation/types/erc20.d.ts +65 -0
- package/dist/shared/foundation/types/erc20.js +1 -0
- package/dist/shared/foundation/types/holders-maker.d.ts +64 -0
- package/dist/shared/foundation/types/holders-maker.js +1 -0
- package/dist/shared/foundation/types/index.d.ts +7 -0
- package/dist/shared/foundation/types/index.js +1 -0
- package/dist/shared/foundation/types/lp-inspect.d.ts +102 -0
- package/dist/shared/foundation/types/lp-inspect.js +1 -0
- package/dist/shared/foundation/types/multicall.d.ts +5 -0
- package/dist/shared/foundation/types/multicall.js +1 -0
- package/dist/shared/foundation/types/private-sale.d.ts +35 -0
- package/dist/shared/foundation/types/private-sale.js +1 -0
- package/dist/shared/foundation/types/quote-helpers.d.ts +17 -0
- package/dist/shared/foundation/types/quote-helpers.js +1 -0
- package/dist/shared/four/tax-token.d.ts +1 -1
- package/dist/shared/four/tax-token.js +27 -7
- package/dist/shared/index.d.ts +6 -0
- package/dist/shared/index.js +4 -0
- package/dist/types/errors.d.ts +27 -0
- package/dist/types/errors.js +34 -0
- package/dist/utils/airdrop-sweep.d.ts +4 -76
- package/dist/utils/airdrop-sweep.js +42 -55
- package/dist/utils/bundle-helpers.d.ts +9 -243
- package/dist/utils/bundle-helpers.js +10 -584
- package/dist/utils/constants.d.ts +5 -61
- package/dist/utils/constants.js +5 -80
- package/dist/utils/contract-factory.d.ts +2 -4
- package/dist/utils/contract-factory.js +25 -18
- package/dist/utils/erc20.d.ts +7 -89
- package/dist/utils/erc20.js +94 -125
- package/dist/utils/errors.d.ts +12 -1
- package/dist/utils/errors.js +60 -1
- package/dist/utils/holders-maker/addresses.d.ts +12 -0
- package/dist/utils/holders-maker/addresses.js +15 -0
- package/dist/utils/holders-maker/buy-tx.d.ts +44 -0
- package/dist/utils/holders-maker/buy-tx.js +278 -0
- package/dist/utils/holders-maker/constants.d.ts +6 -0
- package/dist/utils/holders-maker/constants.js +7 -0
- package/dist/utils/holders-maker/disperse.d.ts +18 -0
- package/dist/utils/holders-maker/disperse.js +90 -0
- package/dist/utils/holders-maker/routing.d.ts +4 -0
- package/dist/utils/holders-maker/routing.js +45 -0
- package/dist/utils/holders-maker/transfer-tx.d.ts +4 -0
- package/dist/utils/holders-maker/transfer-tx.js +67 -0
- package/dist/utils/holders-maker-helpers.d.ts +9 -0
- package/dist/utils/holders-maker-helpers.js +9 -0
- package/dist/utils/holders-maker.d.ts +2 -138
- package/dist/utils/holders-maker.js +26 -661
- package/dist/utils/hop-chains.d.ts +35 -0
- package/dist/utils/hop-chains.js +215 -0
- package/dist/utils/lp-inspect-helpers.d.ts +9 -0
- package/dist/utils/lp-inspect-helpers.js +109 -0
- package/dist/utils/lp-inspect.d.ts +2 -112
- package/dist/utils/lp-inspect.js +73 -223
- package/dist/utils/mpcExclusive.d.ts +2 -5
- package/dist/utils/mpcExclusive.js +4 -3
- package/dist/utils/private-sale.d.ts +2 -58
- package/dist/utils/private-sale.js +4 -15
- package/dist/utils/provider-factory.d.ts +4 -0
- package/dist/utils/provider-factory.js +10 -0
- package/dist/utils/quote-helpers.d.ts +4 -45
- package/dist/utils/quote-helpers.js +17 -74
- package/dist/utils/stealth-transfer.d.ts +2 -28
- package/dist/utils/stealth-transfer.js +31 -15
- package/dist/utils/swap-helpers.d.ts +2 -15
- package/dist/utils/swap-helpers.js +6 -11
- package/dist/utils/types/airdrop-sweep.d.ts +1 -0
- package/dist/utils/types/airdrop-sweep.js +1 -0
- package/dist/utils/types/contract-factory.d.ts +1 -0
- package/dist/utils/types/contract-factory.js +1 -0
- package/dist/utils/types/erc20.d.ts +1 -0
- package/dist/utils/types/erc20.js +1 -0
- package/dist/utils/types/errors.d.ts +1 -0
- package/dist/utils/types/errors.js +1 -0
- package/dist/utils/types/holders-maker.d.ts +1 -0
- package/dist/utils/types/holders-maker.js +1 -0
- package/dist/utils/types/hop-chains.d.ts +8 -0
- package/dist/utils/types/hop-chains.js +1 -0
- package/dist/utils/types/index.d.ts +13 -0
- package/dist/utils/types/index.js +1 -0
- package/dist/utils/types/lp-inspect.d.ts +1 -0
- package/dist/utils/types/lp-inspect.js +1 -0
- package/dist/utils/types/mpc-exclusive.d.ts +5 -0
- package/dist/utils/types/mpc-exclusive.js +1 -0
- package/dist/utils/types/private-sale.d.ts +1 -0
- package/dist/utils/types/private-sale.js +1 -0
- package/dist/utils/types/quote-helpers.d.ts +1 -0
- package/dist/utils/types/quote-helpers.js +1 -0
- package/dist/utils/types/stealth-transfer.d.ts +44 -0
- package/dist/utils/types/stealth-transfer.js +1 -0
- package/dist/utils/types/wallet.d.ts +25 -0
- package/dist/utils/types/wallet.js +1 -0
- package/dist/utils/wallet.d.ts +2 -25
- package/dist/utils/wallet.js +13 -10
- package/dist/vanity/index.d.ts +5 -0
- package/dist/vanity/index.js +5 -0
- package/package.json +160 -4
- package/src/abis/contracts/TaxToken.json +969 -0
- package/src/abis/contracts/TokenManager.json +836 -0
- package/src/abis/contracts/TokenManager2.json +136 -0
- package/src/abis/contracts/TokenManagerHelper3.json +993 -0
- package/dist/shared/abis/TaxToken.json +0 -105
- package/dist/shared/abis/TokenManager2.json +0 -60
- /package/dist/{shared/abis → abis/contracts}/TokenManager.json +0 -0
- /package/dist/{shared/abis → abis/contracts}/TokenManagerHelper3.json +0 -0
|
@@ -1,592 +1,15 @@
|
|
|
1
|
-
// ==================== 多跳转账常量(使用统一常量) ====================
|
|
2
|
-
import { GAS_LIMITS, CHAINS } from '../../../shared/constants/index.js';
|
|
3
|
-
const NATIVE_TRANSFER_GAS_LIMIT = GAS_LIMITS.NATIVE_TRANSFER;
|
|
4
|
-
const ERC20_TRANSFER_GAS_LIMIT_HOP = GAS_LIMITS.ERC20_TRANSFER;
|
|
5
|
-
const BRIBE_GAS_LIMIT = GAS_LIMITS.BRIBE;
|
|
6
1
|
/**
|
|
7
|
-
*
|
|
8
|
-
*/
|
|
9
|
-
function generateDisperseHopPaths(targetAddresses, hopCount, provider) {
|
|
10
|
-
if (hopCount <= 0) {
|
|
11
|
-
return targetAddresses.map(addr => ({
|
|
12
|
-
targetAddress: addr,
|
|
13
|
-
hopWallets: [],
|
|
14
|
-
hopWalletsInfo: []
|
|
15
|
-
}));
|
|
16
|
-
}
|
|
17
|
-
return targetAddresses.map(targetAddress => {
|
|
18
|
-
const hopWalletsInfo = generateWallets(hopCount);
|
|
19
|
-
const hopWallets = hopWalletsInfo.map(w => new Wallet(w.privateKey, provider));
|
|
20
|
-
return { targetAddress, hopWallets, hopWalletsInfo };
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* 构建原生代币多跳转账链
|
|
25
|
-
*/
|
|
26
|
-
async function buildNativeHopChain(payer, path, finalAmount, gasPrice, chainId, txType, payerNonce) {
|
|
27
|
-
const signedTxs = [];
|
|
28
|
-
const hopCount = path.hopWallets.length;
|
|
29
|
-
if (hopCount === 0) {
|
|
30
|
-
const tx = {
|
|
31
|
-
to: path.targetAddress,
|
|
32
|
-
value: finalAmount,
|
|
33
|
-
nonce: payerNonce,
|
|
34
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
35
|
-
chainId,
|
|
36
|
-
type: txType
|
|
37
|
-
};
|
|
38
|
-
if (txType === 2) {
|
|
39
|
-
tx.maxFeePerGas = gasPrice;
|
|
40
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
tx.gasPrice = gasPrice;
|
|
44
|
-
}
|
|
45
|
-
signedTxs.push(await payer.signTransaction(tx));
|
|
46
|
-
return signedTxs;
|
|
47
|
-
}
|
|
48
|
-
const hopGasCost = NATIVE_TRANSFER_GAS_LIMIT * gasPrice;
|
|
49
|
-
// 计算每跳需要的金额
|
|
50
|
-
const amountsPerHop = [];
|
|
51
|
-
for (let i = 0; i < hopCount; i++) {
|
|
52
|
-
const remainingHops = hopCount - i;
|
|
53
|
-
amountsPerHop.push(finalAmount + hopGasCost * BigInt(remainingHops));
|
|
54
|
-
}
|
|
55
|
-
// payer → hop1
|
|
56
|
-
const firstTx = {
|
|
57
|
-
to: path.hopWallets[0].address,
|
|
58
|
-
value: amountsPerHop[0],
|
|
59
|
-
nonce: payerNonce,
|
|
60
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
61
|
-
chainId,
|
|
62
|
-
type: txType
|
|
63
|
-
};
|
|
64
|
-
if (txType === 2) {
|
|
65
|
-
firstTx.maxFeePerGas = gasPrice;
|
|
66
|
-
firstTx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
firstTx.gasPrice = gasPrice;
|
|
70
|
-
}
|
|
71
|
-
signedTxs.push(await payer.signTransaction(firstTx));
|
|
72
|
-
// hop1 → hop2 → ... → target
|
|
73
|
-
for (let i = 0; i < hopCount; i++) {
|
|
74
|
-
const fromWallet = path.hopWallets[i];
|
|
75
|
-
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
76
|
-
const amount = i === hopCount - 1 ? finalAmount : amountsPerHop[i + 1];
|
|
77
|
-
const tx = {
|
|
78
|
-
to: toAddress,
|
|
79
|
-
value: amount,
|
|
80
|
-
nonce: 0,
|
|
81
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
82
|
-
chainId,
|
|
83
|
-
type: txType
|
|
84
|
-
};
|
|
85
|
-
if (txType === 2) {
|
|
86
|
-
tx.maxFeePerGas = gasPrice;
|
|
87
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
tx.gasPrice = gasPrice;
|
|
91
|
-
}
|
|
92
|
-
signedTxs.push(await fromWallet.signTransaction(tx));
|
|
93
|
-
}
|
|
94
|
-
return signedTxs;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* 构建 ERC20 多跳转账链(需要配合 BNB 多跳使用)
|
|
98
|
-
*/
|
|
99
|
-
async function buildERC20HopChain(payer, path, erc20Address, erc20Amount, gasPrice, chainId, txType, payerNonce) {
|
|
100
|
-
const signedTxs = [];
|
|
101
|
-
const hopCount = path.hopWallets.length;
|
|
102
|
-
const erc20Interface = new ethers.Interface(ERC20_ABI);
|
|
103
|
-
if (hopCount === 0) {
|
|
104
|
-
const data = erc20Interface.encodeFunctionData('transfer', [path.targetAddress, erc20Amount]);
|
|
105
|
-
const tx = {
|
|
106
|
-
to: erc20Address,
|
|
107
|
-
data,
|
|
108
|
-
value: 0n,
|
|
109
|
-
nonce: payerNonce,
|
|
110
|
-
gasLimit: ERC20_TRANSFER_GAS_LIMIT_HOP,
|
|
111
|
-
chainId,
|
|
112
|
-
type: txType
|
|
113
|
-
};
|
|
114
|
-
if (txType === 2) {
|
|
115
|
-
tx.maxFeePerGas = gasPrice;
|
|
116
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
tx.gasPrice = gasPrice;
|
|
120
|
-
}
|
|
121
|
-
signedTxs.push(await payer.signTransaction(tx));
|
|
122
|
-
return signedTxs;
|
|
123
|
-
}
|
|
124
|
-
// payer → hop1
|
|
125
|
-
const firstData = erc20Interface.encodeFunctionData('transfer', [path.hopWallets[0].address, erc20Amount]);
|
|
126
|
-
const firstTx = {
|
|
127
|
-
to: erc20Address,
|
|
128
|
-
data: firstData,
|
|
129
|
-
value: 0n,
|
|
130
|
-
nonce: payerNonce,
|
|
131
|
-
gasLimit: ERC20_TRANSFER_GAS_LIMIT_HOP,
|
|
132
|
-
chainId,
|
|
133
|
-
type: txType
|
|
134
|
-
};
|
|
135
|
-
if (txType === 2) {
|
|
136
|
-
firstTx.maxFeePerGas = gasPrice;
|
|
137
|
-
firstTx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
firstTx.gasPrice = gasPrice;
|
|
141
|
-
}
|
|
142
|
-
signedTxs.push(await payer.signTransaction(firstTx));
|
|
143
|
-
// hop1 → hop2 → ... → target (nonce=1,nonce=0 用于 BNB 转发)
|
|
144
|
-
for (let i = 0; i < hopCount; i++) {
|
|
145
|
-
const fromWallet = path.hopWallets[i];
|
|
146
|
-
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
147
|
-
const data = erc20Interface.encodeFunctionData('transfer', [toAddress, erc20Amount]);
|
|
148
|
-
const tx = {
|
|
149
|
-
to: erc20Address,
|
|
150
|
-
data,
|
|
151
|
-
value: 0n,
|
|
152
|
-
nonce: 1, // nonce=0 已用于 BNB 转发
|
|
153
|
-
gasLimit: ERC20_TRANSFER_GAS_LIMIT_HOP,
|
|
154
|
-
chainId,
|
|
155
|
-
type: txType
|
|
156
|
-
};
|
|
157
|
-
if (txType === 2) {
|
|
158
|
-
tx.maxFeePerGas = gasPrice;
|
|
159
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
tx.gasPrice = gasPrice;
|
|
163
|
-
}
|
|
164
|
-
signedTxs.push(await fromWallet.signTransaction(tx));
|
|
165
|
-
}
|
|
166
|
-
return signedTxs;
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* 构建 BNB 多跳转账链(为 ERC20 中间钱包预留 gas)
|
|
170
|
-
*/
|
|
171
|
-
async function buildBNBHopChainForERC20(payer, path, finalGasAmount, gasPrice, chainId, txType, payerNonce) {
|
|
172
|
-
const signedTxs = [];
|
|
173
|
-
const hopCount = path.hopWallets.length;
|
|
174
|
-
if (hopCount === 0) {
|
|
175
|
-
// 无多跳时不需要转 gas
|
|
176
|
-
return signedTxs;
|
|
177
|
-
}
|
|
178
|
-
const hopGasCost = NATIVE_TRANSFER_GAS_LIMIT * gasPrice;
|
|
179
|
-
const erc20GasCost = ERC20_TRANSFER_GAS_LIMIT_HOP * gasPrice;
|
|
180
|
-
// 每个中间钱包需要: BNB 转发 gas + ERC20 转发 gas
|
|
181
|
-
const gasPerHop = hopGasCost + erc20GasCost * 2n; // 2倍安全系数
|
|
182
|
-
// 计算每跳需要的金额
|
|
183
|
-
const amountsPerHop = [];
|
|
184
|
-
for (let i = 0; i < hopCount; i++) {
|
|
185
|
-
const remainingHops = hopCount - i;
|
|
186
|
-
amountsPerHop.push(finalGasAmount + gasPerHop * BigInt(remainingHops));
|
|
187
|
-
}
|
|
188
|
-
// payer → hop1
|
|
189
|
-
const firstTx = {
|
|
190
|
-
to: path.hopWallets[0].address,
|
|
191
|
-
value: amountsPerHop[0],
|
|
192
|
-
nonce: payerNonce,
|
|
193
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
194
|
-
chainId,
|
|
195
|
-
type: txType
|
|
196
|
-
};
|
|
197
|
-
if (txType === 2) {
|
|
198
|
-
firstTx.maxFeePerGas = gasPrice;
|
|
199
|
-
firstTx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
firstTx.gasPrice = gasPrice;
|
|
203
|
-
}
|
|
204
|
-
signedTxs.push(await payer.signTransaction(firstTx));
|
|
205
|
-
// hop1 → hop2 → ... → target (nonce=0)
|
|
206
|
-
for (let i = 0; i < hopCount; i++) {
|
|
207
|
-
const fromWallet = path.hopWallets[i];
|
|
208
|
-
const toAddress = i === hopCount - 1 ? path.targetAddress : path.hopWallets[i + 1].address;
|
|
209
|
-
const amount = i === hopCount - 1 ? finalGasAmount : amountsPerHop[i + 1];
|
|
210
|
-
const tx = {
|
|
211
|
-
to: toAddress,
|
|
212
|
-
value: amount,
|
|
213
|
-
nonce: 0,
|
|
214
|
-
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
215
|
-
chainId,
|
|
216
|
-
type: txType
|
|
217
|
-
};
|
|
218
|
-
if (txType === 2) {
|
|
219
|
-
tx.maxFeePerGas = gasPrice;
|
|
220
|
-
tx.maxPriorityFeePerGas = gasPrice / 10n || 1n;
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
tx.gasPrice = gasPrice;
|
|
224
|
-
}
|
|
225
|
-
signedTxs.push(await fromWallet.signTransaction(tx));
|
|
226
|
-
}
|
|
227
|
-
return signedTxs;
|
|
228
|
-
}
|
|
229
|
-
// ==================== 工具函数 ====================
|
|
230
|
-
function createPancakeContext(config) {
|
|
231
|
-
const chainId = config.chainId ?? 56;
|
|
232
|
-
const provider = new ethers.JsonRpcProvider(config.rpcUrl, {
|
|
233
|
-
chainId,
|
|
234
|
-
name: 'BSC'
|
|
235
|
-
});
|
|
236
|
-
return { chainId, provider };
|
|
237
|
-
}
|
|
238
|
-
async function quoteSellOutput({ routeParams, sellAmountWei, provider, v2RouterAddress }) {
|
|
239
|
-
console.log(`[quoteSellOutput] 开始报价, routeType=${routeParams.routeType}, sellAmount=${sellAmountWei} wei`);
|
|
240
|
-
// ==================== V2 报价 ====================
|
|
241
|
-
if (routeParams.routeType === 'v2') {
|
|
242
|
-
const { v2Path } = routeParams;
|
|
243
|
-
const tokenIn = v2Path[0];
|
|
244
|
-
const tokenOut = v2Path[v2Path.length - 1];
|
|
245
|
-
console.log(`[quoteSellOutput] V2 路径: ${v2Path.join(' → ')}`);
|
|
246
|
-
const result = await quoteV2(provider, tokenIn, tokenOut, sellAmountWei, 'BSC', v2RouterAddress);
|
|
247
|
-
if (result.amountOut > 0n) {
|
|
248
|
-
console.log(`[quoteSellOutput] V2 报价成功: ${ethers.formatEther(result.amountOut)} BNB`);
|
|
249
|
-
return { estimatedBNBOut: result.amountOut };
|
|
250
|
-
}
|
|
251
|
-
throw new Error('V2 报价失败: 所有路径均无效');
|
|
252
|
-
}
|
|
253
|
-
// ==================== V3 单跳报价 ====================
|
|
254
|
-
if (routeParams.routeType === 'v3-single') {
|
|
255
|
-
const params = routeParams;
|
|
256
|
-
console.log(`[quoteSellOutput] V3 单跳: ${params.v3TokenIn} → ${params.v3TokenOut}, fee=${params.v3Fee}`);
|
|
257
|
-
// ✅ 只用 V3 报价,不 fallback 到 V2
|
|
258
|
-
const result = await quoteV3(provider, params.v3TokenIn, params.v3TokenOut, sellAmountWei, 'BSC', params.v3Fee);
|
|
259
|
-
if (result.amountOut > 0n) {
|
|
260
|
-
console.log(`[quoteSellOutput] V3 报价成功 (fee=${result.fee}): ${ethers.formatEther(result.amountOut)} BNB`);
|
|
261
|
-
return { estimatedBNBOut: result.amountOut };
|
|
262
|
-
}
|
|
263
|
-
// ❌ V3 报价失败时不再 fallback 到 V2,因为价格可能差异很大
|
|
264
|
-
throw new Error(`V3 单跳报价失败: tokenIn=${params.v3TokenIn}, tokenOut=${params.v3TokenOut}, fee=${params.v3Fee}`);
|
|
265
|
-
}
|
|
266
|
-
// ==================== V3 多跳报价 ====================
|
|
267
|
-
if (routeParams.routeType === 'v3-multi') {
|
|
268
|
-
const params = routeParams;
|
|
269
|
-
console.log(`[quoteSellOutput] V3 多跳: LPs=${params.v3LpAddresses?.join(', ')}`);
|
|
270
|
-
// ✅ V3 多跳:只用 V3 报价,不 fallback 到 V2
|
|
271
|
-
if (params.v3LpAddresses && params.v3LpAddresses.length > 0) {
|
|
272
|
-
const tokenIn = params.v3ExactTokenIn;
|
|
273
|
-
const WBNB = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c';
|
|
274
|
-
const result = await quoteV3(provider, tokenIn, WBNB, sellAmountWei, 'BSC');
|
|
275
|
-
if (result.amountOut > 0n) {
|
|
276
|
-
console.log(`[quoteSellOutput] V3 多跳报价成功: ${ethers.formatEther(result.amountOut)} BNB`);
|
|
277
|
-
return { estimatedBNBOut: result.amountOut };
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
// ❌ V3 多跳报价失败时不再 fallback 到 V2,因为价格可能差异很大
|
|
281
|
-
throw new Error(`V3 多跳报价失败: LPs=${params.v3LpAddresses?.join(', ')}, tokenIn=${params.v3ExactTokenIn}`);
|
|
282
|
-
}
|
|
283
|
-
throw new Error(`不支持的路由类型: ${routeParams.routeType}`);
|
|
284
|
-
}
|
|
285
|
-
async function buildSwapTransactions({ routeParams, sellAmountWei, buyAmountBNB, buyer, seller, tokenAddress, useNativeToken = true, v2RouterAddress }) {
|
|
286
|
-
const deadline = getDeadline();
|
|
287
|
-
const buyValue = useNativeToken ? buyAmountBNB + FLAT_FEE : 0n;
|
|
288
|
-
if (routeParams.routeType === 'v2') {
|
|
289
|
-
const { v2Path } = routeParams;
|
|
290
|
-
const reversePath = [...v2Path].reverse();
|
|
291
|
-
const routerAddr = v2RouterAddress || PANCAKE_V2_ROUTER_ADDRESS;
|
|
292
|
-
const v2RouterSeller = new Contract(routerAddr, PANCAKE_V2_ROUTER_ABI, seller);
|
|
293
|
-
const v2RouterBuyer = new Contract(routerAddr, PANCAKE_V2_ROUTER_ABI, buyer);
|
|
294
|
-
let sellUnsigned;
|
|
295
|
-
let buyUnsigned;
|
|
296
|
-
if (useNativeToken) {
|
|
297
|
-
// ✅ 原生代币模式(BNB)
|
|
298
|
-
// 卖出:Token → WBNB → BNB
|
|
299
|
-
sellUnsigned = await v2RouterSeller.swapExactTokensForETHSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
300
|
-
// 买入:BNB → WBNB → Token
|
|
301
|
-
buyUnsigned = await v2RouterBuyer.swapExactETHForTokensSupportingFeeOnTransferTokens.populateTransaction(0n, reversePath, buyer.address, deadline, { value: buyValue });
|
|
302
|
-
}
|
|
303
|
-
else {
|
|
304
|
-
// ✅ ERC20 模式(如 USDT)
|
|
305
|
-
// 卖出:Token → USDT
|
|
306
|
-
sellUnsigned = await v2RouterSeller.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(sellAmountWei, 0n, v2Path, seller.address, deadline);
|
|
307
|
-
// 买入:USDT → Token
|
|
308
|
-
buyUnsigned = await v2RouterBuyer.swapExactTokensForTokensSupportingFeeOnTransferTokens.populateTransaction(buyAmountBNB, // USDT 数量
|
|
309
|
-
0n, reversePath, buyer.address, deadline);
|
|
310
|
-
}
|
|
311
|
-
return { sellUnsigned, buyUnsigned };
|
|
312
|
-
}
|
|
313
|
-
if (routeParams.routeType === 'v3-single') {
|
|
314
|
-
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
315
|
-
const v3RouterIface = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
316
|
-
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
317
|
-
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
318
|
-
// 卖出:Token → WBNB,需要 unwrapWETH9
|
|
319
|
-
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
320
|
-
tokenIn: v3TokenIn,
|
|
321
|
-
tokenOut: v3TokenOut,
|
|
322
|
-
fee: v3Fee,
|
|
323
|
-
recipient: PANCAKE_V3_ROUTER_ADDRESS, // 先发给 Router
|
|
324
|
-
amountIn: sellAmountWei,
|
|
325
|
-
amountOutMinimum: 0n,
|
|
326
|
-
sqrtPriceLimitX96: 0n
|
|
327
|
-
}]);
|
|
328
|
-
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
329
|
-
const sellUnsigned = await v3RouterSeller.multicall.populateTransaction(deadline, [sellSwapData, sellUnwrapData]);
|
|
330
|
-
// 买入:WBNB → Token
|
|
331
|
-
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
332
|
-
tokenIn: v3TokenOut,
|
|
333
|
-
tokenOut: v3TokenIn,
|
|
334
|
-
fee: v3Fee,
|
|
335
|
-
recipient: buyer.address,
|
|
336
|
-
amountIn: buyAmountBNB,
|
|
337
|
-
amountOutMinimum: 0n,
|
|
338
|
-
sqrtPriceLimitX96: 0n
|
|
339
|
-
}]);
|
|
340
|
-
const buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
341
|
-
return { sellUnsigned, buyUnsigned };
|
|
342
|
-
}
|
|
343
|
-
// V3 多跳暂不支持官方合约
|
|
344
|
-
throw new Error('V3 多跳路由暂不支持官方合约,请使用 V2 路由或 V3 单跳');
|
|
345
|
-
}
|
|
346
|
-
async function calculateBuyerBudget({ buyer, quotedBNBOut, reserveGasBNB, useNativeToken = true, quoteToken, quoteTokenDecimals = 18, provider }) {
|
|
347
|
-
const reserveGas = ethers.parseEther((reserveGasBNB ?? 0.0005).toString());
|
|
348
|
-
// ✅ 已移除滑点保护:直接使用报价金额
|
|
349
|
-
const buyAmountBNB = quotedBNBOut;
|
|
350
|
-
// ✅ 根据是否使用原生代币获取不同的余额
|
|
351
|
-
let buyerBalance;
|
|
352
|
-
if (useNativeToken) {
|
|
353
|
-
buyerBalance = await buyer.provider.getBalance(buyer.address);
|
|
354
|
-
const requiredBalance = buyAmountBNB + FLAT_FEE + reserveGas;
|
|
355
|
-
if (buyerBalance < requiredBalance) {
|
|
356
|
-
throw new Error(`买方余额不足: 需要 ${ethers.formatEther(requiredBalance)} BNB, 实际 ${ethers.formatEther(buyerBalance)} BNB`);
|
|
357
|
-
}
|
|
358
|
-
return { buyerBalance, reserveGas, requiredBalance, buyAmountBNB, useNativeToken };
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
// ERC20 代币余额
|
|
362
|
-
const erc20 = new Contract(quoteToken, ERC20_BALANCE_OF_ABI, provider || buyer.provider);
|
|
363
|
-
buyerBalance = await erc20.balanceOf(buyer.address);
|
|
364
|
-
const requiredBalance = buyAmountBNB + FLAT_FEE; // ERC20 不需要预留 Gas 在代币余额中
|
|
365
|
-
if (buyerBalance < requiredBalance) {
|
|
366
|
-
throw new Error(`买方代币余额不足: 需要 ${ethers.formatUnits(requiredBalance, quoteTokenDecimals)}, 实际 ${ethers.formatUnits(buyerBalance, quoteTokenDecimals)}`);
|
|
367
|
-
}
|
|
368
|
-
// ✅ ERC20 购买时,还需要检查买方是否有足够 BNB 支付 Gas
|
|
369
|
-
const buyerBnbBalance = await buyer.provider.getBalance(buyer.address);
|
|
370
|
-
if (buyerBnbBalance < reserveGas) {
|
|
371
|
-
throw new Error(`买方 BNB 余额不足 (用于支付 Gas): 需要 ${ethers.formatEther(reserveGas)} BNB, 实际 ${ethers.formatEther(buyerBnbBalance)} BNB`);
|
|
372
|
-
}
|
|
373
|
-
return { buyerBalance, reserveGas, requiredBalance, buyAmountBNB, useNativeToken };
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* ✅ 从前端传入的 startNonces 构建 NoncePlan(用于性能优化)
|
|
378
|
-
* 顺序:[sellerBaseNonce, buyerNonce]
|
|
379
|
-
*/
|
|
380
|
-
function buildNoncePlanFromNonces(startNonces, sameAddress, approvalExists, profitNeeded, needBribeTx) {
|
|
381
|
-
if (sameAddress) {
|
|
382
|
-
let idx = 0;
|
|
383
|
-
const bribeNonce = needBribeTx ? startNonces[0] + idx++ : undefined;
|
|
384
|
-
if (approvalExists)
|
|
385
|
-
idx++;
|
|
386
|
-
const sellerNonce = startNonces[0] + idx++;
|
|
387
|
-
const buyerNonce = startNonces[0] + idx++;
|
|
388
|
-
const profitNonce = profitNeeded ? startNonces[0] + idx : undefined;
|
|
389
|
-
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
390
|
-
}
|
|
391
|
-
let sellerIdx = 0;
|
|
392
|
-
const bribeNonce = needBribeTx ? startNonces[0] + sellerIdx++ : undefined;
|
|
393
|
-
if (approvalExists)
|
|
394
|
-
sellerIdx++;
|
|
395
|
-
const sellerNonce = startNonces[0] + sellerIdx++;
|
|
396
|
-
const profitNonce = profitNeeded ? startNonces[0] + sellerIdx : undefined;
|
|
397
|
-
const buyerNonce = startNonces[1];
|
|
398
|
-
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* ✅ 规划 nonce
|
|
402
|
-
* 交易顺序:贿赂 → 授权 → 卖出 → 买入 → 利润
|
|
403
|
-
* 贿赂和利润由卖方发送
|
|
404
|
-
*/
|
|
405
|
-
async function planNonces({ seller, buyer, sameAddress, approvalExists, profitNeeded, needBribeTx, nonceManager }) {
|
|
406
|
-
if (sameAddress) {
|
|
407
|
-
// 同一地址:贿赂(可选) + 授权(可选) + 卖出 + 买入 + 利润(可选)
|
|
408
|
-
const txCount = countTruthy([needBribeTx, approvalExists, true, true, profitNeeded]);
|
|
409
|
-
const nonces = await nonceManager.getNextNonceBatch(buyer, txCount);
|
|
410
|
-
let idx = 0;
|
|
411
|
-
const bribeNonce = needBribeTx ? nonces[idx++] : undefined;
|
|
412
|
-
if (approvalExists)
|
|
413
|
-
idx++;
|
|
414
|
-
const sellerNonce = nonces[idx++];
|
|
415
|
-
const buyerNonce = nonces[idx++];
|
|
416
|
-
const profitNonce = profitNeeded ? nonces[idx] : undefined;
|
|
417
|
-
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
418
|
-
}
|
|
419
|
-
if (needBribeTx || approvalExists || profitNeeded) {
|
|
420
|
-
// 卖方需要多个 nonce:贿赂(可选) + 授权(可选) + 卖出 + 利润(可选)
|
|
421
|
-
const sellerTxCount = countTruthy([needBribeTx, approvalExists, true, profitNeeded]);
|
|
422
|
-
// ✅ 并行获取 seller 和 buyer 的 nonce
|
|
423
|
-
const [sellerNonces, buyerNonce] = await Promise.all([
|
|
424
|
-
nonceManager.getNextNonceBatch(seller, sellerTxCount),
|
|
425
|
-
nonceManager.getNextNonce(buyer)
|
|
426
|
-
]);
|
|
427
|
-
let idx = 0;
|
|
428
|
-
const bribeNonce = needBribeTx ? sellerNonces[idx++] : undefined;
|
|
429
|
-
if (approvalExists)
|
|
430
|
-
idx++;
|
|
431
|
-
const sellerNonce = sellerNonces[idx++];
|
|
432
|
-
const profitNonce = profitNeeded ? sellerNonces[idx] : undefined;
|
|
433
|
-
return { sellerNonce, buyerNonce, bribeNonce, profitNonce };
|
|
434
|
-
}
|
|
435
|
-
// ✅ 并行获取 seller 和 buyer 的 nonce
|
|
436
|
-
const [sellerNonce, buyerNonce] = await Promise.all([
|
|
437
|
-
nonceManager.getNextNonce(seller),
|
|
438
|
-
nonceManager.getNextNonce(buyer)
|
|
439
|
-
]);
|
|
440
|
-
return { sellerNonce, buyerNonce };
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* 根据 userType 计算利润率
|
|
444
|
-
*/
|
|
445
|
-
function getProfitRateBps(userType) {
|
|
446
|
-
if (userType === 'v0') {
|
|
447
|
-
return PROFIT_CONFIG.RATE_BPS_V0; // 0.06%
|
|
448
|
-
}
|
|
449
|
-
else if (userType === 'v1') {
|
|
450
|
-
return PROFIT_CONFIG.RATE_BPS_V1; // 0.05%
|
|
451
|
-
}
|
|
452
|
-
return PROFIT_CONFIG.RATE_BPS_V0; // 默认 0.06%
|
|
453
|
-
}
|
|
454
|
-
function calculateProfitAmount(estimatedBNBOut, userType) {
|
|
455
|
-
if (estimatedBNBOut <= 0n) {
|
|
456
|
-
return 0n;
|
|
457
|
-
}
|
|
458
|
-
const rateBps = getProfitRateBps(userType);
|
|
459
|
-
return (estimatedBNBOut * BigInt(rateBps)) / 10000n;
|
|
460
|
-
}
|
|
461
|
-
/**
|
|
462
|
-
* ✅ 获取 ERC20 代币 → 原生代币(BNB)的报价
|
|
463
|
-
* 用于将 ERC20 利润转换为 BNB
|
|
464
|
-
* 使用共享的报价函数
|
|
465
|
-
*/
|
|
466
|
-
async function getERC20ToNativeQuote(provider, tokenAddress, tokenAmount, version = 'v2', fee) {
|
|
467
|
-
if (tokenAmount <= 0n)
|
|
468
|
-
return 0n;
|
|
469
|
-
const tokenLower = tokenAddress.toLowerCase();
|
|
470
|
-
const wbnbLower = WBNB_ADDRESS.toLowerCase();
|
|
471
|
-
// 如果代币本身就是 WBNB,直接返回
|
|
472
|
-
if (tokenLower === wbnbLower) {
|
|
473
|
-
return tokenAmount;
|
|
474
|
-
}
|
|
475
|
-
// 使用共享的报价函数
|
|
476
|
-
if (version === 'v3') {
|
|
477
|
-
const result = await quoteV3(provider, tokenAddress, WBNB_ADDRESS, tokenAmount, 'BSC', fee);
|
|
478
|
-
if (result.amountOut > 0n) {
|
|
479
|
-
console.log(`[getERC20ToNativeQuote] V3 报价成功: ${ethers.formatEther(result.amountOut)} BNB`);
|
|
480
|
-
return result.amountOut;
|
|
481
|
-
}
|
|
482
|
-
console.warn(`[getERC20ToNativeQuote] V3 报价失败`);
|
|
483
|
-
return 0n;
|
|
484
|
-
}
|
|
485
|
-
// V2 报价
|
|
486
|
-
const result = await quoteV2(provider, tokenAddress, WBNB_ADDRESS, tokenAmount, 'BSC');
|
|
487
|
-
if (result.amountOut > 0n) {
|
|
488
|
-
console.log(`[getERC20ToNativeQuote] V2 报价成功: ${ethers.formatEther(result.amountOut)} BNB`);
|
|
489
|
-
return result.amountOut;
|
|
490
|
-
}
|
|
491
|
-
console.warn(`[getERC20ToNativeQuote] V2 报价失败`);
|
|
492
|
-
return 0n;
|
|
493
|
-
}
|
|
494
|
-
async function validateFinalBalances({ sameAddress, buyerBalance, buyAmountBNB, reserveGas, gasLimit, gasPrice, useNativeToken = true, quoteTokenDecimals = 18, provider, buyerAddress }) {
|
|
495
|
-
const gasCost = gasLimit * gasPrice;
|
|
496
|
-
if (sameAddress) {
|
|
497
|
-
// 同一地址:需要足够的余额支付两笔交易
|
|
498
|
-
if (useNativeToken) {
|
|
499
|
-
const requiredCombined = buyAmountBNB + FLAT_FEE * 2n + gasCost * 2n;
|
|
500
|
-
if (buyerBalance < requiredCombined) {
|
|
501
|
-
throw new Error(`账户余额不足:\n - 需要: ${ethers.formatEther(requiredCombined)} BNB(含两笔Gas与两笔手续费)\n - 实际: ${ethers.formatEther(buyerBalance)} BNB`);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
else {
|
|
505
|
-
// ERC20:检查代币余额 + BNB Gas 余额
|
|
506
|
-
const requiredToken = buyAmountBNB + FLAT_FEE * 2n;
|
|
507
|
-
if (buyerBalance < requiredToken) {
|
|
508
|
-
throw new Error(`账户代币余额不足:\n - 需要: ${ethers.formatUnits(requiredToken, quoteTokenDecimals)}\n - 实际: ${ethers.formatUnits(buyerBalance, quoteTokenDecimals)}`);
|
|
509
|
-
}
|
|
510
|
-
// 检查 BNB Gas
|
|
511
|
-
if (provider && buyerAddress) {
|
|
512
|
-
const bnbBalance = await provider.getBalance(buyerAddress);
|
|
513
|
-
const requiredGas = gasCost * 2n;
|
|
514
|
-
if (bnbBalance < requiredGas) {
|
|
515
|
-
throw new Error(`账户 BNB 余额不足 (用于支付 Gas):\n - 需要: ${ethers.formatEther(requiredGas)} BNB\n - 实际: ${ethers.formatEther(bnbBalance)} BNB`);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
// 不同地址
|
|
522
|
-
if (useNativeToken) {
|
|
523
|
-
const requiredBuyer = buyAmountBNB + FLAT_FEE + reserveGas;
|
|
524
|
-
if (buyerBalance < requiredBuyer) {
|
|
525
|
-
throw new Error(`买方余额不足:\n - 需要: ${ethers.formatEther(requiredBuyer)} BNB\n - 实际: ${ethers.formatEther(buyerBalance)} BNB`);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
// ERC20 余额已在 calculateBuyerBudget 中检查过
|
|
529
|
-
}
|
|
530
|
-
function countTruthy(values) {
|
|
531
|
-
return values.filter(Boolean).length;
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* PancakeSwap V2/V3 通用捆绑换手(Merkle Bundle)
|
|
535
|
-
*
|
|
536
|
-
* 功能:钱包A卖出代币 → 钱包B买入相同数量 → 原子执行
|
|
537
|
-
* 适用于所有已迁移到外盘的代币(不区分Four/Flap)
|
|
2
|
+
* bundle-swap - 主函数
|
|
538
3
|
*/
|
|
4
|
+
import { GAS_LIMITS, CHAINS, getProfitRecipient } from '../../../shared/constants/index.js';
|
|
539
5
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
540
6
|
import { calculateSellAmount } from '../../../utils/swap-helpers.js';
|
|
541
|
-
import { NonceManager, getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../../utils/bundle-helpers.js';
|
|
542
|
-
import { ADDRESSES,
|
|
543
|
-
import {
|
|
544
|
-
import {
|
|
545
|
-
import { generateWallets } from '../../../utils/wallet.js';
|
|
546
|
-
/**
|
|
547
|
-
* 获取 Gas Limit
|
|
548
|
-
*/
|
|
549
|
-
function getGasLimit(config, defaultGas = 800000) {
|
|
550
|
-
if (config.gasLimit !== undefined) {
|
|
551
|
-
return typeof config.gasLimit === 'bigint' ? config.gasLimit : BigInt(config.gasLimit);
|
|
552
|
-
}
|
|
553
|
-
const multiplier = config.gasLimitMultiplier ?? 1.0;
|
|
554
|
-
const calculatedGas = Math.ceil(defaultGas * multiplier);
|
|
555
|
-
return BigInt(calculatedGas);
|
|
556
|
-
}
|
|
557
|
-
async function getGasPrice(provider, config) {
|
|
558
|
-
const feeData = await provider.getFeeData();
|
|
559
|
-
let gasPrice = feeData.gasPrice || ethers.parseUnits('3', 'gwei');
|
|
560
|
-
if (config.minGasPriceGwei) {
|
|
561
|
-
const minGas = ethers.parseUnits(config.minGasPriceGwei.toString(), 'gwei');
|
|
562
|
-
if (gasPrice < minGas)
|
|
563
|
-
gasPrice = minGas;
|
|
564
|
-
}
|
|
565
|
-
if (config.maxGasPriceGwei) {
|
|
566
|
-
const maxGas = ethers.parseUnits(config.maxGasPriceGwei.toString(), 'gwei');
|
|
567
|
-
if (gasPrice > maxGas)
|
|
568
|
-
gasPrice = maxGas;
|
|
569
|
-
}
|
|
570
|
-
return gasPrice;
|
|
571
|
-
}
|
|
572
|
-
// ✅ ABI 别名(从公共模块导入)
|
|
573
|
-
const PANCAKE_V2_ROUTER_ABI = V2_ROUTER_ABI;
|
|
574
|
-
const PANCAKE_V3_ROUTER_ABI = V3_ROUTER02_ABI;
|
|
575
|
-
const ERC20_BALANCE_OF_ABI = ERC20_BALANCE_ABI;
|
|
576
|
-
// ✅ 使用官方 PancakeSwap Router 地址
|
|
577
|
-
const PANCAKE_V2_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV2Router;
|
|
578
|
-
const PANCAKE_V3_ROUTER_ADDRESS = ADDRESSES.BSC.PancakeV3Router;
|
|
579
|
-
// 常量
|
|
580
|
-
const FLAT_FEE = 0n;
|
|
581
|
-
const WBNB_ADDRESS = ADDRESSES.BSC.WBNB;
|
|
582
|
-
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
583
|
-
// ✅ ERC20_BALANCE_OF_ABI 已在文件开头定义
|
|
584
|
-
/**
|
|
585
|
-
* PancakeSwap捆绑换手(V2/V3通用)
|
|
586
|
-
* ✅ 支持 quoteToken:传入 USDT 等地址时,卖出得到该代币,买入使用该代币
|
|
587
|
-
*/
|
|
7
|
+
import { NonceManager, getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT, } from '../../../utils/bundle-helpers.js';
|
|
8
|
+
import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA } from '../../../shared/constants/index.js';
|
|
9
|
+
import { ERC20_ABI } from '../../../abis/common.js';
|
|
10
|
+
import { BRIBE_GAS_LIMIT, ERC20_BALANCE_OF_ABI, FLAT_FEE, NATIVE_TRANSFER_GAS_LIMIT, PANCAKE_V2_ROUTER_ABI, PANCAKE_V2_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, PANCAKE_V3_ROUTER_ADDRESS, ZERO_ADDRESS, buildBNBHopChainForERC20, buildERC20HopChain, buildNativeHopChain, buildNoncePlanFromNonces, buildSwapTransactions, calculateBuyerBudget, calculateProfitAmount, createPancakeContext, generateDisperseHopPaths, getERC20ToNativeQuote, getGasLimit, getGasPrice, planNonces, quoteSellOutput, validateFinalBalances, } from './bundle-swap-helpers.js';
|
|
588
11
|
export async function pancakeBundleSwapMerkle(params) {
|
|
589
|
-
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces // ✅ 可选:前端预获取的 nonces
|
|
12
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces, // ✅ 可选:前端预获取的 nonces
|
|
590
13
|
} = params;
|
|
591
14
|
// ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
|
|
592
15
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
@@ -601,14 +24,14 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
601
24
|
// ✅ 并行获取 gasPrice 和卖出数量
|
|
602
25
|
const [gasPrice, sellAmountResult] = await Promise.all([
|
|
603
26
|
getGasPrice(context.provider, config),
|
|
604
|
-
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
27
|
+
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage),
|
|
605
28
|
]);
|
|
606
29
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
607
30
|
const quoteResult = await quoteSellOutput({
|
|
608
31
|
routeParams,
|
|
609
32
|
sellAmountWei,
|
|
610
33
|
provider: context.provider,
|
|
611
|
-
v2RouterAddress: config.v2RouterAddress
|
|
34
|
+
v2RouterAddress: config.v2RouterAddress,
|
|
612
35
|
});
|
|
613
36
|
const buyerBudget = await calculateBuyerBudget({
|
|
614
37
|
buyer,
|
|
@@ -617,7 +40,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
617
40
|
useNativeToken,
|
|
618
41
|
quoteToken,
|
|
619
42
|
quoteTokenDecimals,
|
|
620
|
-
provider: context.provider
|
|
43
|
+
provider: context.provider,
|
|
621
44
|
});
|
|
622
45
|
const swapUnsigned = await buildSwapTransactions({
|
|
623
46
|
routeParams,
|
|
@@ -627,7 +50,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
627
50
|
seller,
|
|
628
51
|
tokenAddress,
|
|
629
52
|
useNativeToken,
|
|
630
|
-
v2RouterAddress: config.v2RouterAddress
|
|
53
|
+
v2RouterAddress: config.v2RouterAddress,
|
|
631
54
|
});
|
|
632
55
|
// ✅ 修复:利润计算应基于 BNB 数量,不是 ERC20 数量
|
|
633
56
|
// 如果输出是原生代币(BNB),直接使用报价结果
|
|
@@ -648,9 +71,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
648
71
|
// ERC20→BNB 报价完成
|
|
649
72
|
}
|
|
650
73
|
// ✅ 获取贿赂金额
|
|
651
|
-
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
652
|
-
? ethers.parseEther(String(config.bribeAmount))
|
|
653
|
-
: 0n;
|
|
74
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0 ? ethers.parseEther(String(config.bribeAmount)) : 0n;
|
|
654
75
|
const needBribeTx = bribeAmount > 0n;
|
|
655
76
|
// ✅ 使用共享的 NonceManager 规划 nonce
|
|
656
77
|
// 如果前端传入了 startNonces,直接使用(性能优化)
|
|
@@ -663,48 +84,54 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
663
84
|
approvalExists: false, // ✅ 已移除授权
|
|
664
85
|
profitNeeded: profitAmount > 0n,
|
|
665
86
|
needBribeTx,
|
|
666
|
-
nonceManager
|
|
87
|
+
nonceManager,
|
|
667
88
|
});
|
|
668
89
|
// ✅ 并行签名所有交易
|
|
669
90
|
const signPromises = [];
|
|
670
91
|
// 贿赂交易
|
|
671
92
|
if (needBribeTx && noncePlan.bribeNonce !== undefined) {
|
|
672
|
-
signPromises.push(seller
|
|
93
|
+
signPromises.push(seller
|
|
94
|
+
.signTransaction({
|
|
673
95
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
674
96
|
value: bribeAmount,
|
|
675
97
|
nonce: noncePlan.bribeNonce,
|
|
676
98
|
gasPrice,
|
|
677
99
|
gasLimit: BRIBE_GAS_LIMIT,
|
|
678
100
|
chainId: context.chainId,
|
|
679
|
-
type: txType
|
|
680
|
-
})
|
|
101
|
+
type: txType,
|
|
102
|
+
})
|
|
103
|
+
.then((tx) => ({ type: 'bribe', tx })));
|
|
681
104
|
}
|
|
682
105
|
// 卖出交易
|
|
683
|
-
signPromises.push(seller
|
|
106
|
+
signPromises.push(seller
|
|
107
|
+
.signTransaction({
|
|
684
108
|
...swapUnsigned.sellUnsigned,
|
|
685
109
|
from: seller.address,
|
|
686
110
|
nonce: noncePlan.sellerNonce,
|
|
687
111
|
gasLimit: finalGasLimit,
|
|
688
112
|
gasPrice,
|
|
689
113
|
chainId: context.chainId,
|
|
690
|
-
type: txType
|
|
691
|
-
})
|
|
114
|
+
type: txType,
|
|
115
|
+
})
|
|
116
|
+
.then((tx) => ({ type: 'sell', tx })));
|
|
692
117
|
// 买入交易
|
|
693
|
-
signPromises.push(buyer
|
|
118
|
+
signPromises.push(buyer
|
|
119
|
+
.signTransaction({
|
|
694
120
|
...swapUnsigned.buyUnsigned,
|
|
695
121
|
from: buyer.address,
|
|
696
122
|
nonce: noncePlan.buyerNonce,
|
|
697
123
|
gasLimit: finalGasLimit,
|
|
698
124
|
gasPrice,
|
|
699
125
|
chainId: context.chainId,
|
|
700
|
-
type: txType
|
|
701
|
-
})
|
|
126
|
+
type: txType,
|
|
127
|
+
})
|
|
128
|
+
.then((tx) => ({ type: 'buy', tx })));
|
|
702
129
|
// ✅ 并行执行所有签名
|
|
703
130
|
const signedResults = await Promise.all(signPromises);
|
|
704
131
|
// 按类型提取结果
|
|
705
|
-
const bribeTx = signedResults.find(r => r.type === 'bribe')?.tx || null;
|
|
706
|
-
const signedSell = signedResults.find(r => r.type === 'sell').tx;
|
|
707
|
-
const signedBuy = signedResults.find(r => r.type === 'buy').tx;
|
|
132
|
+
const bribeTx = signedResults.find((r) => r.type === 'bribe')?.tx || null;
|
|
133
|
+
const signedSell = signedResults.find((r) => r.type === 'sell').tx;
|
|
134
|
+
const signedBuy = signedResults.find((r) => r.type === 'buy').tx;
|
|
708
135
|
nonceManager.clearTemp();
|
|
709
136
|
validateFinalBalances({
|
|
710
137
|
sameAddress,
|
|
@@ -716,7 +143,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
716
143
|
useNativeToken,
|
|
717
144
|
quoteTokenDecimals,
|
|
718
145
|
provider: context.provider,
|
|
719
|
-
buyerAddress: buyer.address
|
|
146
|
+
buyerAddress: buyer.address,
|
|
720
147
|
});
|
|
721
148
|
// ✅ 组装顺序:贿赂 → 卖出 → 买入
|
|
722
149
|
const signedTransactions = [];
|
|
@@ -730,12 +157,12 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
730
157
|
provider: context.provider,
|
|
731
158
|
payerWallet: seller,
|
|
732
159
|
profitAmount,
|
|
733
|
-
profitRecipient:
|
|
160
|
+
profitRecipient: getProfitRecipient(),
|
|
734
161
|
hopCount: PROFIT_HOP_COUNT,
|
|
735
162
|
gasPrice,
|
|
736
163
|
chainId: context.chainId,
|
|
737
164
|
txType,
|
|
738
|
-
startNonce: noncePlan.profitNonce
|
|
165
|
+
startNonce: noncePlan.profitNonce,
|
|
739
166
|
});
|
|
740
167
|
signedTransactions.push(...profitHopResult.signedTransactions);
|
|
741
168
|
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
@@ -750,8 +177,8 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
750
177
|
buyAmount: useNativeToken
|
|
751
178
|
? ethers.formatEther(buyerBudget.buyAmountBNB)
|
|
752
179
|
: ethers.formatUnits(buyerBudget.buyAmountBNB, quoteTokenDecimals),
|
|
753
|
-
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
754
|
-
}
|
|
180
|
+
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
|
|
181
|
+
},
|
|
755
182
|
};
|
|
756
183
|
}
|
|
757
184
|
/**
|
|
@@ -763,7 +190,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
763
190
|
* 交易顺序:[授权(可选)] → [卖出] → [买入1, 买入2, ..., 买入N] → [利润]
|
|
764
191
|
*/
|
|
765
192
|
export async function pancakeBatchSwapMerkle(params) {
|
|
766
|
-
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces // ✅ 可选:前端预获取的 nonces
|
|
193
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces, // ✅ 可选:前端预获取的 nonces
|
|
767
194
|
} = params;
|
|
768
195
|
// ✅ 校验买方数量(最多 24 个)
|
|
769
196
|
const MAX_BUYERS = 24;
|
|
@@ -776,7 +203,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
776
203
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
777
204
|
const context = createPancakeContext(config);
|
|
778
205
|
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
779
|
-
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
206
|
+
const buyers = buyerPrivateKeys.map((pk) => new Wallet(pk, context.provider));
|
|
780
207
|
// ✅ 创建共享资源
|
|
781
208
|
const nonceManager = new NonceManager(context.provider);
|
|
782
209
|
const finalGasLimit = getGasLimit(config);
|
|
@@ -784,14 +211,14 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
784
211
|
// ✅ 并行获取 gasPrice 和卖出数量
|
|
785
212
|
const [gasPrice, sellAmountResult] = await Promise.all([
|
|
786
213
|
getGasPrice(context.provider, config),
|
|
787
|
-
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
214
|
+
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage),
|
|
788
215
|
]);
|
|
789
216
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
790
217
|
const quoteResult = await quoteSellOutput({
|
|
791
218
|
routeParams,
|
|
792
219
|
sellAmountWei,
|
|
793
220
|
provider: context.provider,
|
|
794
|
-
v2RouterAddress: config.v2RouterAddress
|
|
221
|
+
v2RouterAddress: config.v2RouterAddress,
|
|
795
222
|
});
|
|
796
223
|
const estimatedBNBOut = quoteResult.estimatedBNBOut;
|
|
797
224
|
// ✅ 计算每个买方的买入金额(已移除滑点保护:直接使用报价金额)
|
|
@@ -799,14 +226,12 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
799
226
|
const totalBuyAmount = estimatedBNBOut;
|
|
800
227
|
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
801
228
|
// 方式1:使用指定的买入金额(USDT)
|
|
802
|
-
buyAmountsWei = buyerAmounts.map(amt => useNativeToken
|
|
803
|
-
? ethers.parseEther(amt)
|
|
804
|
-
: ethers.parseUnits(amt, quoteTokenDecimals));
|
|
229
|
+
buyAmountsWei = buyerAmounts.map((amt) => useNativeToken ? ethers.parseEther(amt) : ethers.parseUnits(amt, quoteTokenDecimals));
|
|
805
230
|
}
|
|
806
231
|
else if (params.buyerRatios && params.buyerRatios.length === buyers.length) {
|
|
807
232
|
// ✅ 方式2:按比例分配卖出所得
|
|
808
233
|
// buyerRatios 如 [0.3, 0.5, 0.2] 表示第一个买方分 30%,第二个 50%,第三个 20%
|
|
809
|
-
buyAmountsWei = params.buyerRatios.map((ratio
|
|
234
|
+
buyAmountsWei = params.buyerRatios.map((ratio) => {
|
|
810
235
|
// 按比例计算每个买方的金额
|
|
811
236
|
const amount = (totalBuyAmount * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
812
237
|
return amount;
|
|
@@ -856,15 +281,17 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
856
281
|
else if (routeParams.routeType === 'v3-single') {
|
|
857
282
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
858
283
|
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
859
|
-
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
|
|
284
|
+
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
|
|
285
|
+
{
|
|
860
286
|
tokenIn: v3TokenIn,
|
|
861
287
|
tokenOut: v3TokenOut,
|
|
862
288
|
fee: v3Fee,
|
|
863
289
|
recipient: useNativeToken ? PANCAKE_V3_ROUTER_ADDRESS : seller.address,
|
|
864
290
|
amountIn: sellAmountWei,
|
|
865
291
|
amountOutMinimum: 0n,
|
|
866
|
-
sqrtPriceLimitX96: 0n
|
|
867
|
-
}
|
|
292
|
+
sqrtPriceLimitX96: 0n,
|
|
293
|
+
},
|
|
294
|
+
]);
|
|
868
295
|
if (useNativeToken) {
|
|
869
296
|
// 原生代币:需要 unwrap WBNB
|
|
870
297
|
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
@@ -897,15 +324,17 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
897
324
|
else if (routeParams.routeType === 'v3-single') {
|
|
898
325
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
899
326
|
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
900
|
-
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
|
|
327
|
+
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
|
|
328
|
+
{
|
|
901
329
|
tokenIn: v3TokenOut,
|
|
902
330
|
tokenOut: v3TokenIn,
|
|
903
331
|
fee: v3Fee,
|
|
904
332
|
recipient: buyer.address,
|
|
905
333
|
amountIn: buyAmount,
|
|
906
334
|
amountOutMinimum: 0n,
|
|
907
|
-
sqrtPriceLimitX96: 0n
|
|
908
|
-
}
|
|
335
|
+
sqrtPriceLimitX96: 0n,
|
|
336
|
+
},
|
|
337
|
+
]);
|
|
909
338
|
return await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
910
339
|
}
|
|
911
340
|
else {
|
|
@@ -913,16 +342,14 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
913
342
|
}
|
|
914
343
|
}));
|
|
915
344
|
// ✅ 获取贿赂金额
|
|
916
|
-
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
917
|
-
? ethers.parseEther(String(config.bribeAmount))
|
|
918
|
-
: 0n;
|
|
345
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0 ? ethers.parseEther(String(config.bribeAmount)) : 0n;
|
|
919
346
|
// ✅ 规划 Nonce(贿赂和利润都由卖方发送)
|
|
920
347
|
// 卖方: [贿赂(可选)] → [卖出] → [利润(可选)]
|
|
921
348
|
let bribeNonce;
|
|
922
349
|
let sellNonce;
|
|
923
350
|
let buyerNonces;
|
|
924
351
|
// ✅ 如果前端传入了 startNonces,直接使用(性能优化)
|
|
925
|
-
if (startNonces && startNonces.length >=
|
|
352
|
+
if (startNonces && startNonces.length >= 1 + buyers.length) {
|
|
926
353
|
let sellerIdx = 0;
|
|
927
354
|
if (bribeAmount > 0n) {
|
|
928
355
|
bribeNonce = startNonces[0] + sellerIdx++;
|
|
@@ -935,7 +362,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
935
362
|
bribeNonce = await nonceManager.getNextNonce(seller);
|
|
936
363
|
}
|
|
937
364
|
sellNonce = await nonceManager.getNextNonce(seller);
|
|
938
|
-
buyerNonces = await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
365
|
+
buyerNonces = await Promise.all(buyers.map((buyer) => nonceManager.getNextNonce(buyer)));
|
|
939
366
|
}
|
|
940
367
|
// ✅ 贿赂交易放在首位(由卖方发送)
|
|
941
368
|
let bribeTx = null;
|
|
@@ -947,7 +374,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
947
374
|
gasPrice,
|
|
948
375
|
gasLimit: BRIBE_GAS_LIMIT,
|
|
949
376
|
chainId: context.chainId,
|
|
950
|
-
type: txType
|
|
377
|
+
type: txType,
|
|
951
378
|
});
|
|
952
379
|
}
|
|
953
380
|
// ✅ 修复:利润计算应基于 BNB 数量,不是 ERC20 数量
|
|
@@ -965,7 +392,9 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
965
392
|
}
|
|
966
393
|
// 计算利润 nonce
|
|
967
394
|
const profitNonce = profitAmount > 0n
|
|
968
|
-
?
|
|
395
|
+
? startNonces && startNonces.length >= 1
|
|
396
|
+
? sellNonce + 1
|
|
397
|
+
: await nonceManager.getNextNonce(seller)
|
|
969
398
|
: undefined;
|
|
970
399
|
nonceManager.clearTemp();
|
|
971
400
|
// ✅ 并行签名所有交易
|
|
@@ -977,7 +406,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
977
406
|
gasLimit: finalGasLimit,
|
|
978
407
|
gasPrice,
|
|
979
408
|
chainId: context.chainId,
|
|
980
|
-
type: txType
|
|
409
|
+
type: txType,
|
|
981
410
|
});
|
|
982
411
|
// 2. 并行签名所有买入交易
|
|
983
412
|
const signedBuyPromises = buyers.map((buyer, i) => buyer.signTransaction({
|
|
@@ -987,13 +416,10 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
987
416
|
gasLimit: finalGasLimit,
|
|
988
417
|
gasPrice,
|
|
989
418
|
chainId: context.chainId,
|
|
990
|
-
type: txType
|
|
419
|
+
type: txType,
|
|
991
420
|
}));
|
|
992
421
|
// 3. 等待所有签名完成
|
|
993
|
-
const [signedSell, ...signedBuys] = await Promise.all([
|
|
994
|
-
signedSellPromise,
|
|
995
|
-
...signedBuyPromises
|
|
996
|
-
]);
|
|
422
|
+
const [signedSell, ...signedBuys] = await Promise.all([signedSellPromise, ...signedBuyPromises]);
|
|
997
423
|
// 4. 按顺序组装交易数组:贿赂 → 卖出 → 买入
|
|
998
424
|
const signedTransactions = [];
|
|
999
425
|
if (bribeTx)
|
|
@@ -1007,12 +433,12 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
1007
433
|
provider: context.provider,
|
|
1008
434
|
payerWallet: seller,
|
|
1009
435
|
profitAmount,
|
|
1010
|
-
profitRecipient:
|
|
436
|
+
profitRecipient: getProfitRecipient(),
|
|
1011
437
|
hopCount: PROFIT_HOP_COUNT,
|
|
1012
438
|
gasPrice,
|
|
1013
439
|
chainId: context.chainId,
|
|
1014
440
|
txType,
|
|
1015
|
-
startNonce: profitNonce
|
|
441
|
+
startNonce: profitNonce,
|
|
1016
442
|
});
|
|
1017
443
|
signedTransactions.push(...profitHopResult.signedTransactions);
|
|
1018
444
|
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
@@ -1022,13 +448,11 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
1022
448
|
profitHopWallets, // ✅ 导出利润多跳钱包
|
|
1023
449
|
metadata: {
|
|
1024
450
|
sellerAddress: seller.address,
|
|
1025
|
-
buyerAddresses: buyers.map(b => b.address),
|
|
451
|
+
buyerAddresses: buyers.map((b) => b.address),
|
|
1026
452
|
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
1027
|
-
buyAmounts: buyAmountsWei.map(amt => useNativeToken
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
1031
|
-
}
|
|
453
|
+
buyAmounts: buyAmountsWei.map((amt) => useNativeToken ? ethers.formatEther(amt) : ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
454
|
+
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
|
|
455
|
+
},
|
|
1032
456
|
};
|
|
1033
457
|
}
|
|
1034
458
|
/**
|
|
@@ -1047,7 +471,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
1047
471
|
*/
|
|
1048
472
|
export async function pancakeQuickBatchSwapMerkle(params) {
|
|
1049
473
|
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerRatios, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, disperseHopCount = 0, // ✅ 转账多跳数(默认0=直接转账)
|
|
1050
|
-
startNonces // ✅ 可选:前端预获取的 nonces
|
|
474
|
+
startNonces, // ✅ 可选:前端预获取的 nonces
|
|
1051
475
|
} = params;
|
|
1052
476
|
// ✅ 判断是否使用原生代币
|
|
1053
477
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
@@ -1074,7 +498,6 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1074
498
|
MAX_BUYERS = Math.floor(maxTxs / (2 * disperseHopCount + 3));
|
|
1075
499
|
}
|
|
1076
500
|
MAX_BUYERS = Math.max(1, MAX_BUYERS); // 至少1个
|
|
1077
|
-
console.log(`[pancakeQuickBatchSwapMerkle] 多跳数: ${disperseHopCount}, 最大买方数: ${MAX_BUYERS}`);
|
|
1078
501
|
if (buyerPrivateKeys.length === 0) {
|
|
1079
502
|
throw new Error('至少需要一个买方钱包');
|
|
1080
503
|
}
|
|
@@ -1094,7 +517,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1094
517
|
}
|
|
1095
518
|
const context = createPancakeContext(config);
|
|
1096
519
|
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
1097
|
-
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
520
|
+
const buyers = buyerPrivateKeys.map((pk) => new Wallet(pk, context.provider));
|
|
1098
521
|
// ✅ 校验卖出路径输出代币是否匹配
|
|
1099
522
|
let sellOutputToken;
|
|
1100
523
|
if (routeParams.routeType === 'v2') {
|
|
@@ -1134,30 +557,16 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1134
557
|
// ✅ 并行获取 gasPrice 和卖出数量
|
|
1135
558
|
const [gasPrice, sellAmountResult] = await Promise.all([
|
|
1136
559
|
getGasPrice(context.provider, config),
|
|
1137
|
-
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
560
|
+
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage),
|
|
1138
561
|
]);
|
|
1139
562
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
1140
|
-
// ✅ 调试日志
|
|
1141
|
-
console.log(`[pancakeQuickBatchSwapMerkle] 模式: ${useNativeToken ? 'BNB' : 'ERC20'}`);
|
|
1142
|
-
console.log(`[pancakeQuickBatchSwapMerkle] 卖出数量: ${ethers.formatUnits(sellAmountWei, decimals)}`);
|
|
1143
|
-
console.log(`[pancakeQuickBatchSwapMerkle] routeParams:`, JSON.stringify({
|
|
1144
|
-
routeType: routeParams.routeType,
|
|
1145
|
-
v2Path: routeParams.v2Path,
|
|
1146
|
-
v3TokenIn: routeParams.v3TokenIn,
|
|
1147
|
-
v3TokenOut: routeParams.v3TokenOut,
|
|
1148
|
-
v3Fee: routeParams.v3Fee
|
|
1149
|
-
}));
|
|
1150
563
|
const quoteResult = await quoteSellOutput({
|
|
1151
564
|
routeParams,
|
|
1152
565
|
sellAmountWei,
|
|
1153
566
|
provider: context.provider,
|
|
1154
|
-
v2RouterAddress: config.v2RouterAddress
|
|
567
|
+
v2RouterAddress: config.v2RouterAddress,
|
|
1155
568
|
});
|
|
1156
569
|
const estimatedOutput = quoteResult.estimatedBNBOut;
|
|
1157
|
-
const outputFormatted = useNativeToken
|
|
1158
|
-
? ethers.formatEther(estimatedOutput)
|
|
1159
|
-
: ethers.formatUnits(estimatedOutput, quoteTokenDecimals);
|
|
1160
|
-
console.log(`[pancakeQuickBatchSwapMerkle] 预估卖出所得: ${outputFormatted} ${useNativeToken ? 'BNB' : 'ERC20'}`);
|
|
1161
570
|
// ✅ 计算利润(万分之六)
|
|
1162
571
|
let profitAmount;
|
|
1163
572
|
if (useNativeToken) {
|
|
@@ -1169,16 +578,13 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1169
578
|
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
1170
579
|
const estimatedBNBValue = await getERC20ToNativeQuote(context.provider, quoteToken, estimatedOutput, version, fee);
|
|
1171
580
|
profitAmount = calculateProfitAmount(estimatedBNBValue, config.userType);
|
|
1172
|
-
console.log(`[pancakeQuickBatchSwapMerkle] ERC20→BNB 报价: ${outputFormatted} → ${ethers.formatEther(estimatedBNBValue)} BNB`);
|
|
1173
581
|
}
|
|
1174
582
|
const distributableAmount = estimatedOutput - (useNativeToken ? profitAmount : 0n);
|
|
1175
583
|
// ✅ 计算每个买方分到的金额
|
|
1176
584
|
let transferAmountsWei;
|
|
1177
585
|
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
1178
586
|
// 数量模式
|
|
1179
|
-
transferAmountsWei = buyerAmounts.map(amt => useNativeToken
|
|
1180
|
-
? ethers.parseEther(amt)
|
|
1181
|
-
: ethers.parseUnits(amt, quoteTokenDecimals));
|
|
587
|
+
transferAmountsWei = buyerAmounts.map((amt) => useNativeToken ? ethers.parseEther(amt) : ethers.parseUnits(amt, quoteTokenDecimals));
|
|
1182
588
|
const totalTransfer = transferAmountsWei.reduce((a, b) => a + b, 0n);
|
|
1183
589
|
if (totalTransfer > distributableAmount) {
|
|
1184
590
|
const formatted = useNativeToken
|
|
@@ -1189,7 +595,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1189
595
|
}
|
|
1190
596
|
else if (buyerRatios && buyerRatios.length === buyers.length) {
|
|
1191
597
|
// 比例模式
|
|
1192
|
-
transferAmountsWei = buyerRatios.map(ratio => {
|
|
598
|
+
transferAmountsWei = buyerRatios.map((ratio) => {
|
|
1193
599
|
return (distributableAmount * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
1194
600
|
});
|
|
1195
601
|
}
|
|
@@ -1197,22 +603,22 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1197
603
|
throw new Error('必须提供 buyerRatios 或 buyerAmounts');
|
|
1198
604
|
}
|
|
1199
605
|
// ✅ 获取贿赂金额
|
|
1200
|
-
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
1201
|
-
? ethers.parseEther(String(config.bribeAmount))
|
|
1202
|
-
: 0n;
|
|
606
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0 ? ethers.parseEther(String(config.bribeAmount)) : 0n;
|
|
1203
607
|
// ✅ 验证主钱包余额
|
|
1204
608
|
const sellerBalance = await seller.provider.getBalance(seller.address);
|
|
1205
609
|
let sellerGasCost;
|
|
1206
610
|
let sellerRequired;
|
|
1207
611
|
if (useNativeToken) {
|
|
1208
612
|
// BNB 模式:贿赂(BRIBE) + 卖出(gasLimit) + N个原生转账(BRIBE each) + 利润(BRIBE)
|
|
1209
|
-
sellerGasCost =
|
|
613
|
+
sellerGasCost =
|
|
614
|
+
gasPrice * (BRIBE_GAS_LIMIT + finalGasLimit + BRIBE_GAS_LIMIT * BigInt(buyers.length) + BRIBE_GAS_LIMIT);
|
|
1210
615
|
sellerRequired = bribeAmount + sellerGasCost;
|
|
1211
616
|
}
|
|
1212
617
|
else {
|
|
1213
618
|
// ERC20 模式:子钱包已预留 BNB,不需要主钱包转 Gas
|
|
1214
619
|
// 卖方 Gas: 贿赂(BRIBE) + 卖出(gasLimit) + N个ERC20转账(65000 each) + 利润(BRIBE)
|
|
1215
|
-
sellerGasCost =
|
|
620
|
+
sellerGasCost =
|
|
621
|
+
gasPrice * (BRIBE_GAS_LIMIT + finalGasLimit + ERC20_TRANSFER_GAS * BigInt(buyers.length) + BRIBE_GAS_LIMIT);
|
|
1216
622
|
sellerRequired = bribeAmount + sellerGasCost;
|
|
1217
623
|
}
|
|
1218
624
|
if (sellerBalance < sellerRequired) {
|
|
@@ -1220,9 +626,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1220
626
|
}
|
|
1221
627
|
// ==================== 规划 Nonce ====================
|
|
1222
628
|
// ✅ 如果前端传入了 startNonces,直接使用(性能优化)
|
|
1223
|
-
let sellerNonce = startNonces && startNonces.length > 0
|
|
1224
|
-
? startNonces[0]
|
|
1225
|
-
: await nonceManager.getNextNonce(seller);
|
|
629
|
+
let sellerNonce = startNonces && startNonces.length > 0 ? startNonces[0] : await nonceManager.getNextNonce(seller);
|
|
1226
630
|
const deadline = Math.floor(Date.now() / 1000) + 600;
|
|
1227
631
|
// ==================== 1. 贿赂交易 ====================
|
|
1228
632
|
let bribeTx = null;
|
|
@@ -1234,9 +638,8 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1234
638
|
gasPrice,
|
|
1235
639
|
gasLimit: BRIBE_GAS_LIMIT,
|
|
1236
640
|
chainId: context.chainId,
|
|
1237
|
-
type: txType
|
|
641
|
+
type: txType,
|
|
1238
642
|
});
|
|
1239
|
-
console.log(`[pancakeQuickBatchSwapMerkle] 贿赂交易已签名`);
|
|
1240
643
|
}
|
|
1241
644
|
// ==================== 2. 卖出交易 ====================
|
|
1242
645
|
const v3RouterIface2 = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
@@ -1257,15 +660,17 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1257
660
|
else if (routeParams.routeType === 'v3-single') {
|
|
1258
661
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
1259
662
|
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
1260
|
-
const sellSwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [
|
|
663
|
+
const sellSwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [
|
|
664
|
+
{
|
|
1261
665
|
tokenIn: v3TokenIn,
|
|
1262
666
|
tokenOut: v3TokenOut,
|
|
1263
667
|
fee: v3Fee,
|
|
1264
668
|
recipient: useNativeToken ? PANCAKE_V3_ROUTER_ADDRESS : seller.address,
|
|
1265
669
|
amountIn: sellAmountWei,
|
|
1266
670
|
amountOutMinimum: 0n,
|
|
1267
|
-
sqrtPriceLimitX96: 0n
|
|
1268
|
-
}
|
|
671
|
+
sqrtPriceLimitX96: 0n,
|
|
672
|
+
},
|
|
673
|
+
]);
|
|
1269
674
|
if (useNativeToken) {
|
|
1270
675
|
// 原生代币:需要 unwrap WBNB
|
|
1271
676
|
const sellUnwrapData = v3RouterIface2.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
@@ -1286,19 +691,18 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1286
691
|
gasLimit: finalGasLimit,
|
|
1287
692
|
gasPrice,
|
|
1288
693
|
chainId: context.chainId,
|
|
1289
|
-
type: txType
|
|
694
|
+
type: txType,
|
|
1290
695
|
});
|
|
1291
|
-
console.log(`[pancakeQuickBatchSwapMerkle] 卖出交易已签名`);
|
|
1292
696
|
// ==================== 3. 转账交易(支持多跳)====================
|
|
1293
697
|
const buyerGasCost = gasPrice * finalGasLimit;
|
|
1294
698
|
// ✅ 生成多跳路径
|
|
1295
|
-
const hopPaths = generateDisperseHopPaths(buyers.map(b => b.address), disperseHopCount, context.provider);
|
|
699
|
+
const hopPaths = generateDisperseHopPaths(buyers.map((b) => b.address), disperseHopCount, context.provider);
|
|
1296
700
|
// 收集所有中间钱包信息
|
|
1297
701
|
const allHopWallets = [];
|
|
1298
|
-
hopPaths.forEach(path => {
|
|
702
|
+
hopPaths.forEach((path) => {
|
|
1299
703
|
allHopWallets.push(...path.hopWalletsInfo);
|
|
1300
704
|
});
|
|
1301
|
-
let transferTxs
|
|
705
|
+
let transferTxs;
|
|
1302
706
|
if (disperseHopCount === 0) {
|
|
1303
707
|
// ✅ 无多跳:直接转账
|
|
1304
708
|
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
@@ -1313,17 +717,14 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1313
717
|
gasPrice,
|
|
1314
718
|
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
1315
719
|
chainId: context.chainId,
|
|
1316
|
-
type: txType
|
|
720
|
+
type: txType,
|
|
1317
721
|
});
|
|
1318
722
|
}));
|
|
1319
723
|
}
|
|
1320
724
|
else {
|
|
1321
725
|
const erc20Interface = new ethers.Interface(ERC20_ABI);
|
|
1322
726
|
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
1323
|
-
const transferData = erc20Interface.encodeFunctionData('transfer', [
|
|
1324
|
-
buyer.address,
|
|
1325
|
-
transferAmountsWei[i]
|
|
1326
|
-
]);
|
|
727
|
+
const transferData = erc20Interface.encodeFunctionData('transfer', [buyer.address, transferAmountsWei[i]]);
|
|
1327
728
|
return seller.signTransaction({
|
|
1328
729
|
to: quoteToken,
|
|
1329
730
|
data: transferData,
|
|
@@ -1332,7 +733,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1332
733
|
gasPrice,
|
|
1333
734
|
gasLimit: ERC20_TRANSFER_GAS,
|
|
1334
735
|
chainId: context.chainId,
|
|
1335
|
-
type: txType
|
|
736
|
+
type: txType,
|
|
1336
737
|
});
|
|
1337
738
|
}));
|
|
1338
739
|
}
|
|
@@ -1372,12 +773,11 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1372
773
|
transferTxs = [...bnbTxs, ...erc20Txs];
|
|
1373
774
|
}
|
|
1374
775
|
}
|
|
1375
|
-
console.log(`[pancakeQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名 (多跳数=${disperseHopCount})`);
|
|
1376
776
|
// ==================== 4. 买入交易 ====================
|
|
1377
777
|
// ✅ 如果前端传入了 startNonces,使用 buyer 部分(从索引 1 开始)
|
|
1378
778
|
const buyerNonces = startNonces && startNonces.length > 1
|
|
1379
779
|
? startNonces.slice(1)
|
|
1380
|
-
: await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
780
|
+
: await Promise.all(buyers.map((buyer) => nonceManager.getNextNonce(buyer)));
|
|
1381
781
|
const signedBuys = await Promise.all(buyers.map(async (buyer, i) => {
|
|
1382
782
|
const buyAmount = transferAmountsWei[i];
|
|
1383
783
|
// ✅ ERC20 模式:value = 0(通过代币授权支付)
|
|
@@ -1397,15 +797,17 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1397
797
|
else if (routeParams.routeType === 'v3-single') {
|
|
1398
798
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
1399
799
|
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
1400
|
-
const buySwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [
|
|
800
|
+
const buySwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [
|
|
801
|
+
{
|
|
1401
802
|
tokenIn: v3TokenOut,
|
|
1402
803
|
tokenOut: v3TokenIn,
|
|
1403
804
|
fee: v3Fee,
|
|
1404
805
|
recipient: buyer.address,
|
|
1405
806
|
amountIn: buyAmount,
|
|
1406
807
|
amountOutMinimum: 0n,
|
|
1407
|
-
sqrtPriceLimitX96: 0n
|
|
1408
|
-
}
|
|
808
|
+
sqrtPriceLimitX96: 0n,
|
|
809
|
+
},
|
|
810
|
+
]);
|
|
1409
811
|
buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
1410
812
|
}
|
|
1411
813
|
else {
|
|
@@ -1418,10 +820,9 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1418
820
|
gasLimit: finalGasLimit,
|
|
1419
821
|
gasPrice,
|
|
1420
822
|
chainId: context.chainId,
|
|
1421
|
-
type: txType
|
|
823
|
+
type: txType,
|
|
1422
824
|
});
|
|
1423
825
|
}));
|
|
1424
|
-
console.log(`[pancakeQuickBatchSwapMerkle] ${signedBuys.length} 笔买入交易已签名`);
|
|
1425
826
|
nonceManager.clearTemp();
|
|
1426
827
|
// ==================== 组装交易数组 ====================
|
|
1427
828
|
// BNB 模式:贿赂 → 卖出 → 转账 → 买入 → 利润多跳
|
|
@@ -1439,41 +840,33 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1439
840
|
provider: context.provider,
|
|
1440
841
|
payerWallet: seller,
|
|
1441
842
|
profitAmount,
|
|
1442
|
-
profitRecipient:
|
|
843
|
+
profitRecipient: getProfitRecipient(),
|
|
1443
844
|
hopCount: PROFIT_HOP_COUNT,
|
|
1444
845
|
gasPrice,
|
|
1445
846
|
chainId: context.chainId,
|
|
1446
847
|
txType,
|
|
1447
|
-
startNonce: sellerNonce
|
|
848
|
+
startNonce: sellerNonce++,
|
|
1448
849
|
});
|
|
1449
850
|
signedTransactions.push(...profitHopResult.signedTransactions);
|
|
1450
851
|
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
1451
852
|
// 多跳交易已签名
|
|
1452
853
|
}
|
|
1453
|
-
console.log(`[pancakeQuickBatchSwapMerkle] 交易组装完成: ${signedTransactions.length} 笔`);
|
|
1454
|
-
console.log(` - 贿赂: ${bribeTx ? 1 : 0}`);
|
|
1455
|
-
console.log(` - 卖出: 1`);
|
|
1456
|
-
console.log(` - 转账: ${transferTxs.length}`);
|
|
1457
|
-
console.log(` - 买入: ${signedBuys.length}`);
|
|
1458
|
-
const outputUnit = useNativeToken ? 'BNB' : 'ERC20';
|
|
1459
854
|
return {
|
|
1460
855
|
signedTransactions,
|
|
1461
856
|
disperseHopWallets: allHopWallets.length > 0 ? allHopWallets : undefined, // ✅ 返回转账多跳钱包
|
|
1462
857
|
profitHopWallets, // ✅ 返回利润多跳钱包
|
|
1463
858
|
metadata: {
|
|
1464
859
|
sellerAddress: seller.address,
|
|
1465
|
-
buyerAddresses: buyers.map(b => b.address),
|
|
860
|
+
buyerAddresses: buyers.map((b) => b.address),
|
|
1466
861
|
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
1467
862
|
estimatedOutput: useNativeToken
|
|
1468
863
|
? ethers.formatEther(estimatedOutput)
|
|
1469
864
|
: ethers.formatUnits(estimatedOutput, quoteTokenDecimals),
|
|
1470
|
-
transferAmounts: transferAmountsWei.map(amt => useNativeToken
|
|
1471
|
-
? ethers.formatEther(amt)
|
|
1472
|
-
: ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
865
|
+
transferAmounts: transferAmountsWei.map((amt) => useNativeToken ? ethers.formatEther(amt) : ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
1473
866
|
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
|
|
1474
867
|
useNativeToken,
|
|
1475
|
-
disperseHopCount: disperseHopCount > 0 ? disperseHopCount : undefined // ✅ 返回多跳数
|
|
1476
|
-
}
|
|
868
|
+
disperseHopCount: disperseHopCount > 0 ? disperseHopCount : undefined, // ✅ 返回多跳数
|
|
869
|
+
},
|
|
1477
870
|
};
|
|
1478
871
|
}
|
|
1479
872
|
/**
|
|
@@ -1484,7 +877,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
1484
877
|
* - ✅ 正确处理 nonce:同一钱包在多轮中使用时 nonce 自动递增
|
|
1485
878
|
*/
|
|
1486
879
|
export async function pancakeCrossSwapMerkle(params) {
|
|
1487
|
-
const { sellerPrivateKeys, sellAmounts, buyerPrivateKeys, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, buyersPerSell, disperseHopCount = 0 } = params;
|
|
880
|
+
const { sellerPrivateKeys, sellAmounts, buyerPrivateKeys, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, buyersPerSell, disperseHopCount = 0, } = params;
|
|
1488
881
|
if (sellerPrivateKeys.length === 0) {
|
|
1489
882
|
throw new Error('至少需要一个卖方');
|
|
1490
883
|
}
|
|
@@ -1498,8 +891,8 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
1498
891
|
const context = createPancakeContext(config);
|
|
1499
892
|
const nonceManager = new NonceManager(context.provider);
|
|
1500
893
|
// ✅ 预先获取所有钱包的初始 nonce
|
|
1501
|
-
const allSellerWallets = sellerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
1502
|
-
const allBuyerWallets = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
894
|
+
const allSellerWallets = sellerPrivateKeys.map((pk) => new Wallet(pk, context.provider));
|
|
895
|
+
const allBuyerWallets = buyerPrivateKeys.map((pk) => new Wallet(pk, context.provider));
|
|
1503
896
|
// 使用 Map 去重(同一私钥可能出现多次)
|
|
1504
897
|
const addressToNonce = new Map();
|
|
1505
898
|
// 获取所有卖方 nonce
|
|
@@ -1516,7 +909,6 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
1516
909
|
addressToNonce.set(wallet.address, nonce);
|
|
1517
910
|
}
|
|
1518
911
|
}
|
|
1519
|
-
console.log(`[pancakeCrossSwapMerkle] 初始化完成: ${sellerPrivateKeys.length} 卖方, ${buyerPrivateKeys.length} 买方`);
|
|
1520
912
|
const allSigned = [];
|
|
1521
913
|
const allDisperse = [];
|
|
1522
914
|
const allProfit = [];
|
|
@@ -1527,9 +919,7 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
1527
919
|
// 判断是否使用原生代币
|
|
1528
920
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
1529
921
|
// 获取贿赂金额
|
|
1530
|
-
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
1531
|
-
? ethers.parseEther(String(config.bribeAmount))
|
|
1532
|
-
: 0n;
|
|
922
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0 ? ethers.parseEther(String(config.bribeAmount)) : 0n;
|
|
1533
923
|
const hasBribe = bribeAmount > 0n;
|
|
1534
924
|
for (let i = 0; i < sellerPrivateKeys.length; i++) {
|
|
1535
925
|
const sellerPk = sellerPrivateKeys[i];
|
|
@@ -1547,7 +937,7 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
1547
937
|
// ✅ 获取卖方当前 nonce
|
|
1548
938
|
const sellerNonce = addressToNonce.get(sellerWallet.address);
|
|
1549
939
|
// ✅ 获取并更新买方 nonces(每个买方本轮需要 1 个 nonce)
|
|
1550
|
-
const roundBuyerNonces = roundBuyerWallets.map(w => {
|
|
940
|
+
const roundBuyerNonces = roundBuyerWallets.map((w) => {
|
|
1551
941
|
const nonce = addressToNonce.get(w.address);
|
|
1552
942
|
addressToNonce.set(w.address, nonce + 1); // 每个买方用 1 个 nonce
|
|
1553
943
|
return nonce;
|
|
@@ -1587,11 +977,10 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
1587
977
|
quoteToken,
|
|
1588
978
|
quoteTokenDecimals,
|
|
1589
979
|
disperseHopCount,
|
|
1590
|
-
startNonces // ✅ 传入预计算的 nonces
|
|
980
|
+
startNonces, // ✅ 传入预计算的 nonces
|
|
1591
981
|
});
|
|
1592
982
|
// ✅ 更新卖方 nonce(使用精确计算的值)
|
|
1593
983
|
addressToNonce.set(sellerWallet.address, sellerNonce + sellerNonceConsumed);
|
|
1594
|
-
console.log(`[pancakeCrossSwapMerkle] 轮次 ${i + 1}: 卖方=${sellerWallet.address.slice(0, 10)}..., 买方=${roundBuyerPks.length}, 交易=${res.signedTransactions.length}`);
|
|
1595
984
|
// 累积签名与中间钱包
|
|
1596
985
|
allSigned.push(...res.signedTransactions);
|
|
1597
986
|
if (res.disperseHopWallets)
|
|
@@ -1602,15 +991,14 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
1602
991
|
sellerAddress: res.metadata?.sellerAddress || '',
|
|
1603
992
|
buyerAddresses: res.metadata?.buyerAddresses || roundBuyerPks.map(() => ''),
|
|
1604
993
|
sellAmount,
|
|
1605
|
-
bundleHash: undefined
|
|
994
|
+
bundleHash: undefined,
|
|
1606
995
|
});
|
|
1607
996
|
}
|
|
1608
997
|
nonceManager.clearTemp();
|
|
1609
|
-
console.log(`[pancakeCrossSwapMerkle] 完成: ${rounds.length} 轮, ${allSigned.length} 笔交易`);
|
|
1610
998
|
return {
|
|
1611
999
|
signedTransactions: allSigned,
|
|
1612
1000
|
rounds,
|
|
1613
1001
|
disperseHopWallets: allDisperse.length > 0 ? allDisperse : undefined,
|
|
1614
|
-
profitHopWallets: allProfit.length > 0 ? allProfit : undefined
|
|
1002
|
+
profitHopWallets: allProfit.length > 0 ? allProfit : undefined,
|
|
1615
1003
|
};
|
|
1616
1004
|
}
|