four-flap-meme-sdk 2.2.8 → 2.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +6 -41
- package/README.md +0 -31
- package/README.zh-CN.md +6 -41
- package/dist/chains/bsc/four/approve-tokenmanager.d.ts +26 -1
- package/dist/chains/bsc/four/approve-tokenmanager.js +113 -1
- package/dist/chains/bsc/four/config.d.ts +67 -5
- package/dist/chains/bsc/four/config.js +114 -2
- package/dist/chains/bsc/four/core.d.ts +4 -1
- package/dist/chains/bsc/four/core.js +592 -1
- package/dist/chains/bsc/four/index.d.ts +6 -6
- package/dist/chains/bsc/four/index.js +6 -6
- package/dist/chains/bsc/four/internal.d.ts +46 -2
- package/dist/chains/bsc/four/internal.js +239 -2
- package/dist/chains/bsc/four/pancake-proxy.d.ts +28 -1
- package/dist/chains/bsc/four/pancake-proxy.js +687 -1
- package/dist/chains/bsc/four/private.d.ts +27 -1
- package/dist/chains/bsc/four/private.js +477 -1
- package/dist/chains/bsc/four/submit.d.ts +315 -2
- package/dist/chains/bsc/four/submit.js +752 -2
- package/dist/chains/bsc/four/swap-buy-first.d.ts +55 -2
- package/dist/chains/bsc/four/swap-buy-first.js +507 -2
- package/dist/chains/bsc/four/swap-internal.d.ts +3 -1
- package/dist/chains/bsc/four/swap-internal.js +18 -1
- package/dist/chains/bsc/four/swap.d.ts +144 -2
- package/dist/chains/bsc/four/swap.js +766 -2
- package/dist/chains/bsc/four/types.d.ts +476 -1
- package/dist/chains/bsc/four/utils.d.ts +18 -5
- package/dist/chains/bsc/four/utils.js +1552 -5
- package/dist/chains/bsc/pancake/bundle-buy-first.d.ts +91 -1
- package/dist/chains/bsc/pancake/bundle-buy-first.js +212 -97
- package/dist/chains/bsc/pancake/bundle-swap.d.ts +79 -1
- package/dist/chains/bsc/pancake/bundle-swap.js +726 -114
- package/dist/chains/bsc/pancake/index.d.ts +2 -4
- package/dist/chains/bsc/pancake/index.js +3 -1
- package/dist/chains/bsc/platforms/iro/factory.d.ts +2 -2
- package/dist/chains/bsc/platforms/iro/factory.js +1 -3
- package/dist/chains/bsc/platforms/iro/index.d.ts +5 -5
- package/dist/chains/bsc/platforms/iro/index.js +3 -3
- package/dist/chains/bsc/platforms/iro/pool.js +10 -31
- package/dist/chains/bsc/platforms/iro/token.js +1 -4
- package/dist/chains/eni/batch-router/bundle-approve.js +3 -4
- package/dist/chains/eni/batch-router/transfer.js +25 -55
- package/dist/chains/eni/batch-router/utils.js +6 -32
- package/dist/chains/eni/bundler/sign.js +6 -5
- package/dist/chains/eni/bundler/submit.js +4 -1
- package/dist/chains/eni/constants.js +1 -1
- package/dist/chains/eni/index.d.ts +1 -2
- package/dist/chains/eni/index.js +0 -1
- package/dist/chains/eni/platforms/daoaas/create.js +2 -2
- package/dist/chains/eni/platforms/daoaas/index.d.ts +3 -3
- package/dist/chains/eni/platforms/daoaas/index.js +3 -3
- package/dist/chains/eni/platforms/daoaas/meta.js +6 -9
- package/dist/chains/eni/platforms/daoaas/portal-direct.js +44 -28
- package/dist/chains/eni/platforms/daoaas/portal.js +6 -10
- package/dist/chains/eni/platforms/dswap/liquidity.js +26 -58
- package/dist/chains/eni/platforms/fair-launch/index.d.ts +2 -2
- package/dist/chains/eni/platforms/fair-launch/index.js +1 -1
- package/dist/chains/eni/platforms/fair-launch/launcher.js +46 -87
- package/dist/chains/eni/platforms/fair-launch/pool.js +1 -4
- package/dist/chains/eni/platforms/fair-launch/presets.js +2 -2
- package/dist/chains/eni/platforms/iro/factory.d.ts +2 -2
- package/dist/chains/eni/platforms/iro/factory.js +1 -3
- package/dist/chains/eni/platforms/iro/index.d.ts +6 -6
- package/dist/chains/eni/platforms/iro/index.js +4 -4
- package/dist/chains/eni/platforms/iro/pool.js +26 -90
- package/dist/chains/eni/platforms/iro/token.js +31 -107
- package/dist/chains/eni/platforms/iro/whitelist.js +18 -6
- package/dist/chains/index.d.ts +0 -13
- package/dist/chains/index.js +0 -13
- package/dist/chains/xlayer/eip7702/bundle-approve.d.ts +26 -2
- package/dist/chains/xlayer/eip7702/bundle-approve.js +21 -11
- package/dist/chains/xlayer/eip7702/bundle-buy.d.ts +6 -2
- package/dist/chains/xlayer/eip7702/bundle-buy.js +51 -13
- package/dist/chains/xlayer/eip7702/bundle-create.js +59 -93
- package/dist/chains/xlayer/eip7702/bundle-sell.d.ts +6 -2
- package/dist/chains/xlayer/eip7702/bundle-sell.js +111 -29
- package/dist/chains/xlayer/eip7702/bundle-swap.d.ts +65 -3
- package/dist/chains/xlayer/eip7702/bundle-swap.js +245 -51
- package/dist/chains/xlayer/eip7702/constants.d.ts +16 -1
- package/dist/chains/xlayer/eip7702/constants.js +21 -3
- package/dist/chains/xlayer/eip7702/index.d.ts +46 -28
- package/dist/chains/xlayer/eip7702/index.js +81 -28
- package/dist/chains/xlayer/eip7702/multi-hop-transfer.d.ts +203 -2
- package/dist/chains/xlayer/eip7702/multi-hop-transfer.js +307 -63
- package/dist/chains/xlayer/eip7702/types.d.ts +0 -88
- package/dist/chains/xlayer/eip7702/utils.d.ts +3 -0
- package/dist/chains/xlayer/eip7702/utils.js +28 -23
- package/dist/chains/xlayer/eip7702/volume.d.ts +184 -6
- package/dist/chains/xlayer/eip7702/volume.js +164 -89
- package/dist/chains/xlayer/eoa/constants.js +1 -1
- package/dist/chains/xlayer/eoa/dex-helpers.js +5 -5
- package/dist/chains/xlayer/eoa/eoa-bundle-swap.d.ts +95 -1
- package/dist/chains/xlayer/eoa/eoa-bundle-swap.js +299 -66
- package/dist/chains/xlayer/eoa/eoa-wash-volume.d.ts +1 -1
- package/dist/chains/xlayer/eoa/eoa-wash-volume.js +23 -18
- package/dist/chains/xlayer/eoa/index.d.ts +6 -10
- package/dist/chains/xlayer/eoa/index.js +23 -8
- package/dist/chains/xlayer/eoa/portal-ops.js +2 -7
- package/dist/chains/xlayer/eoa/router-manager.js +3 -3
- package/dist/chains/xlayer/eoa/types.d.ts +2 -2
- package/dist/chains/xlayer/eoa/types.js +3 -1
- package/dist/chains/xlayer/index.d.ts +2 -3
- package/dist/chains/xlayer/index.js +7 -4
- package/dist/contracts/helper3.d.ts +5 -20
- package/dist/contracts/helper3.js +20 -56
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.d.ts +26 -1
- package/dist/contracts/tm-bundle-merkle/approve-tokenmanager.js +113 -1
- package/dist/contracts/tm-bundle-merkle/config.d.ts +67 -5
- package/dist/contracts/tm-bundle-merkle/config.js +114 -2
- package/dist/contracts/tm-bundle-merkle/core.d.ts +4 -1
- package/dist/contracts/tm-bundle-merkle/core.js +591 -1
- package/dist/contracts/tm-bundle-merkle/index.d.ts +5 -5
- package/dist/contracts/tm-bundle-merkle/index.js +5 -5
- package/dist/contracts/tm-bundle-merkle/internal.d.ts +46 -2
- package/dist/contracts/tm-bundle-merkle/internal.js +238 -2
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.d.ts +28 -1
- package/dist/contracts/tm-bundle-merkle/pancake-proxy.js +686 -1
- package/dist/contracts/tm-bundle-merkle/private.d.ts +27 -1
- package/dist/contracts/tm-bundle-merkle/private.js +476 -1
- package/dist/contracts/tm-bundle-merkle/submit.d.ts +314 -3
- package/dist/contracts/tm-bundle-merkle/submit.js +928 -3
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.d.ts +55 -2
- package/dist/contracts/tm-bundle-merkle/swap-buy-first.js +506 -2
- package/dist/contracts/tm-bundle-merkle/swap-internal.d.ts +3 -1
- package/dist/contracts/tm-bundle-merkle/swap-internal.js +18 -1
- package/dist/contracts/tm-bundle-merkle/swap.d.ts +144 -2
- package/dist/contracts/tm-bundle-merkle/swap.js +764 -2
- package/dist/contracts/tm-bundle-merkle/types.d.ts +476 -1
- package/dist/contracts/tm-bundle-merkle/utils.d.ts +18 -6
- package/dist/contracts/tm-bundle-merkle/utils.js +1501 -6
- package/dist/contracts/tm-bundle.d.ts +51 -3
- package/dist/contracts/tm-bundle.js +177 -108
- package/dist/contracts/tm.d.ts +2 -3
- package/dist/contracts/tm.js +32 -37
- package/dist/contracts/tm1.js +4 -9
- package/dist/contracts/tm2.js +4 -9
- package/dist/dex/direct-router.d.ts +125 -3
- package/dist/dex/direct-router.js +666 -237
- package/dist/flows/create.d.ts +1 -2
- package/dist/flows/create.js +6 -6
- package/dist/index.d.ts +87 -20
- package/dist/index.js +217 -20
- package/dist/shared/abis/TaxToken.json +105 -0
- package/dist/shared/abis/TokenManager2.json +60 -0
- package/dist/shared/abis/common.d.ts +83 -2
- package/dist/shared/abis/common.js +254 -2
- package/dist/shared/abis/index.d.ts +6 -5
- package/dist/shared/abis/index.js +7 -5
- package/dist/shared/clients/blockrazor.js +25 -39
- package/dist/shared/clients/club48.d.ts +2 -2
- package/dist/shared/clients/club48.js +29 -34
- package/dist/shared/clients/emitservice.js +0 -2
- package/dist/shared/clients/four.d.ts +6 -21
- package/dist/shared/clients/four.js +24 -29
- package/dist/shared/clients/merkle.js +34 -27
- package/dist/shared/constants/addresses.d.ts +1 -1
- package/dist/shared/constants/addresses.js +2 -11
- package/dist/shared/constants/chains.d.ts +1 -1
- package/dist/shared/constants/chains.js +1 -1
- package/dist/shared/constants/gas.d.ts +1 -1
- package/dist/shared/constants/gas.js +6 -2
- package/dist/shared/constants/index.d.ts +0 -3
- package/dist/shared/constants/index.js +0 -1
- package/dist/shared/flap/abi.js +1 -1
- package/dist/shared/flap/constants.d.ts +2 -1
- package/dist/shared/flap/constants.js +4 -3
- package/dist/shared/flap/curve.js +0 -3
- package/dist/shared/flap/errors.d.ts +4 -1
- package/dist/shared/flap/errors.js +1 -20
- package/dist/shared/flap/index.d.ts +5 -4
- package/dist/shared/flap/index.js +5 -4
- package/dist/shared/flap/launch-v6.d.ts +127 -0
- package/dist/shared/flap/launch-v6.js +123 -0
- package/dist/shared/flap/meta.d.ts +18 -22
- package/dist/shared/flap/meta.js +17 -12
- package/dist/shared/flap/permit.js +2 -5
- package/dist/shared/flap/pinata.d.ts +6 -22
- package/dist/shared/flap/pinata.js +26 -21
- package/dist/shared/flap/portal-bundle-merkle/config.d.ts +72 -3
- package/dist/shared/flap/portal-bundle-merkle/config.js +124 -4
- package/dist/shared/flap/portal-bundle-merkle/core.d.ts +4 -0
- package/dist/shared/flap/portal-bundle-merkle/core.js +151 -142
- package/dist/shared/flap/portal-bundle-merkle/create-to-dex.d.ts +4 -17
- package/dist/shared/flap/portal-bundle-merkle/create-to-dex.js +86 -86
- package/dist/shared/flap/portal-bundle-merkle/curve-to-dex.js +92 -100
- package/dist/shared/flap/portal-bundle-merkle/index.d.ts +7 -11
- package/dist/shared/flap/portal-bundle-merkle/index.js +7 -4
- package/dist/shared/flap/portal-bundle-merkle/pancake-proxy.js +68 -71
- package/dist/shared/flap/portal-bundle-merkle/private.js +114 -61
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first.d.ts +64 -1
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first.js +247 -66
- package/dist/shared/flap/portal-bundle-merkle/swap.d.ts +71 -2
- package/dist/shared/flap/portal-bundle-merkle/swap.js +410 -103
- package/dist/shared/flap/portal-bundle-merkle/types.d.ts +12 -88
- package/dist/shared/flap/portal-bundle-merkle/utils.d.ts +80 -1
- package/dist/shared/flap/portal-bundle-merkle/utils.js +265 -145
- package/dist/shared/flap/portal-bundle.js +56 -55
- package/dist/shared/flap/portal-create-token.d.ts +19 -37
- package/dist/shared/flap/portal-create-token.js +124 -121
- package/dist/shared/flap/portal.d.ts +2 -1
- package/dist/shared/flap/portal.js +28 -36
- package/dist/shared/flap/vanity.d.ts +6 -6
- package/dist/shared/flap/vanity.js +21 -15
- package/dist/shared/flap/vault.d.ts +93 -25
- package/dist/shared/flap/vault.js +126 -75
- package/dist/shared/four/tax-token.d.ts +1 -1
- package/dist/shared/four/tax-token.js +7 -27
- package/dist/shared/index.d.ts +0 -6
- package/dist/shared/index.js +0 -4
- package/dist/utils/airdrop-sweep.d.ts +76 -4
- package/dist/utils/airdrop-sweep.js +55 -42
- package/dist/utils/bundle-helpers.d.ts +243 -9
- package/dist/utils/bundle-helpers.js +584 -10
- package/dist/utils/constants.d.ts +61 -5
- package/dist/utils/constants.js +80 -5
- package/dist/utils/contract-factory.d.ts +4 -2
- package/dist/utils/contract-factory.js +18 -25
- package/dist/utils/erc20.d.ts +89 -7
- package/dist/utils/erc20.js +125 -94
- package/dist/utils/errors.d.ts +1 -12
- package/dist/utils/errors.js +1 -60
- package/dist/utils/holders-maker.d.ts +138 -2
- package/dist/utils/holders-maker.js +661 -26
- package/dist/utils/lp-inspect.d.ts +112 -2
- package/dist/utils/lp-inspect.js +223 -73
- package/dist/utils/mpcExclusive.d.ts +5 -2
- package/dist/utils/mpcExclusive.js +3 -4
- package/dist/utils/private-sale.d.ts +58 -2
- package/dist/utils/private-sale.js +15 -4
- package/dist/utils/provider-factory.d.ts +0 -4
- package/dist/utils/provider-factory.js +0 -10
- package/dist/utils/quote-helpers.d.ts +45 -4
- package/dist/utils/quote-helpers.js +74 -17
- package/dist/utils/stealth-transfer.d.ts +28 -2
- package/dist/utils/stealth-transfer.js +15 -31
- package/dist/utils/swap-helpers.d.ts +15 -2
- package/dist/utils/swap-helpers.js +11 -6
- package/dist/utils/wallet.d.ts +25 -2
- package/dist/utils/wallet.js +10 -13
- package/package.json +4 -160
- package/dist/__tests__/subpath-exports.test.d.ts +0 -1
- package/dist/__tests__/subpath-exports.test.js +0 -64
- package/dist/abis/common.d.ts +0 -85
- package/dist/abis/common.js +0 -264
- package/dist/abis/contracts/TaxToken.json +0 -969
- package/dist/abis/contracts/TokenManager2.json +0 -136
- package/dist/abis/contracts/index.d.ts +0 -5
- package/dist/abis/contracts/index.js +0 -5
- package/dist/abis/flap/index.d.ts +0 -3
- package/dist/abis/flap/index.js +0 -3
- package/dist/abis/flap/portal-events.d.ts +0 -6
- package/dist/abis/flap/portal-events.js +0 -17
- package/dist/abis/flap/portal.d.ts +0 -6
- package/dist/abis/flap/portal.js +0 -37
- package/dist/abis/flap/vault.d.ts +0 -171
- package/dist/abis/flap/vault.js +0 -91
- package/dist/abis/index.d.ts +0 -8
- package/dist/abis/index.js +0 -11
- package/dist/bundle-core/__tests__/config-helpers.test.d.ts +0 -1
- package/dist/bundle-core/__tests__/config-helpers.test.js +0 -28
- package/dist/bundle-core/__tests__/facade-parity.test.d.ts +0 -1
- package/dist/bundle-core/__tests__/facade-parity.test.js +0 -33
- package/dist/bundle-core/__tests__/sign-context-helpers.test.d.ts +0 -1
- package/dist/bundle-core/__tests__/sign-context-helpers.test.js +0 -60
- package/dist/bundle-core/__tests__/sign-fixture.test.d.ts +0 -1
- package/dist/bundle-core/__tests__/sign-fixture.test.js +0 -220
- package/dist/bundle-core/__tests__/sign-fixtures.d.ts +0 -10
- package/dist/bundle-core/__tests__/sign-fixtures.js +0 -16
- package/dist/bundle-core/config-helpers.d.ts +0 -36
- package/dist/bundle-core/config-helpers.js +0 -57
- package/dist/bundle-core/errors.d.ts +0 -50
- package/dist/bundle-core/errors.js +0 -35
- package/dist/bundle-core/four-meme/approve-tokenmanager.d.ts +0 -7
- package/dist/bundle-core/four-meme/approve-tokenmanager.js +0 -99
- package/dist/bundle-core/four-meme/core-helpers.d.ts +0 -8
- package/dist/bundle-core/four-meme/core-helpers.js +0 -40
- package/dist/bundle-core/four-meme/core.d.ts +0 -4
- package/dist/bundle-core/four-meme/core.js +0 -515
- package/dist/bundle-core/four-meme/pancake-proxy.d.ts +0 -28
- package/dist/bundle-core/four-meme/pancake-proxy.js +0 -679
- package/dist/bundle-core/four-meme/private.d.ts +0 -27
- package/dist/bundle-core/four-meme/private.js +0 -465
- package/dist/bundle-core/four-meme/sign-context-helpers.d.ts +0 -2
- package/dist/bundle-core/four-meme/sign-context-helpers.js +0 -2
- package/dist/bundle-core/four-meme/swap-buy-first.d.ts +0 -8
- package/dist/bundle-core/four-meme/swap-buy-first.js +0 -493
- package/dist/bundle-core/four-meme/swap-hop-helpers.d.ts +0 -6
- package/dist/bundle-core/four-meme/swap-hop-helpers.js +0 -63
- package/dist/bundle-core/four-meme/swap-internal.d.ts +0 -3
- package/dist/bundle-core/four-meme/swap-internal.js +0 -18
- package/dist/bundle-core/four-meme/swap-sign-helpers.d.ts +0 -27
- package/dist/bundle-core/four-meme/swap-sign-helpers.js +0 -105
- package/dist/bundle-core/four-meme/swap.d.ts +0 -17
- package/dist/bundle-core/four-meme/swap.js +0 -505
- package/dist/bundle-core/four-meme/types/buy-first.d.ts +0 -50
- package/dist/bundle-core/four-meme/types/buy-first.js +0 -1
- package/dist/bundle-core/four-meme/types/core-flow.d.ts +0 -63
- package/dist/bundle-core/four-meme/types/core-flow.js +0 -1
- package/dist/bundle-core/four-meme/types/index.d.ts +0 -600
- package/dist/bundle-core/four-meme/types/index.js +0 -1
- package/dist/bundle-core/four-meme/types/swap-internal.d.ts +0 -19
- package/dist/bundle-core/four-meme/types/swap-internal.js +0 -1
- package/dist/bundle-core/four-meme/types.d.ts +0 -1
- package/dist/bundle-core/four-meme/types.js +0 -1
- package/dist/bundle-core/four-meme/utils-disperse.d.ts +0 -7
- package/dist/bundle-core/four-meme/utils-disperse.js +0 -396
- package/dist/bundle-core/four-meme/utils-pairwise.d.ts +0 -8
- package/dist/bundle-core/four-meme/utils-pairwise.js +0 -328
- package/dist/bundle-core/four-meme/utils-sweep.d.ts +0 -8
- package/dist/bundle-core/four-meme/utils-sweep.js +0 -744
- package/dist/bundle-core/index.d.ts +0 -8
- package/dist/bundle-core/index.js +0 -8
- package/dist/bundle-core/internal.d.ts +0 -21
- package/dist/bundle-core/internal.js +0 -182
- package/dist/bundle-core/sign-context-helpers.d.ts +0 -25
- package/dist/bundle-core/sign-context-helpers.js +0 -67
- package/dist/bundle-core/submit.d.ts +0 -293
- package/dist/bundle-core/submit.js +0 -727
- package/dist/bundle-core/types/index.d.ts +0 -8
- package/dist/bundle-core/types/index.js +0 -1
- package/dist/bundle-core/types.d.ts +0 -1
- package/dist/bundle-core/types.js +0 -1
- package/dist/chains/bsc/four/utils-disperse.d.ts +0 -1
- package/dist/chains/bsc/four/utils-disperse.js +0 -1
- package/dist/chains/bsc/four/utils-pairwise.d.ts +0 -1
- package/dist/chains/bsc/four/utils-pairwise.js +0 -1
- package/dist/chains/bsc/four/utils-sweep.d.ts +0 -1
- package/dist/chains/bsc/four/utils-sweep.js +0 -1
- package/dist/chains/bsc/iro.d.ts +0 -5
- package/dist/chains/bsc/iro.js +0 -4
- package/dist/chains/bsc/pancake/bundle-buy-first-helpers.d.ts +0 -159
- package/dist/chains/bsc/pancake/bundle-buy-first-helpers.js +0 -117
- package/dist/chains/bsc/pancake/bundle-swap-helpers.d.ts +0 -241
- package/dist/chains/bsc/pancake/bundle-swap-helpers.js +0 -565
- package/dist/chains/eni/flat-aliases.d.ts +0 -10
- package/dist/chains/eni/flat-aliases.js +0 -8
- package/dist/chains/eni/submit.d.ts +0 -43
- package/dist/chains/eni/submit.js +0 -286
- package/dist/chains/xlayer/eip7702/flat-aliases.d.ts +0 -13
- package/dist/chains/xlayer/eip7702/flat-aliases.js +0 -10
- package/dist/chains/xlayer/eip7702/multi-hop-transfer-helpers.d.ts +0 -79
- package/dist/chains/xlayer/eip7702/multi-hop-transfer-helpers.js +0 -1
- package/dist/chains/xlayer/eip7702/transfer-context-helpers.d.ts +0 -26
- package/dist/chains/xlayer/eip7702/transfer-context-helpers.js +0 -57
- package/dist/chains/xlayer/eip7702/volume-helpers.d.ts +0 -148
- package/dist/chains/xlayer/eip7702/volume-helpers.js +0 -48
- package/dist/chains/xlayer/eoa/eoa-bundle-swap-helpers.d.ts +0 -126
- package/dist/chains/xlayer/eoa/eoa-bundle-swap-helpers.js +0 -228
- package/dist/contracts/tm-bundle-helpers.d.ts +0 -88
- package/dist/contracts/tm-bundle-helpers.js +0 -72
- package/dist/contracts/tm-bundle-merkle/utils-disperse.d.ts +0 -1
- package/dist/contracts/tm-bundle-merkle/utils-disperse.js +0 -1
- package/dist/contracts/tm-bundle-merkle/utils-pairwise.d.ts +0 -1
- package/dist/contracts/tm-bundle-merkle/utils-pairwise.js +0 -1
- package/dist/contracts/tm-bundle-merkle/utils-sweep.d.ts +0 -1
- package/dist/contracts/tm-bundle-merkle/utils-sweep.js +0 -1
- package/dist/dex/direct-router-helpers.d.ts +0 -264
- package/dist/dex/direct-router-helpers.js +0 -539
- package/dist/dex/types.d.ts +0 -81
- package/dist/dex/types.js +0 -1
- package/dist/exports/root-bundle-and-tooling.d.ts +0 -27
- package/dist/exports/root-bundle-and-tooling.js +0 -30
- package/dist/exports/root-eni-and-bsc-iro.d.ts +0 -26
- package/dist/exports/root-eni-and-bsc-iro.js +0 -66
- package/dist/exports/root-foundations.d.ts +0 -35
- package/dist/exports/root-foundations.js +0 -70
- package/dist/exports/root-swap-dex-and-xlayer.d.ts +0 -30
- package/dist/exports/root-swap-dex-and-xlayer.js +0 -78
- package/dist/flap/index.d.ts +0 -10
- package/dist/flap/index.js +0 -8
- package/dist/flows/index.d.ts +0 -1
- package/dist/flows/index.js +0 -1
- package/dist/merkle/index.d.ts +0 -12
- package/dist/merkle/index.js +0 -11
- package/dist/shared/clients/index.d.ts +0 -8
- package/dist/shared/clients/index.js +0 -8
- package/dist/shared/constants/quote.d.ts +0 -30
- package/dist/shared/constants/quote.js +0 -37
- package/dist/shared/flap/portal-bundle-merkle/core-helpers.d.ts +0 -32
- package/dist/shared/flap/portal-bundle-merkle/core-helpers.js +0 -83
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first-helpers.d.ts +0 -125
- package/dist/shared/flap/portal-bundle-merkle/swap-buy-first-helpers.js +0 -113
- package/dist/shared/flap/portal-bundle-merkle/swap-helpers.d.ts +0 -149
- package/dist/shared/flap/portal-bundle-merkle/swap-helpers.js +0 -259
- package/dist/shared/foundation/dex/v3-path.d.ts +0 -6
- package/dist/shared/foundation/dex/v3-path.js +0 -35
- package/dist/shared/foundation/gas/bundle-gas.d.ts +0 -49
- package/dist/shared/foundation/gas/bundle-gas.js +0 -93
- package/dist/shared/foundation/gas/profit-hop.d.ts +0 -20
- package/dist/shared/foundation/gas/profit-hop.js +0 -72
- package/dist/shared/foundation/index.d.ts +0 -13
- package/dist/shared/foundation/index.js +0 -12
- package/dist/shared/foundation/nonce/nonce-manager.d.ts +0 -17
- package/dist/shared/foundation/nonce/nonce-manager.js +0 -183
- package/dist/shared/foundation/normalize-unknown.d.ts +0 -9
- package/dist/shared/foundation/normalize-unknown.js +0 -29
- package/dist/shared/foundation/sdk-logger.d.ts +0 -13
- package/dist/shared/foundation/sdk-logger.js +0 -12
- package/dist/shared/foundation/tx/build-request.d.ts +0 -17
- package/dist/shared/foundation/tx/build-request.js +0 -25
- package/dist/shared/foundation/tx/sign-batch.d.ts +0 -5
- package/dist/shared/foundation/tx/sign-batch.js +0 -26
- package/dist/shared/foundation/tx/wallet-sign-patch.d.ts +0 -1
- package/dist/shared/foundation/tx/wallet-sign-patch.js +0 -18
- package/dist/shared/foundation/types/airdrop-sweep.d.ts +0 -79
- package/dist/shared/foundation/types/airdrop-sweep.js +0 -1
- package/dist/shared/foundation/types/erc20.d.ts +0 -65
- package/dist/shared/foundation/types/erc20.js +0 -1
- package/dist/shared/foundation/types/holders-maker.d.ts +0 -64
- package/dist/shared/foundation/types/holders-maker.js +0 -1
- package/dist/shared/foundation/types/index.d.ts +0 -7
- package/dist/shared/foundation/types/index.js +0 -1
- package/dist/shared/foundation/types/lp-inspect.d.ts +0 -102
- package/dist/shared/foundation/types/lp-inspect.js +0 -1
- package/dist/shared/foundation/types/multicall.d.ts +0 -5
- package/dist/shared/foundation/types/multicall.js +0 -1
- package/dist/shared/foundation/types/private-sale.d.ts +0 -35
- package/dist/shared/foundation/types/private-sale.js +0 -1
- package/dist/shared/foundation/types/quote-helpers.d.ts +0 -17
- package/dist/shared/foundation/types/quote-helpers.js +0 -1
- package/dist/types/errors.d.ts +0 -27
- package/dist/types/errors.js +0 -34
- package/dist/utils/holders-maker/addresses.d.ts +0 -12
- package/dist/utils/holders-maker/addresses.js +0 -15
- package/dist/utils/holders-maker/buy-tx.d.ts +0 -44
- package/dist/utils/holders-maker/buy-tx.js +0 -278
- package/dist/utils/holders-maker/constants.d.ts +0 -6
- package/dist/utils/holders-maker/constants.js +0 -7
- package/dist/utils/holders-maker/disperse.d.ts +0 -18
- package/dist/utils/holders-maker/disperse.js +0 -90
- package/dist/utils/holders-maker/routing.d.ts +0 -4
- package/dist/utils/holders-maker/routing.js +0 -45
- package/dist/utils/holders-maker/transfer-tx.d.ts +0 -4
- package/dist/utils/holders-maker/transfer-tx.js +0 -67
- package/dist/utils/holders-maker-helpers.d.ts +0 -9
- package/dist/utils/holders-maker-helpers.js +0 -9
- package/dist/utils/hop-chains.d.ts +0 -35
- package/dist/utils/hop-chains.js +0 -215
- package/dist/utils/lp-inspect-helpers.d.ts +0 -9
- package/dist/utils/lp-inspect-helpers.js +0 -109
- package/dist/utils/types/airdrop-sweep.d.ts +0 -1
- package/dist/utils/types/airdrop-sweep.js +0 -1
- package/dist/utils/types/contract-factory.d.ts +0 -1
- package/dist/utils/types/contract-factory.js +0 -1
- package/dist/utils/types/erc20.d.ts +0 -1
- package/dist/utils/types/erc20.js +0 -1
- package/dist/utils/types/errors.d.ts +0 -1
- package/dist/utils/types/errors.js +0 -1
- package/dist/utils/types/holders-maker.d.ts +0 -1
- package/dist/utils/types/holders-maker.js +0 -1
- package/dist/utils/types/hop-chains.d.ts +0 -8
- package/dist/utils/types/hop-chains.js +0 -1
- package/dist/utils/types/index.d.ts +0 -13
- package/dist/utils/types/index.js +0 -1
- package/dist/utils/types/lp-inspect.d.ts +0 -1
- package/dist/utils/types/lp-inspect.js +0 -1
- package/dist/utils/types/mpc-exclusive.d.ts +0 -5
- package/dist/utils/types/mpc-exclusive.js +0 -1
- package/dist/utils/types/private-sale.d.ts +0 -1
- package/dist/utils/types/private-sale.js +0 -1
- package/dist/utils/types/quote-helpers.d.ts +0 -1
- package/dist/utils/types/quote-helpers.js +0 -1
- package/dist/utils/types/stealth-transfer.d.ts +0 -44
- package/dist/utils/types/stealth-transfer.js +0 -1
- package/dist/utils/types/wallet.d.ts +0 -25
- package/dist/utils/types/wallet.js +0 -1
- package/dist/vanity/index.d.ts +0 -5
- package/dist/vanity/index.js +0 -5
- package/src/abis/contracts/TaxToken.json +0 -969
- package/src/abis/contracts/TokenManager.json +0 -836
- package/src/abis/contracts/TokenManager2.json +0 -136
- package/src/abis/contracts/TokenManagerHelper3.json +0 -993
- /package/dist/{abis/contracts → shared/abis}/TokenManager.json +0 -0
- /package/dist/{abis/contracts → shared/abis}/TokenManagerHelper3.json +0 -0
|
@@ -1,15 +1,592 @@
|
|
|
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;
|
|
1
6
|
/**
|
|
2
|
-
*
|
|
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)
|
|
3
538
|
*/
|
|
4
|
-
import { GAS_LIMITS, CHAINS, getProfitRecipient } from '../../../shared/constants/index.js';
|
|
5
539
|
import { ethers, Contract, Wallet } from 'ethers';
|
|
6
540
|
import { calculateSellAmount } from '../../../utils/swap-helpers.js';
|
|
7
|
-
import { NonceManager, getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT
|
|
8
|
-
import { ADDRESSES, BLOCKRAZOR_BUILDER_EOA } from '../../../
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
541
|
+
import { NonceManager, getDeadline, buildProfitHopTransactions, PROFIT_HOP_COUNT } from '../../../utils/bundle-helpers.js';
|
|
542
|
+
import { ADDRESSES, PROFIT_CONFIG, BLOCKRAZOR_BUILDER_EOA } from '../../../utils/constants.js';
|
|
543
|
+
import { quoteV2, quoteV3 } from '../../../utils/quote-helpers.js';
|
|
544
|
+
import { V2_ROUTER_ABI, V3_ROUTER02_ABI, ERC20_BALANCE_ABI, ERC20_ABI } from '../../../shared/abis/common.js';
|
|
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
|
+
*/
|
|
11
588
|
export async function pancakeBundleSwapMerkle(params) {
|
|
12
|
-
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces
|
|
589
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKey, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces // ✅ 可选:前端预获取的 nonces
|
|
13
590
|
} = params;
|
|
14
591
|
// ✅ 判断是否使用原生代币(BNB)或 ERC20 代币(如 USDT)
|
|
15
592
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
@@ -24,14 +601,14 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
24
601
|
// ✅ 并行获取 gasPrice 和卖出数量
|
|
25
602
|
const [gasPrice, sellAmountResult] = await Promise.all([
|
|
26
603
|
getGasPrice(context.provider, config),
|
|
27
|
-
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
604
|
+
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
28
605
|
]);
|
|
29
606
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
30
607
|
const quoteResult = await quoteSellOutput({
|
|
31
608
|
routeParams,
|
|
32
609
|
sellAmountWei,
|
|
33
610
|
provider: context.provider,
|
|
34
|
-
v2RouterAddress: config.v2RouterAddress
|
|
611
|
+
v2RouterAddress: config.v2RouterAddress
|
|
35
612
|
});
|
|
36
613
|
const buyerBudget = await calculateBuyerBudget({
|
|
37
614
|
buyer,
|
|
@@ -40,7 +617,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
40
617
|
useNativeToken,
|
|
41
618
|
quoteToken,
|
|
42
619
|
quoteTokenDecimals,
|
|
43
|
-
provider: context.provider
|
|
620
|
+
provider: context.provider
|
|
44
621
|
});
|
|
45
622
|
const swapUnsigned = await buildSwapTransactions({
|
|
46
623
|
routeParams,
|
|
@@ -50,7 +627,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
50
627
|
seller,
|
|
51
628
|
tokenAddress,
|
|
52
629
|
useNativeToken,
|
|
53
|
-
v2RouterAddress: config.v2RouterAddress
|
|
630
|
+
v2RouterAddress: config.v2RouterAddress
|
|
54
631
|
});
|
|
55
632
|
// ✅ 修复:利润计算应基于 BNB 数量,不是 ERC20 数量
|
|
56
633
|
// 如果输出是原生代币(BNB),直接使用报价结果
|
|
@@ -71,7 +648,9 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
71
648
|
// ERC20→BNB 报价完成
|
|
72
649
|
}
|
|
73
650
|
// ✅ 获取贿赂金额
|
|
74
|
-
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
651
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
652
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
653
|
+
: 0n;
|
|
75
654
|
const needBribeTx = bribeAmount > 0n;
|
|
76
655
|
// ✅ 使用共享的 NonceManager 规划 nonce
|
|
77
656
|
// 如果前端传入了 startNonces,直接使用(性能优化)
|
|
@@ -84,54 +663,48 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
84
663
|
approvalExists: false, // ✅ 已移除授权
|
|
85
664
|
profitNeeded: profitAmount > 0n,
|
|
86
665
|
needBribeTx,
|
|
87
|
-
nonceManager
|
|
666
|
+
nonceManager
|
|
88
667
|
});
|
|
89
668
|
// ✅ 并行签名所有交易
|
|
90
669
|
const signPromises = [];
|
|
91
670
|
// 贿赂交易
|
|
92
671
|
if (needBribeTx && noncePlan.bribeNonce !== undefined) {
|
|
93
|
-
signPromises.push(seller
|
|
94
|
-
.signTransaction({
|
|
672
|
+
signPromises.push(seller.signTransaction({
|
|
95
673
|
to: BLOCKRAZOR_BUILDER_EOA,
|
|
96
674
|
value: bribeAmount,
|
|
97
675
|
nonce: noncePlan.bribeNonce,
|
|
98
676
|
gasPrice,
|
|
99
677
|
gasLimit: BRIBE_GAS_LIMIT,
|
|
100
678
|
chainId: context.chainId,
|
|
101
|
-
type: txType
|
|
102
|
-
})
|
|
103
|
-
.then((tx) => ({ type: 'bribe', tx })));
|
|
679
|
+
type: txType
|
|
680
|
+
}).then(tx => ({ type: 'bribe', tx })));
|
|
104
681
|
}
|
|
105
682
|
// 卖出交易
|
|
106
|
-
signPromises.push(seller
|
|
107
|
-
.signTransaction({
|
|
683
|
+
signPromises.push(seller.signTransaction({
|
|
108
684
|
...swapUnsigned.sellUnsigned,
|
|
109
685
|
from: seller.address,
|
|
110
686
|
nonce: noncePlan.sellerNonce,
|
|
111
687
|
gasLimit: finalGasLimit,
|
|
112
688
|
gasPrice,
|
|
113
689
|
chainId: context.chainId,
|
|
114
|
-
type: txType
|
|
115
|
-
})
|
|
116
|
-
.then((tx) => ({ type: 'sell', tx })));
|
|
690
|
+
type: txType
|
|
691
|
+
}).then(tx => ({ type: 'sell', tx })));
|
|
117
692
|
// 买入交易
|
|
118
|
-
signPromises.push(buyer
|
|
119
|
-
.signTransaction({
|
|
693
|
+
signPromises.push(buyer.signTransaction({
|
|
120
694
|
...swapUnsigned.buyUnsigned,
|
|
121
695
|
from: buyer.address,
|
|
122
696
|
nonce: noncePlan.buyerNonce,
|
|
123
697
|
gasLimit: finalGasLimit,
|
|
124
698
|
gasPrice,
|
|
125
699
|
chainId: context.chainId,
|
|
126
|
-
type: txType
|
|
127
|
-
})
|
|
128
|
-
.then((tx) => ({ type: 'buy', tx })));
|
|
700
|
+
type: txType
|
|
701
|
+
}).then(tx => ({ type: 'buy', tx })));
|
|
129
702
|
// ✅ 并行执行所有签名
|
|
130
703
|
const signedResults = await Promise.all(signPromises);
|
|
131
704
|
// 按类型提取结果
|
|
132
|
-
const bribeTx = signedResults.find(
|
|
133
|
-
const signedSell = signedResults.find(
|
|
134
|
-
const signedBuy = signedResults.find(
|
|
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;
|
|
135
708
|
nonceManager.clearTemp();
|
|
136
709
|
validateFinalBalances({
|
|
137
710
|
sameAddress,
|
|
@@ -143,7 +716,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
143
716
|
useNativeToken,
|
|
144
717
|
quoteTokenDecimals,
|
|
145
718
|
provider: context.provider,
|
|
146
|
-
buyerAddress: buyer.address
|
|
719
|
+
buyerAddress: buyer.address
|
|
147
720
|
});
|
|
148
721
|
// ✅ 组装顺序:贿赂 → 卖出 → 买入
|
|
149
722
|
const signedTransactions = [];
|
|
@@ -157,12 +730,12 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
157
730
|
provider: context.provider,
|
|
158
731
|
payerWallet: seller,
|
|
159
732
|
profitAmount,
|
|
160
|
-
profitRecipient:
|
|
733
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
161
734
|
hopCount: PROFIT_HOP_COUNT,
|
|
162
735
|
gasPrice,
|
|
163
736
|
chainId: context.chainId,
|
|
164
737
|
txType,
|
|
165
|
-
startNonce: noncePlan.profitNonce
|
|
738
|
+
startNonce: noncePlan.profitNonce
|
|
166
739
|
});
|
|
167
740
|
signedTransactions.push(...profitHopResult.signedTransactions);
|
|
168
741
|
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
@@ -177,8 +750,8 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
177
750
|
buyAmount: useNativeToken
|
|
178
751
|
? ethers.formatEther(buyerBudget.buyAmountBNB)
|
|
179
752
|
: ethers.formatUnits(buyerBudget.buyAmountBNB, quoteTokenDecimals),
|
|
180
|
-
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
181
|
-
}
|
|
753
|
+
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
754
|
+
}
|
|
182
755
|
};
|
|
183
756
|
}
|
|
184
757
|
/**
|
|
@@ -190,7 +763,7 @@ export async function pancakeBundleSwapMerkle(params) {
|
|
|
190
763
|
* 交易顺序:[授权(可选)] → [卖出] → [买入1, 买入2, ..., 买入N] → [利润]
|
|
191
764
|
*/
|
|
192
765
|
export async function pancakeBatchSwapMerkle(params) {
|
|
193
|
-
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces
|
|
766
|
+
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, startNonces // ✅ 可选:前端预获取的 nonces
|
|
194
767
|
} = params;
|
|
195
768
|
// ✅ 校验买方数量(最多 24 个)
|
|
196
769
|
const MAX_BUYERS = 24;
|
|
@@ -203,7 +776,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
203
776
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
204
777
|
const context = createPancakeContext(config);
|
|
205
778
|
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
206
|
-
const buyers = buyerPrivateKeys.map(
|
|
779
|
+
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
207
780
|
// ✅ 创建共享资源
|
|
208
781
|
const nonceManager = new NonceManager(context.provider);
|
|
209
782
|
const finalGasLimit = getGasLimit(config);
|
|
@@ -211,14 +784,14 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
211
784
|
// ✅ 并行获取 gasPrice 和卖出数量
|
|
212
785
|
const [gasPrice, sellAmountResult] = await Promise.all([
|
|
213
786
|
getGasPrice(context.provider, config),
|
|
214
|
-
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
787
|
+
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
215
788
|
]);
|
|
216
789
|
const { amount: sellAmountWei, decimals } = sellAmountResult;
|
|
217
790
|
const quoteResult = await quoteSellOutput({
|
|
218
791
|
routeParams,
|
|
219
792
|
sellAmountWei,
|
|
220
793
|
provider: context.provider,
|
|
221
|
-
v2RouterAddress: config.v2RouterAddress
|
|
794
|
+
v2RouterAddress: config.v2RouterAddress
|
|
222
795
|
});
|
|
223
796
|
const estimatedBNBOut = quoteResult.estimatedBNBOut;
|
|
224
797
|
// ✅ 计算每个买方的买入金额(已移除滑点保护:直接使用报价金额)
|
|
@@ -226,12 +799,14 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
226
799
|
const totalBuyAmount = estimatedBNBOut;
|
|
227
800
|
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
228
801
|
// 方式1:使用指定的买入金额(USDT)
|
|
229
|
-
buyAmountsWei = buyerAmounts.map(
|
|
802
|
+
buyAmountsWei = buyerAmounts.map(amt => useNativeToken
|
|
803
|
+
? ethers.parseEther(amt)
|
|
804
|
+
: ethers.parseUnits(amt, quoteTokenDecimals));
|
|
230
805
|
}
|
|
231
806
|
else if (params.buyerRatios && params.buyerRatios.length === buyers.length) {
|
|
232
807
|
// ✅ 方式2:按比例分配卖出所得
|
|
233
808
|
// buyerRatios 如 [0.3, 0.5, 0.2] 表示第一个买方分 30%,第二个 50%,第三个 20%
|
|
234
|
-
buyAmountsWei = params.buyerRatios.map((ratio) => {
|
|
809
|
+
buyAmountsWei = params.buyerRatios.map((ratio, index) => {
|
|
235
810
|
// 按比例计算每个买方的金额
|
|
236
811
|
const amount = (totalBuyAmount * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
237
812
|
return amount;
|
|
@@ -281,17 +856,15 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
281
856
|
else if (routeParams.routeType === 'v3-single') {
|
|
282
857
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
283
858
|
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
284
|
-
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
|
|
285
|
-
{
|
|
859
|
+
const sellSwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
286
860
|
tokenIn: v3TokenIn,
|
|
287
861
|
tokenOut: v3TokenOut,
|
|
288
862
|
fee: v3Fee,
|
|
289
863
|
recipient: useNativeToken ? PANCAKE_V3_ROUTER_ADDRESS : seller.address,
|
|
290
864
|
amountIn: sellAmountWei,
|
|
291
865
|
amountOutMinimum: 0n,
|
|
292
|
-
sqrtPriceLimitX96: 0n
|
|
293
|
-
}
|
|
294
|
-
]);
|
|
866
|
+
sqrtPriceLimitX96: 0n
|
|
867
|
+
}]);
|
|
295
868
|
if (useNativeToken) {
|
|
296
869
|
// 原生代币:需要 unwrap WBNB
|
|
297
870
|
const sellUnwrapData = v3RouterIface.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
@@ -324,17 +897,15 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
324
897
|
else if (routeParams.routeType === 'v3-single') {
|
|
325
898
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
326
899
|
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
327
|
-
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [
|
|
328
|
-
{
|
|
900
|
+
const buySwapData = v3RouterIface.encodeFunctionData('exactInputSingle', [{
|
|
329
901
|
tokenIn: v3TokenOut,
|
|
330
902
|
tokenOut: v3TokenIn,
|
|
331
903
|
fee: v3Fee,
|
|
332
904
|
recipient: buyer.address,
|
|
333
905
|
amountIn: buyAmount,
|
|
334
906
|
amountOutMinimum: 0n,
|
|
335
|
-
sqrtPriceLimitX96: 0n
|
|
336
|
-
}
|
|
337
|
-
]);
|
|
907
|
+
sqrtPriceLimitX96: 0n
|
|
908
|
+
}]);
|
|
338
909
|
return await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
339
910
|
}
|
|
340
911
|
else {
|
|
@@ -342,14 +913,16 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
342
913
|
}
|
|
343
914
|
}));
|
|
344
915
|
// ✅ 获取贿赂金额
|
|
345
|
-
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
916
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
917
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
918
|
+
: 0n;
|
|
346
919
|
// ✅ 规划 Nonce(贿赂和利润都由卖方发送)
|
|
347
920
|
// 卖方: [贿赂(可选)] → [卖出] → [利润(可选)]
|
|
348
921
|
let bribeNonce;
|
|
349
922
|
let sellNonce;
|
|
350
923
|
let buyerNonces;
|
|
351
924
|
// ✅ 如果前端传入了 startNonces,直接使用(性能优化)
|
|
352
|
-
if (startNonces && startNonces.length >= 1 + buyers.length) {
|
|
925
|
+
if (startNonces && startNonces.length >= (1 + buyers.length)) {
|
|
353
926
|
let sellerIdx = 0;
|
|
354
927
|
if (bribeAmount > 0n) {
|
|
355
928
|
bribeNonce = startNonces[0] + sellerIdx++;
|
|
@@ -362,7 +935,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
362
935
|
bribeNonce = await nonceManager.getNextNonce(seller);
|
|
363
936
|
}
|
|
364
937
|
sellNonce = await nonceManager.getNextNonce(seller);
|
|
365
|
-
buyerNonces = await Promise.all(buyers.map(
|
|
938
|
+
buyerNonces = await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
366
939
|
}
|
|
367
940
|
// ✅ 贿赂交易放在首位(由卖方发送)
|
|
368
941
|
let bribeTx = null;
|
|
@@ -374,7 +947,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
374
947
|
gasPrice,
|
|
375
948
|
gasLimit: BRIBE_GAS_LIMIT,
|
|
376
949
|
chainId: context.chainId,
|
|
377
|
-
type: txType
|
|
950
|
+
type: txType
|
|
378
951
|
});
|
|
379
952
|
}
|
|
380
953
|
// ✅ 修复:利润计算应基于 BNB 数量,不是 ERC20 数量
|
|
@@ -392,9 +965,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
392
965
|
}
|
|
393
966
|
// 计算利润 nonce
|
|
394
967
|
const profitNonce = profitAmount > 0n
|
|
395
|
-
? startNonces && startNonces.length >= 1
|
|
396
|
-
? sellNonce + 1
|
|
397
|
-
: await nonceManager.getNextNonce(seller)
|
|
968
|
+
? (startNonces && startNonces.length >= 1 ? sellNonce + 1 : await nonceManager.getNextNonce(seller))
|
|
398
969
|
: undefined;
|
|
399
970
|
nonceManager.clearTemp();
|
|
400
971
|
// ✅ 并行签名所有交易
|
|
@@ -406,7 +977,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
406
977
|
gasLimit: finalGasLimit,
|
|
407
978
|
gasPrice,
|
|
408
979
|
chainId: context.chainId,
|
|
409
|
-
type: txType
|
|
980
|
+
type: txType
|
|
410
981
|
});
|
|
411
982
|
// 2. 并行签名所有买入交易
|
|
412
983
|
const signedBuyPromises = buyers.map((buyer, i) => buyer.signTransaction({
|
|
@@ -416,10 +987,13 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
416
987
|
gasLimit: finalGasLimit,
|
|
417
988
|
gasPrice,
|
|
418
989
|
chainId: context.chainId,
|
|
419
|
-
type: txType
|
|
990
|
+
type: txType
|
|
420
991
|
}));
|
|
421
992
|
// 3. 等待所有签名完成
|
|
422
|
-
const [signedSell, ...signedBuys] = await Promise.all([
|
|
993
|
+
const [signedSell, ...signedBuys] = await Promise.all([
|
|
994
|
+
signedSellPromise,
|
|
995
|
+
...signedBuyPromises
|
|
996
|
+
]);
|
|
423
997
|
// 4. 按顺序组装交易数组:贿赂 → 卖出 → 买入
|
|
424
998
|
const signedTransactions = [];
|
|
425
999
|
if (bribeTx)
|
|
@@ -433,12 +1007,12 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
433
1007
|
provider: context.provider,
|
|
434
1008
|
payerWallet: seller,
|
|
435
1009
|
profitAmount,
|
|
436
|
-
profitRecipient:
|
|
1010
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
437
1011
|
hopCount: PROFIT_HOP_COUNT,
|
|
438
1012
|
gasPrice,
|
|
439
1013
|
chainId: context.chainId,
|
|
440
1014
|
txType,
|
|
441
|
-
startNonce: profitNonce
|
|
1015
|
+
startNonce: profitNonce
|
|
442
1016
|
});
|
|
443
1017
|
signedTransactions.push(...profitHopResult.signedTransactions);
|
|
444
1018
|
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
@@ -448,11 +1022,13 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
448
1022
|
profitHopWallets, // ✅ 导出利润多跳钱包
|
|
449
1023
|
metadata: {
|
|
450
1024
|
sellerAddress: seller.address,
|
|
451
|
-
buyerAddresses: buyers.map(
|
|
1025
|
+
buyerAddresses: buyers.map(b => b.address),
|
|
452
1026
|
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
453
|
-
buyAmounts: buyAmountsWei.map(
|
|
454
|
-
|
|
455
|
-
|
|
1027
|
+
buyAmounts: buyAmountsWei.map(amt => useNativeToken
|
|
1028
|
+
? ethers.formatEther(amt)
|
|
1029
|
+
: ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
1030
|
+
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined
|
|
1031
|
+
}
|
|
456
1032
|
};
|
|
457
1033
|
}
|
|
458
1034
|
/**
|
|
@@ -471,7 +1047,7 @@ export async function pancakeBatchSwapMerkle(params) {
|
|
|
471
1047
|
*/
|
|
472
1048
|
export async function pancakeQuickBatchSwapMerkle(params) {
|
|
473
1049
|
const { sellerPrivateKey, sellAmount, sellPercentage, buyerPrivateKeys, buyerRatios, buyerAmounts, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, disperseHopCount = 0, // ✅ 转账多跳数(默认0=直接转账)
|
|
474
|
-
startNonces
|
|
1050
|
+
startNonces // ✅ 可选:前端预获取的 nonces
|
|
475
1051
|
} = params;
|
|
476
1052
|
// ✅ 判断是否使用原生代币
|
|
477
1053
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
@@ -498,6 +1074,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
498
1074
|
MAX_BUYERS = Math.floor(maxTxs / (2 * disperseHopCount + 3));
|
|
499
1075
|
}
|
|
500
1076
|
MAX_BUYERS = Math.max(1, MAX_BUYERS); // 至少1个
|
|
1077
|
+
console.log(`[pancakeQuickBatchSwapMerkle] 多跳数: ${disperseHopCount}, 最大买方数: ${MAX_BUYERS}`);
|
|
501
1078
|
if (buyerPrivateKeys.length === 0) {
|
|
502
1079
|
throw new Error('至少需要一个买方钱包');
|
|
503
1080
|
}
|
|
@@ -517,7 +1094,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
517
1094
|
}
|
|
518
1095
|
const context = createPancakeContext(config);
|
|
519
1096
|
const seller = new Wallet(sellerPrivateKey, context.provider);
|
|
520
|
-
const buyers = buyerPrivateKeys.map(
|
|
1097
|
+
const buyers = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
521
1098
|
// ✅ 校验卖出路径输出代币是否匹配
|
|
522
1099
|
let sellOutputToken;
|
|
523
1100
|
if (routeParams.routeType === 'v2') {
|
|
@@ -557,16 +1134,30 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
557
1134
|
// ✅ 并行获取 gasPrice 和卖出数量
|
|
558
1135
|
const [gasPrice, sellAmountResult] = await Promise.all([
|
|
559
1136
|
getGasPrice(context.provider, config),
|
|
560
|
-
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
1137
|
+
calculateSellAmount(context.provider, tokenAddress, seller.address, sellAmount, sellPercentage)
|
|
561
1138
|
]);
|
|
562
1139
|
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
|
+
}));
|
|
563
1150
|
const quoteResult = await quoteSellOutput({
|
|
564
1151
|
routeParams,
|
|
565
1152
|
sellAmountWei,
|
|
566
1153
|
provider: context.provider,
|
|
567
|
-
v2RouterAddress: config.v2RouterAddress
|
|
1154
|
+
v2RouterAddress: config.v2RouterAddress
|
|
568
1155
|
});
|
|
569
1156
|
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'}`);
|
|
570
1161
|
// ✅ 计算利润(万分之六)
|
|
571
1162
|
let profitAmount;
|
|
572
1163
|
if (useNativeToken) {
|
|
@@ -578,13 +1169,16 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
578
1169
|
const fee = routeParams.routeType === 'v3-single' ? routeParams.v3Fee : undefined;
|
|
579
1170
|
const estimatedBNBValue = await getERC20ToNativeQuote(context.provider, quoteToken, estimatedOutput, version, fee);
|
|
580
1171
|
profitAmount = calculateProfitAmount(estimatedBNBValue, config.userType);
|
|
1172
|
+
console.log(`[pancakeQuickBatchSwapMerkle] ERC20→BNB 报价: ${outputFormatted} → ${ethers.formatEther(estimatedBNBValue)} BNB`);
|
|
581
1173
|
}
|
|
582
1174
|
const distributableAmount = estimatedOutput - (useNativeToken ? profitAmount : 0n);
|
|
583
1175
|
// ✅ 计算每个买方分到的金额
|
|
584
1176
|
let transferAmountsWei;
|
|
585
1177
|
if (buyerAmounts && buyerAmounts.length === buyers.length) {
|
|
586
1178
|
// 数量模式
|
|
587
|
-
transferAmountsWei = buyerAmounts.map(
|
|
1179
|
+
transferAmountsWei = buyerAmounts.map(amt => useNativeToken
|
|
1180
|
+
? ethers.parseEther(amt)
|
|
1181
|
+
: ethers.parseUnits(amt, quoteTokenDecimals));
|
|
588
1182
|
const totalTransfer = transferAmountsWei.reduce((a, b) => a + b, 0n);
|
|
589
1183
|
if (totalTransfer > distributableAmount) {
|
|
590
1184
|
const formatted = useNativeToken
|
|
@@ -595,7 +1189,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
595
1189
|
}
|
|
596
1190
|
else if (buyerRatios && buyerRatios.length === buyers.length) {
|
|
597
1191
|
// 比例模式
|
|
598
|
-
transferAmountsWei = buyerRatios.map(
|
|
1192
|
+
transferAmountsWei = buyerRatios.map(ratio => {
|
|
599
1193
|
return (distributableAmount * BigInt(Math.round(ratio * 10000))) / 10000n;
|
|
600
1194
|
});
|
|
601
1195
|
}
|
|
@@ -603,22 +1197,22 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
603
1197
|
throw new Error('必须提供 buyerRatios 或 buyerAmounts');
|
|
604
1198
|
}
|
|
605
1199
|
// ✅ 获取贿赂金额
|
|
606
|
-
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
1200
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
1201
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
1202
|
+
: 0n;
|
|
607
1203
|
// ✅ 验证主钱包余额
|
|
608
1204
|
const sellerBalance = await seller.provider.getBalance(seller.address);
|
|
609
1205
|
let sellerGasCost;
|
|
610
1206
|
let sellerRequired;
|
|
611
1207
|
if (useNativeToken) {
|
|
612
1208
|
// BNB 模式:贿赂(BRIBE) + 卖出(gasLimit) + N个原生转账(BRIBE each) + 利润(BRIBE)
|
|
613
|
-
sellerGasCost =
|
|
614
|
-
gasPrice * (BRIBE_GAS_LIMIT + finalGasLimit + BRIBE_GAS_LIMIT * BigInt(buyers.length) + BRIBE_GAS_LIMIT);
|
|
1209
|
+
sellerGasCost = gasPrice * (BRIBE_GAS_LIMIT + finalGasLimit + BRIBE_GAS_LIMIT * BigInt(buyers.length) + BRIBE_GAS_LIMIT);
|
|
615
1210
|
sellerRequired = bribeAmount + sellerGasCost;
|
|
616
1211
|
}
|
|
617
1212
|
else {
|
|
618
1213
|
// ERC20 模式:子钱包已预留 BNB,不需要主钱包转 Gas
|
|
619
1214
|
// 卖方 Gas: 贿赂(BRIBE) + 卖出(gasLimit) + N个ERC20转账(65000 each) + 利润(BRIBE)
|
|
620
|
-
sellerGasCost =
|
|
621
|
-
gasPrice * (BRIBE_GAS_LIMIT + finalGasLimit + ERC20_TRANSFER_GAS * BigInt(buyers.length) + BRIBE_GAS_LIMIT);
|
|
1215
|
+
sellerGasCost = gasPrice * (BRIBE_GAS_LIMIT + finalGasLimit + ERC20_TRANSFER_GAS * BigInt(buyers.length) + BRIBE_GAS_LIMIT);
|
|
622
1216
|
sellerRequired = bribeAmount + sellerGasCost;
|
|
623
1217
|
}
|
|
624
1218
|
if (sellerBalance < sellerRequired) {
|
|
@@ -626,7 +1220,9 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
626
1220
|
}
|
|
627
1221
|
// ==================== 规划 Nonce ====================
|
|
628
1222
|
// ✅ 如果前端传入了 startNonces,直接使用(性能优化)
|
|
629
|
-
let sellerNonce = startNonces && startNonces.length > 0
|
|
1223
|
+
let sellerNonce = startNonces && startNonces.length > 0
|
|
1224
|
+
? startNonces[0]
|
|
1225
|
+
: await nonceManager.getNextNonce(seller);
|
|
630
1226
|
const deadline = Math.floor(Date.now() / 1000) + 600;
|
|
631
1227
|
// ==================== 1. 贿赂交易 ====================
|
|
632
1228
|
let bribeTx = null;
|
|
@@ -638,8 +1234,9 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
638
1234
|
gasPrice,
|
|
639
1235
|
gasLimit: BRIBE_GAS_LIMIT,
|
|
640
1236
|
chainId: context.chainId,
|
|
641
|
-
type: txType
|
|
1237
|
+
type: txType
|
|
642
1238
|
});
|
|
1239
|
+
console.log(`[pancakeQuickBatchSwapMerkle] 贿赂交易已签名`);
|
|
643
1240
|
}
|
|
644
1241
|
// ==================== 2. 卖出交易 ====================
|
|
645
1242
|
const v3RouterIface2 = new ethers.Interface(PANCAKE_V3_ROUTER_ABI);
|
|
@@ -660,17 +1257,15 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
660
1257
|
else if (routeParams.routeType === 'v3-single') {
|
|
661
1258
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
662
1259
|
const v3RouterSeller = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, seller);
|
|
663
|
-
const sellSwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [
|
|
664
|
-
{
|
|
1260
|
+
const sellSwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [{
|
|
665
1261
|
tokenIn: v3TokenIn,
|
|
666
1262
|
tokenOut: v3TokenOut,
|
|
667
1263
|
fee: v3Fee,
|
|
668
1264
|
recipient: useNativeToken ? PANCAKE_V3_ROUTER_ADDRESS : seller.address,
|
|
669
1265
|
amountIn: sellAmountWei,
|
|
670
1266
|
amountOutMinimum: 0n,
|
|
671
|
-
sqrtPriceLimitX96: 0n
|
|
672
|
-
}
|
|
673
|
-
]);
|
|
1267
|
+
sqrtPriceLimitX96: 0n
|
|
1268
|
+
}]);
|
|
674
1269
|
if (useNativeToken) {
|
|
675
1270
|
// 原生代币:需要 unwrap WBNB
|
|
676
1271
|
const sellUnwrapData = v3RouterIface2.encodeFunctionData('unwrapWETH9', [0n, seller.address]);
|
|
@@ -691,18 +1286,19 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
691
1286
|
gasLimit: finalGasLimit,
|
|
692
1287
|
gasPrice,
|
|
693
1288
|
chainId: context.chainId,
|
|
694
|
-
type: txType
|
|
1289
|
+
type: txType
|
|
695
1290
|
});
|
|
1291
|
+
console.log(`[pancakeQuickBatchSwapMerkle] 卖出交易已签名`);
|
|
696
1292
|
// ==================== 3. 转账交易(支持多跳)====================
|
|
697
1293
|
const buyerGasCost = gasPrice * finalGasLimit;
|
|
698
1294
|
// ✅ 生成多跳路径
|
|
699
|
-
const hopPaths = generateDisperseHopPaths(buyers.map(
|
|
1295
|
+
const hopPaths = generateDisperseHopPaths(buyers.map(b => b.address), disperseHopCount, context.provider);
|
|
700
1296
|
// 收集所有中间钱包信息
|
|
701
1297
|
const allHopWallets = [];
|
|
702
|
-
hopPaths.forEach(
|
|
1298
|
+
hopPaths.forEach(path => {
|
|
703
1299
|
allHopWallets.push(...path.hopWalletsInfo);
|
|
704
1300
|
});
|
|
705
|
-
let transferTxs;
|
|
1301
|
+
let transferTxs = [];
|
|
706
1302
|
if (disperseHopCount === 0) {
|
|
707
1303
|
// ✅ 无多跳:直接转账
|
|
708
1304
|
const transferNonces = buyers.map((_, i) => sellerNonce + i);
|
|
@@ -717,14 +1313,17 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
717
1313
|
gasPrice,
|
|
718
1314
|
gasLimit: NATIVE_TRANSFER_GAS_LIMIT,
|
|
719
1315
|
chainId: context.chainId,
|
|
720
|
-
type: txType
|
|
1316
|
+
type: txType
|
|
721
1317
|
});
|
|
722
1318
|
}));
|
|
723
1319
|
}
|
|
724
1320
|
else {
|
|
725
1321
|
const erc20Interface = new ethers.Interface(ERC20_ABI);
|
|
726
1322
|
transferTxs = await Promise.all(buyers.map((buyer, i) => {
|
|
727
|
-
const transferData = erc20Interface.encodeFunctionData('transfer', [
|
|
1323
|
+
const transferData = erc20Interface.encodeFunctionData('transfer', [
|
|
1324
|
+
buyer.address,
|
|
1325
|
+
transferAmountsWei[i]
|
|
1326
|
+
]);
|
|
728
1327
|
return seller.signTransaction({
|
|
729
1328
|
to: quoteToken,
|
|
730
1329
|
data: transferData,
|
|
@@ -733,7 +1332,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
733
1332
|
gasPrice,
|
|
734
1333
|
gasLimit: ERC20_TRANSFER_GAS,
|
|
735
1334
|
chainId: context.chainId,
|
|
736
|
-
type: txType
|
|
1335
|
+
type: txType
|
|
737
1336
|
});
|
|
738
1337
|
}));
|
|
739
1338
|
}
|
|
@@ -773,11 +1372,12 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
773
1372
|
transferTxs = [...bnbTxs, ...erc20Txs];
|
|
774
1373
|
}
|
|
775
1374
|
}
|
|
1375
|
+
console.log(`[pancakeQuickBatchSwapMerkle] ${transferTxs.length} 笔转账交易已签名 (多跳数=${disperseHopCount})`);
|
|
776
1376
|
// ==================== 4. 买入交易 ====================
|
|
777
1377
|
// ✅ 如果前端传入了 startNonces,使用 buyer 部分(从索引 1 开始)
|
|
778
1378
|
const buyerNonces = startNonces && startNonces.length > 1
|
|
779
1379
|
? startNonces.slice(1)
|
|
780
|
-
: await Promise.all(buyers.map(
|
|
1380
|
+
: await Promise.all(buyers.map(buyer => nonceManager.getNextNonce(buyer)));
|
|
781
1381
|
const signedBuys = await Promise.all(buyers.map(async (buyer, i) => {
|
|
782
1382
|
const buyAmount = transferAmountsWei[i];
|
|
783
1383
|
// ✅ ERC20 模式:value = 0(通过代币授权支付)
|
|
@@ -797,17 +1397,15 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
797
1397
|
else if (routeParams.routeType === 'v3-single') {
|
|
798
1398
|
const { v3TokenIn, v3TokenOut, v3Fee } = routeParams;
|
|
799
1399
|
const v3RouterBuyer = new Contract(PANCAKE_V3_ROUTER_ADDRESS, PANCAKE_V3_ROUTER_ABI, buyer);
|
|
800
|
-
const buySwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [
|
|
801
|
-
{
|
|
1400
|
+
const buySwapData = v3RouterIface2.encodeFunctionData('exactInputSingle', [{
|
|
802
1401
|
tokenIn: v3TokenOut,
|
|
803
1402
|
tokenOut: v3TokenIn,
|
|
804
1403
|
fee: v3Fee,
|
|
805
1404
|
recipient: buyer.address,
|
|
806
1405
|
amountIn: buyAmount,
|
|
807
1406
|
amountOutMinimum: 0n,
|
|
808
|
-
sqrtPriceLimitX96: 0n
|
|
809
|
-
}
|
|
810
|
-
]);
|
|
1407
|
+
sqrtPriceLimitX96: 0n
|
|
1408
|
+
}]);
|
|
811
1409
|
buyUnsigned = await v3RouterBuyer.multicall.populateTransaction(deadline, [buySwapData], { value: buyValue });
|
|
812
1410
|
}
|
|
813
1411
|
else {
|
|
@@ -820,9 +1418,10 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
820
1418
|
gasLimit: finalGasLimit,
|
|
821
1419
|
gasPrice,
|
|
822
1420
|
chainId: context.chainId,
|
|
823
|
-
type: txType
|
|
1421
|
+
type: txType
|
|
824
1422
|
});
|
|
825
1423
|
}));
|
|
1424
|
+
console.log(`[pancakeQuickBatchSwapMerkle] ${signedBuys.length} 笔买入交易已签名`);
|
|
826
1425
|
nonceManager.clearTemp();
|
|
827
1426
|
// ==================== 组装交易数组 ====================
|
|
828
1427
|
// BNB 模式:贿赂 → 卖出 → 转账 → 买入 → 利润多跳
|
|
@@ -840,33 +1439,41 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
840
1439
|
provider: context.provider,
|
|
841
1440
|
payerWallet: seller,
|
|
842
1441
|
profitAmount,
|
|
843
|
-
profitRecipient:
|
|
1442
|
+
profitRecipient: PROFIT_CONFIG.RECIPIENT,
|
|
844
1443
|
hopCount: PROFIT_HOP_COUNT,
|
|
845
1444
|
gasPrice,
|
|
846
1445
|
chainId: context.chainId,
|
|
847
1446
|
txType,
|
|
848
|
-
startNonce: sellerNonce
|
|
1447
|
+
startNonce: sellerNonce++
|
|
849
1448
|
});
|
|
850
1449
|
signedTransactions.push(...profitHopResult.signedTransactions);
|
|
851
1450
|
profitHopWallets = profitHopResult.hopWallets; // ✅ 收集利润多跳钱包
|
|
852
1451
|
// 多跳交易已签名
|
|
853
1452
|
}
|
|
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';
|
|
854
1459
|
return {
|
|
855
1460
|
signedTransactions,
|
|
856
1461
|
disperseHopWallets: allHopWallets.length > 0 ? allHopWallets : undefined, // ✅ 返回转账多跳钱包
|
|
857
1462
|
profitHopWallets, // ✅ 返回利润多跳钱包
|
|
858
1463
|
metadata: {
|
|
859
1464
|
sellerAddress: seller.address,
|
|
860
|
-
buyerAddresses: buyers.map(
|
|
1465
|
+
buyerAddresses: buyers.map(b => b.address),
|
|
861
1466
|
sellAmount: ethers.formatUnits(sellAmountWei, decimals),
|
|
862
1467
|
estimatedOutput: useNativeToken
|
|
863
1468
|
? ethers.formatEther(estimatedOutput)
|
|
864
1469
|
: ethers.formatUnits(estimatedOutput, quoteTokenDecimals),
|
|
865
|
-
transferAmounts: transferAmountsWei.map(
|
|
1470
|
+
transferAmounts: transferAmountsWei.map(amt => useNativeToken
|
|
1471
|
+
? ethers.formatEther(amt)
|
|
1472
|
+
: ethers.formatUnits(amt, quoteTokenDecimals)),
|
|
866
1473
|
profitAmount: profitAmount > 0n ? ethers.formatEther(profitAmount) : undefined,
|
|
867
1474
|
useNativeToken,
|
|
868
|
-
disperseHopCount: disperseHopCount > 0 ? disperseHopCount : undefined
|
|
869
|
-
}
|
|
1475
|
+
disperseHopCount: disperseHopCount > 0 ? disperseHopCount : undefined // ✅ 返回多跳数
|
|
1476
|
+
}
|
|
870
1477
|
};
|
|
871
1478
|
}
|
|
872
1479
|
/**
|
|
@@ -877,7 +1484,7 @@ export async function pancakeQuickBatchSwapMerkle(params) {
|
|
|
877
1484
|
* - ✅ 正确处理 nonce:同一钱包在多轮中使用时 nonce 自动递增
|
|
878
1485
|
*/
|
|
879
1486
|
export async function pancakeCrossSwapMerkle(params) {
|
|
880
|
-
const { sellerPrivateKeys, sellAmounts, buyerPrivateKeys, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, buyersPerSell, disperseHopCount = 0
|
|
1487
|
+
const { sellerPrivateKeys, sellAmounts, buyerPrivateKeys, tokenAddress, routeParams, config, quoteToken, quoteTokenDecimals = 18, buyersPerSell, disperseHopCount = 0 } = params;
|
|
881
1488
|
if (sellerPrivateKeys.length === 0) {
|
|
882
1489
|
throw new Error('至少需要一个卖方');
|
|
883
1490
|
}
|
|
@@ -891,8 +1498,8 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
891
1498
|
const context = createPancakeContext(config);
|
|
892
1499
|
const nonceManager = new NonceManager(context.provider);
|
|
893
1500
|
// ✅ 预先获取所有钱包的初始 nonce
|
|
894
|
-
const allSellerWallets = sellerPrivateKeys.map(
|
|
895
|
-
const allBuyerWallets = buyerPrivateKeys.map(
|
|
1501
|
+
const allSellerWallets = sellerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
1502
|
+
const allBuyerWallets = buyerPrivateKeys.map(pk => new Wallet(pk, context.provider));
|
|
896
1503
|
// 使用 Map 去重(同一私钥可能出现多次)
|
|
897
1504
|
const addressToNonce = new Map();
|
|
898
1505
|
// 获取所有卖方 nonce
|
|
@@ -909,6 +1516,7 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
909
1516
|
addressToNonce.set(wallet.address, nonce);
|
|
910
1517
|
}
|
|
911
1518
|
}
|
|
1519
|
+
console.log(`[pancakeCrossSwapMerkle] 初始化完成: ${sellerPrivateKeys.length} 卖方, ${buyerPrivateKeys.length} 买方`);
|
|
912
1520
|
const allSigned = [];
|
|
913
1521
|
const allDisperse = [];
|
|
914
1522
|
const allProfit = [];
|
|
@@ -919,7 +1527,9 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
919
1527
|
// 判断是否使用原生代币
|
|
920
1528
|
const useNativeToken = !quoteToken || quoteToken === ZERO_ADDRESS;
|
|
921
1529
|
// 获取贿赂金额
|
|
922
|
-
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
1530
|
+
const bribeAmount = config.bribeAmount && config.bribeAmount > 0
|
|
1531
|
+
? ethers.parseEther(String(config.bribeAmount))
|
|
1532
|
+
: 0n;
|
|
923
1533
|
const hasBribe = bribeAmount > 0n;
|
|
924
1534
|
for (let i = 0; i < sellerPrivateKeys.length; i++) {
|
|
925
1535
|
const sellerPk = sellerPrivateKeys[i];
|
|
@@ -937,7 +1547,7 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
937
1547
|
// ✅ 获取卖方当前 nonce
|
|
938
1548
|
const sellerNonce = addressToNonce.get(sellerWallet.address);
|
|
939
1549
|
// ✅ 获取并更新买方 nonces(每个买方本轮需要 1 个 nonce)
|
|
940
|
-
const roundBuyerNonces = roundBuyerWallets.map(
|
|
1550
|
+
const roundBuyerNonces = roundBuyerWallets.map(w => {
|
|
941
1551
|
const nonce = addressToNonce.get(w.address);
|
|
942
1552
|
addressToNonce.set(w.address, nonce + 1); // 每个买方用 1 个 nonce
|
|
943
1553
|
return nonce;
|
|
@@ -977,10 +1587,11 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
977
1587
|
quoteToken,
|
|
978
1588
|
quoteTokenDecimals,
|
|
979
1589
|
disperseHopCount,
|
|
980
|
-
startNonces
|
|
1590
|
+
startNonces // ✅ 传入预计算的 nonces
|
|
981
1591
|
});
|
|
982
1592
|
// ✅ 更新卖方 nonce(使用精确计算的值)
|
|
983
1593
|
addressToNonce.set(sellerWallet.address, sellerNonce + sellerNonceConsumed);
|
|
1594
|
+
console.log(`[pancakeCrossSwapMerkle] 轮次 ${i + 1}: 卖方=${sellerWallet.address.slice(0, 10)}..., 买方=${roundBuyerPks.length}, 交易=${res.signedTransactions.length}`);
|
|
984
1595
|
// 累积签名与中间钱包
|
|
985
1596
|
allSigned.push(...res.signedTransactions);
|
|
986
1597
|
if (res.disperseHopWallets)
|
|
@@ -991,14 +1602,15 @@ export async function pancakeCrossSwapMerkle(params) {
|
|
|
991
1602
|
sellerAddress: res.metadata?.sellerAddress || '',
|
|
992
1603
|
buyerAddresses: res.metadata?.buyerAddresses || roundBuyerPks.map(() => ''),
|
|
993
1604
|
sellAmount,
|
|
994
|
-
bundleHash: undefined
|
|
1605
|
+
bundleHash: undefined
|
|
995
1606
|
});
|
|
996
1607
|
}
|
|
997
1608
|
nonceManager.clearTemp();
|
|
1609
|
+
console.log(`[pancakeCrossSwapMerkle] 完成: ${rounds.length} 轮, ${allSigned.length} 笔交易`);
|
|
998
1610
|
return {
|
|
999
1611
|
signedTransactions: allSigned,
|
|
1000
1612
|
rounds,
|
|
1001
1613
|
disperseHopWallets: allDisperse.length > 0 ? allDisperse : undefined,
|
|
1002
|
-
profitHopWallets: allProfit.length > 0 ? allProfit : undefined
|
|
1614
|
+
profitHopWallets: allProfit.length > 0 ? allProfit : undefined
|
|
1003
1615
|
};
|
|
1004
1616
|
}
|