@miradexio/client 0.1.0
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/LICENSE +21 -0
- package/README.md +405 -0
- package/dist/address/base58.d.ts +9 -0
- package/dist/address/base58.d.ts.map +1 -0
- package/dist/address/base58.js +33 -0
- package/dist/address/base58.js.map +1 -0
- package/dist/address/bech32.d.ts +13 -0
- package/dist/address/bech32.d.ts.map +1 -0
- package/dist/address/bech32.js +41 -0
- package/dist/address/bech32.js.map +1 -0
- package/dist/address/evm.d.ts +5 -0
- package/dist/address/evm.d.ts.map +1 -0
- package/dist/address/evm.js +27 -0
- package/dist/address/evm.js.map +1 -0
- package/dist/address/index.d.ts +15 -0
- package/dist/address/index.d.ts.map +1 -0
- package/dist/address/index.js +134 -0
- package/dist/address/index.js.map +1 -0
- package/dist/address/monero.d.ts +15 -0
- package/dist/address/monero.d.ts.map +1 -0
- package/dist/address/monero.js +30 -0
- package/dist/address/monero.js.map +1 -0
- package/dist/address/polkadot.d.ts +10 -0
- package/dist/address/polkadot.d.ts.map +1 -0
- package/dist/address/polkadot.js +36 -0
- package/dist/address/polkadot.js.map +1 -0
- package/dist/address/solana.d.ts +5 -0
- package/dist/address/solana.d.ts.map +1 -0
- package/dist/address/solana.js +17 -0
- package/dist/address/solana.js.map +1 -0
- package/dist/address/ton.d.ts +11 -0
- package/dist/address/ton.d.ts.map +1 -0
- package/dist/address/ton.js +28 -0
- package/dist/address/ton.js.map +1 -0
- package/dist/api/index.d.ts +80 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +213 -0
- package/dist/api/index.js.map +1 -0
- package/dist/atomic-swap/drive.d.ts +22 -0
- package/dist/atomic-swap/drive.d.ts.map +1 -0
- package/dist/atomic-swap/drive.js +713 -0
- package/dist/atomic-swap/drive.js.map +1 -0
- package/dist/atomic-swap/extract.d.ts +46 -0
- package/dist/atomic-swap/extract.d.ts.map +1 -0
- package/dist/atomic-swap/extract.js +55 -0
- package/dist/atomic-swap/extract.js.map +1 -0
- package/dist/atomic-swap/index.d.ts +15 -0
- package/dist/atomic-swap/index.d.ts.map +1 -0
- package/dist/atomic-swap/index.js +13 -0
- package/dist/atomic-swap/index.js.map +1 -0
- package/dist/atomic-swap/monero-sweep/errors.d.ts +2 -0
- package/dist/atomic-swap/monero-sweep/errors.d.ts.map +1 -0
- package/dist/atomic-swap/monero-sweep/errors.js +43 -0
- package/dist/atomic-swap/monero-sweep/errors.js.map +1 -0
- package/dist/atomic-swap/monero-sweep/index.d.ts +33 -0
- package/dist/atomic-swap/monero-sweep/index.d.ts.map +1 -0
- package/dist/atomic-swap/monero-sweep/index.js +415 -0
- package/dist/atomic-swap/monero-sweep/index.js.map +1 -0
- package/dist/atomic-swap/monero-sweep/ring-select.d.ts +12 -0
- package/dist/atomic-swap/monero-sweep/ring-select.d.ts.map +1 -0
- package/dist/atomic-swap/monero-sweep/ring-select.js +61 -0
- package/dist/atomic-swap/monero-sweep/ring-select.js.map +1 -0
- package/dist/atomic-swap/presign.d.ts +101 -0
- package/dist/atomic-swap/presign.d.ts.map +1 -0
- package/dist/atomic-swap/presign.js +460 -0
- package/dist/atomic-swap/presign.js.map +1 -0
- package/dist/atomic-swap/refund.d.ts +72 -0
- package/dist/atomic-swap/refund.d.ts.map +1 -0
- package/dist/atomic-swap/refund.js +224 -0
- package/dist/atomic-swap/refund.js.map +1 -0
- package/dist/atomic-swap/run.d.ts +27 -0
- package/dist/atomic-swap/run.d.ts.map +1 -0
- package/dist/atomic-swap/run.js +282 -0
- package/dist/atomic-swap/run.js.map +1 -0
- package/dist/atomic-swap/snapshot.d.ts +111 -0
- package/dist/atomic-swap/snapshot.d.ts.map +1 -0
- package/dist/atomic-swap/snapshot.js +69 -0
- package/dist/atomic-swap/snapshot.js.map +1 -0
- package/dist/atomic-swap/submit-encsig.d.ts +10 -0
- package/dist/atomic-swap/submit-encsig.d.ts.map +1 -0
- package/dist/atomic-swap/submit-encsig.js +56 -0
- package/dist/atomic-swap/submit-encsig.js.map +1 -0
- package/dist/atomic-swap/types.d.ts +168 -0
- package/dist/atomic-swap/types.d.ts.map +1 -0
- package/dist/atomic-swap/types.js +5 -0
- package/dist/atomic-swap/types.js.map +1 -0
- package/dist/blockchain/quorum-provider.d.ts +25 -0
- package/dist/blockchain/quorum-provider.d.ts.map +1 -0
- package/dist/blockchain/quorum-provider.js +144 -0
- package/dist/blockchain/quorum-provider.js.map +1 -0
- package/dist/cooperative-redeem.d.ts +23 -0
- package/dist/cooperative-redeem.d.ts.map +1 -0
- package/dist/cooperative-redeem.js +40 -0
- package/dist/cooperative-redeem.js.map +1 -0
- package/dist/engine/blockchain-querier.d.ts +34 -0
- package/dist/engine/blockchain-querier.d.ts.map +1 -0
- package/dist/engine/blockchain-querier.js +2 -0
- package/dist/engine/blockchain-querier.js.map +1 -0
- package/dist/engine/engine-state.d.ts +20 -0
- package/dist/engine/engine-state.d.ts.map +1 -0
- package/dist/engine/engine-state.js +9 -0
- package/dist/engine/engine-state.js.map +1 -0
- package/dist/engine/flow-context.d.ts +494 -0
- package/dist/engine/flow-context.d.ts.map +1 -0
- package/dist/engine/flow-context.js +147 -0
- package/dist/engine/flow-context.js.map +1 -0
- package/dist/engine/flows/atomic-flow-state.d.ts +124 -0
- package/dist/engine/flows/atomic-flow-state.d.ts.map +1 -0
- package/dist/engine/flows/atomic-flow-state.js +2 -0
- package/dist/engine/flows/atomic-flow-state.js.map +1 -0
- package/dist/engine/flows/atomic-flow.d.ts +88 -0
- package/dist/engine/flows/atomic-flow.d.ts.map +1 -0
- package/dist/engine/flows/atomic-flow.js +1192 -0
- package/dist/engine/flows/atomic-flow.js.map +1 -0
- package/dist/engine/flows/history-flow-state.d.ts +19 -0
- package/dist/engine/flows/history-flow-state.d.ts.map +1 -0
- package/dist/engine/flows/history-flow-state.js +2 -0
- package/dist/engine/flows/history-flow-state.js.map +1 -0
- package/dist/engine/flows/providers-flow-state.d.ts +14 -0
- package/dist/engine/flows/providers-flow-state.d.ts.map +1 -0
- package/dist/engine/flows/providers-flow-state.js +2 -0
- package/dist/engine/flows/providers-flow-state.js.map +1 -0
- package/dist/engine/flows/quote-flow-state.d.ts +20 -0
- package/dist/engine/flows/quote-flow-state.d.ts.map +1 -0
- package/dist/engine/flows/quote-flow-state.js +2 -0
- package/dist/engine/flows/quote-flow-state.js.map +1 -0
- package/dist/engine/flows/swap-flow-state.d.ts +155 -0
- package/dist/engine/flows/swap-flow-state.d.ts.map +1 -0
- package/dist/engine/flows/swap-flow-state.js +2 -0
- package/dist/engine/flows/swap-flow-state.js.map +1 -0
- package/dist/engine/flows/swap-flow.d.ts +81 -0
- package/dist/engine/flows/swap-flow.d.ts.map +1 -0
- package/dist/engine/flows/swap-flow.js +720 -0
- package/dist/engine/flows/swap-flow.js.map +1 -0
- package/dist/engine/flows/tokens-flow-state.d.ts +16 -0
- package/dist/engine/flows/tokens-flow-state.d.ts.map +1 -0
- package/dist/engine/flows/tokens-flow-state.js +2 -0
- package/dist/engine/flows/tokens-flow-state.js.map +1 -0
- package/dist/engine/miradex-engine.d.ts +152 -0
- package/dist/engine/miradex-engine.d.ts.map +1 -0
- package/dist/engine/miradex-engine.js +278 -0
- package/dist/engine/miradex-engine.js.map +1 -0
- package/dist/engine/pipeline.d.ts +27 -0
- package/dist/engine/pipeline.d.ts.map +1 -0
- package/dist/engine/pipeline.js +166 -0
- package/dist/engine/pipeline.js.map +1 -0
- package/dist/engine/platform.d.ts +220 -0
- package/dist/engine/platform.d.ts.map +1 -0
- package/dist/engine/platform.js +2 -0
- package/dist/engine/platform.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/blockchain.d.ts +33 -0
- package/dist/interfaces/blockchain.d.ts.map +1 -0
- package/dist/interfaces/blockchain.js +2 -0
- package/dist/interfaces/blockchain.js.map +1 -0
- package/dist/interfaces/logger.d.ts +14 -0
- package/dist/interfaces/logger.d.ts.map +1 -0
- package/dist/interfaces/logger.js +7 -0
- package/dist/interfaces/logger.js.map +1 -0
- package/dist/lib/bitcoin/deposit-watcher.d.ts +66 -0
- package/dist/lib/bitcoin/deposit-watcher.d.ts.map +1 -0
- package/dist/lib/bitcoin/deposit-watcher.js +218 -0
- package/dist/lib/bitcoin/deposit-watcher.js.map +1 -0
- package/dist/lib/bitcoin/script-hash.d.ts +8 -0
- package/dist/lib/bitcoin/script-hash.d.ts.map +1 -0
- package/dist/lib/bitcoin/script-hash.js +29 -0
- package/dist/lib/bitcoin/script-hash.js.map +1 -0
- package/dist/lib/bitcoin/sweep.d.ts +56 -0
- package/dist/lib/bitcoin/sweep.d.ts.map +1 -0
- package/dist/lib/bitcoin/sweep.js +185 -0
- package/dist/lib/bitcoin/sweep.js.map +1 -0
- package/dist/lib/bitcoin/tx-verify.d.ts +43 -0
- package/dist/lib/bitcoin/tx-verify.d.ts.map +1 -0
- package/dist/lib/bitcoin/tx-verify.js +202 -0
- package/dist/lib/bitcoin/tx-verify.js.map +1 -0
- package/dist/lib/bitcoin/wallet.d.ts +71 -0
- package/dist/lib/bitcoin/wallet.d.ts.map +1 -0
- package/dist/lib/bitcoin/wallet.js +141 -0
- package/dist/lib/bitcoin/wallet.js.map +1 -0
- package/dist/lib/crypto/bytes.d.ts +21 -0
- package/dist/lib/crypto/bytes.d.ts.map +1 -0
- package/dist/lib/crypto/bytes.js +39 -0
- package/dist/lib/crypto/bytes.js.map +1 -0
- package/dist/lib/crypto/errors.d.ts +12 -0
- package/dist/lib/crypto/errors.d.ts.map +1 -0
- package/dist/lib/crypto/errors.js +16 -0
- package/dist/lib/crypto/errors.js.map +1 -0
- package/dist/lib/crypto/keygen.d.ts +19 -0
- package/dist/lib/crypto/keygen.d.ts.map +1 -0
- package/dist/lib/crypto/keygen.js +24 -0
- package/dist/lib/crypto/keygen.js.map +1 -0
- package/dist/lib/crypto/libp2p-identity.d.ts +25 -0
- package/dist/lib/crypto/libp2p-identity.d.ts.map +1 -0
- package/dist/lib/crypto/libp2p-identity.js +80 -0
- package/dist/lib/crypto/libp2p-identity.js.map +1 -0
- package/dist/lib/crypto/mnemonic.d.ts +28 -0
- package/dist/lib/crypto/mnemonic.d.ts.map +1 -0
- package/dist/lib/crypto/mnemonic.js +97 -0
- package/dist/lib/crypto/mnemonic.js.map +1 -0
- package/dist/lib/crypto/platform.d.ts +10 -0
- package/dist/lib/crypto/platform.d.ts.map +1 -0
- package/dist/lib/crypto/platform.js +38 -0
- package/dist/lib/crypto/platform.js.map +1 -0
- package/dist/lib/crypto/scalars.d.ts +33 -0
- package/dist/lib/crypto/scalars.d.ts.map +1 -0
- package/dist/lib/crypto/scalars.js +81 -0
- package/dist/lib/crypto/scalars.js.map +1 -0
- package/dist/lib/crypto/types.d.ts +24 -0
- package/dist/lib/crypto/types.d.ts.map +1 -0
- package/dist/lib/crypto/types.js +2 -0
- package/dist/lib/crypto/types.js.map +1 -0
- package/dist/lib/crypto/wasm.d.ts +51 -0
- package/dist/lib/crypto/wasm.d.ts.map +1 -0
- package/dist/lib/crypto/wasm.js +192 -0
- package/dist/lib/crypto/wasm.js.map +1 -0
- package/dist/lib/default-config.d.ts +140 -0
- package/dist/lib/default-config.d.ts.map +1 -0
- package/dist/lib/default-config.js +239 -0
- package/dist/lib/default-config.js.map +1 -0
- package/dist/lib/delay.d.ts +6 -0
- package/dist/lib/delay.d.ts.map +1 -0
- package/dist/lib/delay.js +20 -0
- package/dist/lib/delay.js.map +1 -0
- package/dist/lib/errors.d.ts +90 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +129 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/format.d.ts +7 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +43 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/keystore.d.ts +85 -0
- package/dist/lib/keystore.d.ts.map +1 -0
- package/dist/lib/keystore.js +105 -0
- package/dist/lib/keystore.js.map +1 -0
- package/dist/lib/monero/output-scanner.d.ts +53 -0
- package/dist/lib/monero/output-scanner.d.ts.map +1 -0
- package/dist/lib/monero/output-scanner.js +180 -0
- package/dist/lib/monero/output-scanner.js.map +1 -0
- package/dist/lib/monero/rpc.d.ts +131 -0
- package/dist/lib/monero/rpc.d.ts.map +1 -0
- package/dist/lib/monero/rpc.js +267 -0
- package/dist/lib/monero/rpc.js.map +1 -0
- package/dist/lib/monero/verify-lock.d.ts +50 -0
- package/dist/lib/monero/verify-lock.d.ts.map +1 -0
- package/dist/lib/monero/verify-lock.js +161 -0
- package/dist/lib/monero/verify-lock.js.map +1 -0
- package/dist/lib/monero/verify-sweep.d.ts +59 -0
- package/dist/lib/monero/verify-sweep.d.ts.map +1 -0
- package/dist/lib/monero/verify-sweep.js +82 -0
- package/dist/lib/monero/verify-sweep.js.map +1 -0
- package/dist/lib/monero/wasm.d.ts +19 -0
- package/dist/lib/monero/wasm.d.ts.map +1 -0
- package/dist/lib/monero/wasm.js +24 -0
- package/dist/lib/monero/wasm.js.map +1 -0
- package/dist/lib/pow-solver.d.ts +4 -0
- package/dist/lib/pow-solver.d.ts.map +1 -0
- package/dist/lib/pow-solver.js +37 -0
- package/dist/lib/pow-solver.js.map +1 -0
- package/dist/lib/retry.d.ts +86 -0
- package/dist/lib/retry.d.ts.map +1 -0
- package/dist/lib/retry.js +104 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/portable.d.ts +23 -0
- package/dist/portable.d.ts.map +1 -0
- package/dist/portable.js +13 -0
- package/dist/portable.js.map +1 -0
- package/dist/quote-binding.d.ts +31 -0
- package/dist/quote-binding.d.ts.map +1 -0
- package/dist/quote-binding.js +40 -0
- package/dist/quote-binding.js.map +1 -0
- package/dist/swap-executor.d.ts +51 -0
- package/dist/swap-executor.d.ts.map +1 -0
- package/dist/swap-executor.js +138 -0
- package/dist/swap-executor.js.map +1 -0
- package/dist/types/api.d.ts +34 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +18 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/errors.d.ts +94 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +93 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/keys.d.ts +33 -0
- package/dist/types/keys.d.ts.map +1 -0
- package/dist/types/keys.js +2 -0
- package/dist/types/keys.js.map +1 -0
- package/dist/types/protocol.d.ts +93 -0
- package/dist/types/protocol.d.ts.map +1 -0
- package/dist/types/protocol.js +18 -0
- package/dist/types/protocol.js.map +1 -0
- package/dist/types/status.d.ts +3 -0
- package/dist/types/status.d.ts.map +1 -0
- package/dist/types/status.js +23 -0
- package/dist/types/status.js.map +1 -0
- package/dist/types/verification.d.ts +49 -0
- package/dist/types/verification.d.ts.map +1 -0
- package/dist/types/verification.js +34 -0
- package/dist/types/verification.js.map +1 -0
- package/dist/verification/atomic-swap.d.ts +9 -0
- package/dist/verification/atomic-swap.d.ts.map +1 -0
- package/dist/verification/atomic-swap.js +22 -0
- package/dist/verification/atomic-swap.js.map +1 -0
- package/dist/verification/chainflip-networks.d.ts +46 -0
- package/dist/verification/chainflip-networks.d.ts.map +1 -0
- package/dist/verification/chainflip-networks.js +24 -0
- package/dist/verification/chainflip-networks.js.map +1 -0
- package/dist/verification/chainflip.d.ts +61 -0
- package/dist/verification/chainflip.d.ts.map +1 -0
- package/dist/verification/chainflip.js +377 -0
- package/dist/verification/chainflip.js.map +1 -0
- package/dist/verification/constants.d.ts +52 -0
- package/dist/verification/constants.d.ts.map +1 -0
- package/dist/verification/constants.js +54 -0
- package/dist/verification/constants.js.map +1 -0
- package/dist/verification/index.d.ts +71 -0
- package/dist/verification/index.d.ts.map +1 -0
- package/dist/verification/index.js +91 -0
- package/dist/verification/index.js.map +1 -0
- package/dist/verification/memo.d.ts +27 -0
- package/dist/verification/memo.d.ts.map +1 -0
- package/dist/verification/memo.js +52 -0
- package/dist/verification/memo.js.map +1 -0
- package/dist/verification/near-intents.d.ts +91 -0
- package/dist/verification/near-intents.d.ts.map +1 -0
- package/dist/verification/near-intents.js +213 -0
- package/dist/verification/near-intents.js.map +1 -0
- package/dist/verification/rate-oracle.d.ts +32 -0
- package/dist/verification/rate-oracle.d.ts.map +1 -0
- package/dist/verification/rate-oracle.js +43 -0
- package/dist/verification/rate-oracle.js.map +1 -0
- package/dist/verification/shared.d.ts +20 -0
- package/dist/verification/shared.d.ts.map +1 -0
- package/dist/verification/shared.js +25 -0
- package/dist/verification/shared.js.map +1 -0
- package/dist/verification/thorchain-networks.d.ts +35 -0
- package/dist/verification/thorchain-networks.d.ts.map +1 -0
- package/dist/verification/thorchain-networks.js +35 -0
- package/dist/verification/thorchain-networks.js.map +1 -0
- package/dist/verification/thorchain.d.ts +55 -0
- package/dist/verification/thorchain.d.ts.map +1 -0
- package/dist/verification/thorchain.js +232 -0
- package/dist/verification/thorchain.js.map +1 -0
- package/dist/wasm-pins.d.ts +4 -0
- package/dist/wasm-pins.d.ts.map +1 -0
- package/dist/wasm-pins.js +6 -0
- package/dist/wasm-pins.js.map +1 -0
- package/dist/wire/chainflip.zod.d.ts +144 -0
- package/dist/wire/chainflip.zod.d.ts.map +1 -0
- package/dist/wire/chainflip.zod.js +33 -0
- package/dist/wire/chainflip.zod.js.map +1 -0
- package/dist/wire/near-intents.zod.d.ts +376 -0
- package/dist/wire/near-intents.zod.d.ts.map +1 -0
- package/dist/wire/near-intents.zod.js +101 -0
- package/dist/wire/near-intents.zod.js.map +1 -0
- package/dist/wire/server/action.zod.d.ts +1119 -0
- package/dist/wire/server/action.zod.d.ts.map +1 -0
- package/dist/wire/server/action.zod.js +173 -0
- package/dist/wire/server/action.zod.js.map +1 -0
- package/dist/wire/server/common.zod.d.ts +62 -0
- package/dist/wire/server/common.zod.d.ts.map +1 -0
- package/dist/wire/server/common.zod.js +43 -0
- package/dist/wire/server/common.zod.js.map +1 -0
- package/dist/wire/server/index.d.ts +8 -0
- package/dist/wire/server/index.d.ts.map +1 -0
- package/dist/wire/server/index.js +8 -0
- package/dist/wire/server/index.js.map +1 -0
- package/dist/wire/server/pow.zod.d.ts +45 -0
- package/dist/wire/server/pow.zod.d.ts.map +1 -0
- package/dist/wire/server/pow.zod.js +18 -0
- package/dist/wire/server/pow.zod.js.map +1 -0
- package/dist/wire/server/quotes.zod.d.ts +694 -0
- package/dist/wire/server/quotes.zod.d.ts.map +1 -0
- package/dist/wire/server/quotes.zod.js +103 -0
- package/dist/wire/server/quotes.zod.js.map +1 -0
- package/dist/wire/server/swap.zod.d.ts +1981 -0
- package/dist/wire/server/swap.zod.d.ts.map +1 -0
- package/dist/wire/server/swap.zod.js +270 -0
- package/dist/wire/server/swap.zod.js.map +1 -0
- package/dist/wire/server/tokens.zod.d.ts +93 -0
- package/dist/wire/server/tokens.zod.d.ts.map +1 -0
- package/dist/wire/server/tokens.zod.js +28 -0
- package/dist/wire/server/tokens.zod.js.map +1 -0
- package/dist/wire/server/verify.zod.d.ts +30 -0
- package/dist/wire/server/verify.zod.d.ts.map +1 -0
- package/dist/wire/server/verify.zod.js +12 -0
- package/dist/wire/server/verify.zod.js.map +1 -0
- package/dist/wire/thorchain.zod.d.ts +224 -0
- package/dist/wire/thorchain.zod.d.ts.map +1 -0
- package/dist/wire/thorchain.zod.js +51 -0
- package/dist/wire/thorchain.zod.js.map +1 -0
- package/package.json +128 -0
- package/wasm/miradex-rust/README.md +74 -0
- package/wasm/miradex-rust/miradex_rust.d.ts +149 -0
- package/wasm/miradex-rust/miradex_rust.js +943 -0
- package/wasm/miradex-rust/miradex_rust_bg.wasm +0 -0
- package/wasm/miradex-rust/miradex_rust_bg.wasm.d.ts +31 -0
- package/wasm/miradex-rust/package.json +24 -0
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
import { ApiError, NetworkError } from '../../api/index.js';
|
|
2
|
+
import { TERMINAL_STATUSES, isAtomicProtocolData } from '../../types/index.js';
|
|
3
|
+
import { createFlowContext, mergeFlowContext, validateBase, validatePopulated, validateVerified, } from '../flow-context.js';
|
|
4
|
+
import { solveChallenge, encodePowHeader } from '../../lib/pow-solver.js';
|
|
5
|
+
import { verifyDepositAddress } from '../../verification/index.js';
|
|
6
|
+
import { delay } from '../../lib/delay.js';
|
|
7
|
+
import { mapServerStatus } from '../pipeline.js';
|
|
8
|
+
const DEFAULT_POLL_MS = 5_000;
|
|
9
|
+
const DEFAULT_POLL_TIMEOUT_MS = 3_600_000;
|
|
10
|
+
const MAX_TRANSIENT_RETRIES = 5;
|
|
11
|
+
const TRANSIENT_RETRY_MS = 5_000;
|
|
12
|
+
/**
|
|
13
|
+
* Built-in defaults for SwapFlow. Consumers can inspect these without
|
|
14
|
+
* instantiating a flow and may override via `SwapFlowOptions`.
|
|
15
|
+
*/
|
|
16
|
+
export const SWAP_FLOW_CONFIG = {
|
|
17
|
+
pollMs: DEFAULT_POLL_MS,
|
|
18
|
+
pollTimeoutMs: DEFAULT_POLL_TIMEOUT_MS,
|
|
19
|
+
maxRetries: MAX_TRANSIENT_RETRIES,
|
|
20
|
+
retryMs: TRANSIENT_RETRY_MS,
|
|
21
|
+
};
|
|
22
|
+
// Server-side errors whose CAUSE is the user's request (slippage band, memo
|
|
23
|
+
// shape, dust amount, ...), not a transient server condition. They will fail
|
|
24
|
+
// the same way every time, so retrying just burns the registry's swap-create
|
|
25
|
+
// timeout and surfaces the wrong message ("timeout") instead of the real one.
|
|
26
|
+
//
|
|
27
|
+
// Detected by message shape — these come back as 500s from THORChain's
|
|
28
|
+
// `simulate_swap` handler and similar provider validators, but the body has a
|
|
29
|
+
// stable substring we can match on.
|
|
30
|
+
const PERMANENT_API_ERROR_PATTERNS = [
|
|
31
|
+
/less than price limit/i, // THORChain LIM < quote actual at simulate time
|
|
32
|
+
/swap_too_small/i, // THORChain min-amount rejection
|
|
33
|
+
/memo too long/i, // memo overflow
|
|
34
|
+
];
|
|
35
|
+
function isPermanentApiError(err) {
|
|
36
|
+
if (!(err instanceof ApiError))
|
|
37
|
+
return false;
|
|
38
|
+
return PERMANENT_API_ERROR_PATTERNS.some((re) => re.test(err.message));
|
|
39
|
+
}
|
|
40
|
+
function isTransientError(err) {
|
|
41
|
+
if (err instanceof NetworkError)
|
|
42
|
+
return true;
|
|
43
|
+
if (err instanceof ApiError) {
|
|
44
|
+
if (isPermanentApiError(err))
|
|
45
|
+
return false;
|
|
46
|
+
if (err.statusCode >= 500)
|
|
47
|
+
return true;
|
|
48
|
+
if (err.statusCode === 429)
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Translate a raw API/network error into a message that tells the user what
|
|
55
|
+
* to do, not what the server logged. Falls back to the original message for
|
|
56
|
+
* cases we don't have a curated translation for.
|
|
57
|
+
*/
|
|
58
|
+
function humanizeApiError(err) {
|
|
59
|
+
if (err instanceof ApiError) {
|
|
60
|
+
if (/less than price limit/i.test(err.message)) {
|
|
61
|
+
return 'Slippage tolerance is too tight for current market conditions. Increase slippage in the gear icon and try again.';
|
|
62
|
+
}
|
|
63
|
+
if (/swap_too_small/i.test(err.message)) {
|
|
64
|
+
return 'Amount too small for this provider. Increase the swap amount and try again.';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (err instanceof NetworkError) {
|
|
68
|
+
return 'Network error reaching the swap server. Check your connection and try again.';
|
|
69
|
+
}
|
|
70
|
+
return err instanceof Error ? err.message : String(err);
|
|
71
|
+
}
|
|
72
|
+
export class SwapFlow {
|
|
73
|
+
api;
|
|
74
|
+
platform;
|
|
75
|
+
config;
|
|
76
|
+
emitFn;
|
|
77
|
+
abortController = null;
|
|
78
|
+
pollMs;
|
|
79
|
+
pollTimeoutMs;
|
|
80
|
+
retryMs;
|
|
81
|
+
maxRetries;
|
|
82
|
+
flowCtx = null;
|
|
83
|
+
lastPhase = 'idle';
|
|
84
|
+
lastPollKey = null;
|
|
85
|
+
constructor(api, platform, config, emitFn, options) {
|
|
86
|
+
this.api = api;
|
|
87
|
+
this.platform = platform;
|
|
88
|
+
this.config = config;
|
|
89
|
+
this.emitFn = emitFn;
|
|
90
|
+
this.pollMs = options?.pollMs ?? DEFAULT_POLL_MS;
|
|
91
|
+
this.pollTimeoutMs = options?.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
|
|
92
|
+
this.retryMs = options?.retryMs ?? TRANSIENT_RETRY_MS;
|
|
93
|
+
this.maxRetries = options?.maxRetries ?? MAX_TRANSIENT_RETRIES;
|
|
94
|
+
}
|
|
95
|
+
get signal() {
|
|
96
|
+
return this.abortController?.signal ?? AbortSignal.abort();
|
|
97
|
+
}
|
|
98
|
+
get logger() {
|
|
99
|
+
return this.platform.logger;
|
|
100
|
+
}
|
|
101
|
+
setFlowContext(partial) {
|
|
102
|
+
this.flowCtx = this.flowCtx
|
|
103
|
+
? mergeFlowContext(this.flowCtx, partial)
|
|
104
|
+
: createFlowContext(partial);
|
|
105
|
+
}
|
|
106
|
+
requirePopulated(phase) {
|
|
107
|
+
if (!this.flowCtx) {
|
|
108
|
+
this.emitError(phase, 'FlowContext not initialized');
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const result = validatePopulated(this.flowCtx, phase);
|
|
112
|
+
if (result.ok)
|
|
113
|
+
return result.data;
|
|
114
|
+
this.emitError(phase, result.error.message);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
requireVerified(phase) {
|
|
118
|
+
if (!this.flowCtx) {
|
|
119
|
+
this.emitError(phase, 'FlowContext not initialized');
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
const result = validateVerified(this.flowCtx, phase);
|
|
123
|
+
if (result.ok)
|
|
124
|
+
return result.data;
|
|
125
|
+
this.emitError(phase, result.error.message);
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
emitError(phase, message) {
|
|
129
|
+
this.transition({
|
|
130
|
+
phase: 'failed',
|
|
131
|
+
snapshot: this.flowCtx,
|
|
132
|
+
error: `[${phase}] ${message}`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
transition(state) {
|
|
136
|
+
const prevPhase = this.lastPhase;
|
|
137
|
+
if (state.phase === prevPhase)
|
|
138
|
+
return;
|
|
139
|
+
this.lastPhase = state.phase;
|
|
140
|
+
this.logger.info({ phase: state.phase, prevPhase, swapId: this.flowCtx?.swapId ?? null, provider: this.flowCtx?.provider ?? null }, 'Swap phase transition');
|
|
141
|
+
this.emitFn(state);
|
|
142
|
+
}
|
|
143
|
+
cancel() {
|
|
144
|
+
this.abortController?.abort();
|
|
145
|
+
}
|
|
146
|
+
async start(params) {
|
|
147
|
+
this.abortController = new AbortController();
|
|
148
|
+
const provider = params.selectedQuote.provider;
|
|
149
|
+
this.logger.info({ provider, fromToken: params.fromToken, toToken: params.toToken, amount: params.amount }, 'SwapFlow.start()');
|
|
150
|
+
this.setFlowContext({
|
|
151
|
+
fromToken: params.fromToken,
|
|
152
|
+
toToken: params.toToken,
|
|
153
|
+
depositAmount: params.amount,
|
|
154
|
+
destAddress: params.destAddress,
|
|
155
|
+
refundAddress: params.refundAddress,
|
|
156
|
+
provider,
|
|
157
|
+
extra: { text: 'Solving proof of work...', type: 'message' },
|
|
158
|
+
});
|
|
159
|
+
try {
|
|
160
|
+
this.transition({ phase: 'solving-pow', snapshot: this.flowCtx });
|
|
161
|
+
const swap = await this.createSwapWithRetry(params, provider);
|
|
162
|
+
this.logger.info({ swapNumber: swap.swapNumber, depositAddress: swap.depositAddress }, 'Swap created');
|
|
163
|
+
this.checkAborted();
|
|
164
|
+
// Expose swapId/swapNumber to listeners (notably EngineRegistry's
|
|
165
|
+
// waitForFirstSwapId) BEFORE verification runs. emitAwaitingDeposit
|
|
166
|
+
// can take 30+ s on chainflip's REST indexer lag — without this
|
|
167
|
+
// early emit the registry's 30 s timeout races against verification
|
|
168
|
+
// and orphans the engine. depositAddr is intentionally NOT exposed
|
|
169
|
+
// here; it stays gated by the verification-success branch inside
|
|
170
|
+
// emitAwaitingDeposit so a verification failure can never leak the
|
|
171
|
+
// address to the UI.
|
|
172
|
+
this.setFlowContext({ swapId: swap.swapNumber, swapNumber: swap.swapNumber });
|
|
173
|
+
// Same-phase emit (still 'creating-swap') — bypass transition()'s
|
|
174
|
+
// dedup guard so the snapshot update reaches subscribers.
|
|
175
|
+
this.emitFn({ phase: 'creating-swap', snapshot: this.flowCtx });
|
|
176
|
+
await this.emitAwaitingDeposit(swap, params, provider);
|
|
177
|
+
await this.pollLoop(swap.swapNumber);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
this.handleError(err);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Resume an existing swap. The engine has typically just fetched
|
|
185
|
+
* `detail` to determine which flow to dispatch to; pass it in to skip a
|
|
186
|
+
* redundant `getSwapDetail` round-trip. Without this, a brief network
|
|
187
|
+
* blip on the second fetch sends `ApiClient.withRetry` into an
|
|
188
|
+
* unbounded `network`/`server` retry loop (capped at 60s per backoff,
|
|
189
|
+
* exponential growth), keeping the UI on `creating-swap` for minutes
|
|
190
|
+
* before the call eventually succeeds.
|
|
191
|
+
*/
|
|
192
|
+
async resume(swapId, provider, fromToken, toToken, cachedDetail) {
|
|
193
|
+
this.abortController = new AbortController();
|
|
194
|
+
this.logger.info({ swapId, provider, cached: cachedDetail !== undefined }, 'SwapFlow.resume()');
|
|
195
|
+
this.setFlowContext({
|
|
196
|
+
provider, fromToken, toToken, swapId,
|
|
197
|
+
extra: { text: 'Loading swap...', type: 'message' },
|
|
198
|
+
});
|
|
199
|
+
try {
|
|
200
|
+
this.transition({ phase: 'creating-swap', snapshot: this.flowCtx });
|
|
201
|
+
const detail = cachedDetail ?? (await this.fetchDetailWithRetry(swapId));
|
|
202
|
+
if (TERMINAL_STATUSES.has(detail.status)) {
|
|
203
|
+
this.logger.info({ swapId, status: detail.status }, 'Resumed swap is terminal');
|
|
204
|
+
// Hydrate receipt fields from `detail` before emitting the terminal
|
|
205
|
+
// phase. The live awaiting-deposit code path populates these into
|
|
206
|
+
// flowCtx, but on terminal-resume we never reach that path — without
|
|
207
|
+
// this hydration the rendered receipt is missing addresses, expected
|
|
208
|
+
// out, and USD values, and the swap-info card disappears on reload.
|
|
209
|
+
// Skips re-running verifyDepositAddress: verification is a pre-commit
|
|
210
|
+
// gate; once a swap is terminal, the outcome (completed/refunded) is
|
|
211
|
+
// the source of truth, not a fresh chain query against possibly-
|
|
212
|
+
// rotated vaults.
|
|
213
|
+
this.setFlowContext({
|
|
214
|
+
depositAddr: detail.depositAddress,
|
|
215
|
+
destAddress: detail.destAddress,
|
|
216
|
+
refundAddress: detail.refundAddress,
|
|
217
|
+
expectedOut: detail.expectedAmountOut,
|
|
218
|
+
amountInUsd: detail.amountInUsd,
|
|
219
|
+
expectedOutUsd: detail.expectedAmountOutUsd,
|
|
220
|
+
provider,
|
|
221
|
+
});
|
|
222
|
+
this.emitTerminal(detail, fromToken, toToken);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
this.checkAborted();
|
|
226
|
+
// Hydrate flow context (QR, addresses, verification) but pick the
|
|
227
|
+
// initial phase based on the ACTUAL server status — don't pretend
|
|
228
|
+
// we're awaiting deposit if the swap is already past that point.
|
|
229
|
+
await this.emitInitialResumePhase(detail, {
|
|
230
|
+
destAddress: detail.destAddress,
|
|
231
|
+
refundAddress: detail.refundAddress ?? '',
|
|
232
|
+
toToken, fromToken, amount: detail.amountIn,
|
|
233
|
+
fromChain: detail.fromChain,
|
|
234
|
+
toChain: detail.toChain,
|
|
235
|
+
}, provider);
|
|
236
|
+
await this.pollLoop(swapId);
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
this.handleError(err);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async fetchDetailWithRetry(swapId) {
|
|
243
|
+
let lastErr;
|
|
244
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
245
|
+
try {
|
|
246
|
+
return await this.api.getSwapDetail(swapId);
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
if (!isTransientError(err))
|
|
250
|
+
throw err;
|
|
251
|
+
lastErr = err;
|
|
252
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
253
|
+
this.logger.warn({ swapId, attempt: attempt + 1, error: msg }, 'Server error fetching swap, retrying');
|
|
254
|
+
this.setFlowContext({
|
|
255
|
+
extra: { text: `Server error, retrying (${attempt + 1}/${this.maxRetries})...`, type: 'warning' },
|
|
256
|
+
});
|
|
257
|
+
this.transition({ phase: 'creating-swap', snapshot: this.flowCtx });
|
|
258
|
+
await delay(this.retryMs * (attempt + 1), this.signal).catch(() => { });
|
|
259
|
+
this.checkAborted();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
throw lastErr ?? new Error(`Failed to fetch swap ${swapId} after retries`);
|
|
263
|
+
}
|
|
264
|
+
async createSwapWithRetry(params, provider) {
|
|
265
|
+
let lastErr;
|
|
266
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
267
|
+
try {
|
|
268
|
+
const challenge = await this.api.getChallenge();
|
|
269
|
+
const solution = await solveChallenge(challenge);
|
|
270
|
+
const powHeader = encodePowHeader(solution);
|
|
271
|
+
this.logger.debug({ algorithm: challenge.algorithm, attempt }, 'PoW challenge solved');
|
|
272
|
+
this.checkAborted();
|
|
273
|
+
if (attempt === 0) {
|
|
274
|
+
this.setFlowContext({ extra: { text: 'Creating swap...', type: 'message' } });
|
|
275
|
+
}
|
|
276
|
+
this.transition({ phase: 'creating-swap', snapshot: this.flowCtx });
|
|
277
|
+
return await this.api.createSwap({
|
|
278
|
+
from: params.fromToken, to: params.toToken, amount: params.amount,
|
|
279
|
+
destAddress: params.destAddress, refundAddress: params.refundAddress,
|
|
280
|
+
provider, variantId: params.selectedQuote.variantId,
|
|
281
|
+
fromChain: params.fromChain, toChain: params.toChain,
|
|
282
|
+
slippageBps: params.slippageBps ?? this.config.slippageBps,
|
|
283
|
+
}, powHeader);
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
if (!isTransientError(err))
|
|
287
|
+
throw err;
|
|
288
|
+
lastErr = err;
|
|
289
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
290
|
+
this.logger.warn({ attempt: attempt + 1, error: msg }, 'Server error during swap creation, retrying');
|
|
291
|
+
this.setFlowContext({
|
|
292
|
+
extra: { text: `Server error, retrying (${attempt + 1}/${this.maxRetries})...`, type: 'warning' },
|
|
293
|
+
});
|
|
294
|
+
this.transition({ phase: 'creating-swap', snapshot: this.flowCtx });
|
|
295
|
+
await delay(this.retryMs * (attempt + 1), this.signal).catch(() => { });
|
|
296
|
+
this.checkAborted();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
throw lastErr ?? new Error('Failed to create swap after retries');
|
|
300
|
+
}
|
|
301
|
+
async emitAwaitingDeposit(swap, params, provider) {
|
|
302
|
+
let verification = null;
|
|
303
|
+
if (swap.depositAddress && swap.verification) {
|
|
304
|
+
verification = await verifyDepositAddress({
|
|
305
|
+
depositAddress: swap.depositAddress,
|
|
306
|
+
verification: swap.verification,
|
|
307
|
+
destAddress: params.destAddress,
|
|
308
|
+
refundAddress: params.refundAddress,
|
|
309
|
+
toToken: params.toToken,
|
|
310
|
+
amount: params.amount ?? swap.amountIn,
|
|
311
|
+
fromChain: params.fromChain,
|
|
312
|
+
toChain: params.toChain,
|
|
313
|
+
fromToken: params.fromToken,
|
|
314
|
+
network: this.config.network,
|
|
315
|
+
protocol: isAtomicProtocolData(swap.protocolData)
|
|
316
|
+
? {
|
|
317
|
+
psbt: swap.protocolData.psbt,
|
|
318
|
+
lock_address: swap.protocolData.lock_address,
|
|
319
|
+
timelock_blocks: swap.protocolData.timelock_blocks,
|
|
320
|
+
}
|
|
321
|
+
: undefined,
|
|
322
|
+
expectedAmountOut: swap.expectedAmountOut ?? undefined,
|
|
323
|
+
expectedDestAddress: params.destAddress,
|
|
324
|
+
fetchFn: this.config.fetchFn,
|
|
325
|
+
signal: this.signal,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
// Bail out before any state mutation if the engine was destroyed
|
|
329
|
+
// mid-verification. With Fix 2 the verifier's own retry loop and
|
|
330
|
+
// fetches respect the abort signal, but a verification that already
|
|
331
|
+
// resolved on its last successful attempt before destroy() fired
|
|
332
|
+
// would still fall through to setFlowContext + transition without
|
|
333
|
+
// this guard.
|
|
334
|
+
this.checkAborted();
|
|
335
|
+
// Log the full check list at info level on every verification — both
|
|
336
|
+
// pass and fail. Each entry carries `name`, `passed`, and `detail`
|
|
337
|
+
// (the broker mismatch reason for failed checks). This is the
|
|
338
|
+
// primary diagnostic surface when a verification regresses; the
|
|
339
|
+
// narrow info log on the success path was hiding the data the
|
|
340
|
+
// operator actually needs to see.
|
|
341
|
+
this.logger.info({
|
|
342
|
+
swapNumber: swap.swapNumber,
|
|
343
|
+
verified: verification?.verified ?? null,
|
|
344
|
+
provider: verification?.provider ?? null,
|
|
345
|
+
checks: verification?.checks ?? [],
|
|
346
|
+
}, 'Deposit address verification');
|
|
347
|
+
if (verification && !verification.verified) {
|
|
348
|
+
const failedChecks = verification.checks.filter((c) => !c.passed);
|
|
349
|
+
this.logger.warn({
|
|
350
|
+
swapNumber: swap.swapNumber,
|
|
351
|
+
provider: verification.provider,
|
|
352
|
+
failedChecks,
|
|
353
|
+
allChecks: verification.checks,
|
|
354
|
+
}, 'Deposit address verification FAILED');
|
|
355
|
+
// Withhold depositAddr / qr / destAddress on failure so the UI can't
|
|
356
|
+
// accidentally invite a deposit. Keep verification + identifiers so
|
|
357
|
+
// the failure screen has a swap reference and can show why.
|
|
358
|
+
this.setFlowContext({
|
|
359
|
+
verification,
|
|
360
|
+
swapId: swap.swapNumber,
|
|
361
|
+
swapNumber: swap.swapNumber,
|
|
362
|
+
depositAddr: null,
|
|
363
|
+
destAddress: null,
|
|
364
|
+
qr: null,
|
|
365
|
+
extra: {
|
|
366
|
+
text: "We couldn't verify this swap with the provider. To stay safe, please try a different provider or start a new swap.",
|
|
367
|
+
type: 'error',
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
const baseResult = validateBase(this.flowCtx, 'verification-failed');
|
|
371
|
+
if (!baseResult.ok) {
|
|
372
|
+
this.logger.error({ error: baseResult.error }, 'verification-failed FlowContext invalid');
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
this.transition({ phase: 'verification-failed', snapshot: baseResult.data });
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const depositAddr = swap.depositAddress ?? '';
|
|
379
|
+
let qr = null;
|
|
380
|
+
if (depositAddr) {
|
|
381
|
+
try {
|
|
382
|
+
qr = await this.platform.generateQr(depositAddr);
|
|
383
|
+
}
|
|
384
|
+
catch { /* non-fatal */ }
|
|
385
|
+
}
|
|
386
|
+
let verificationSourceUrl = null;
|
|
387
|
+
if (swap.verification) {
|
|
388
|
+
if ('status_url' in swap.verification)
|
|
389
|
+
verificationSourceUrl = swap.verification.status_url;
|
|
390
|
+
else if ('inbound_addresses_url' in swap.verification)
|
|
391
|
+
verificationSourceUrl = swap.verification.inbound_addresses_url;
|
|
392
|
+
}
|
|
393
|
+
this.setFlowContext({
|
|
394
|
+
swapId: swap.swapNumber, swapNumber: swap.swapNumber, provider,
|
|
395
|
+
depositAddr, depositAmount: swap.amountIn,
|
|
396
|
+
destAddress: params.destAddress,
|
|
397
|
+
refundAddress: params.refundAddress,
|
|
398
|
+
amountInUsd: swap.amountInUsd ?? null,
|
|
399
|
+
expectedOut: swap.expectedAmountOut ?? null,
|
|
400
|
+
expectedOutUsd: swap.expectedAmountOutUsd ?? null,
|
|
401
|
+
expiresAt: swap.expiresAt ?? null,
|
|
402
|
+
qr, verification: verification ?? {
|
|
403
|
+
verified: false,
|
|
404
|
+
provider,
|
|
405
|
+
checks: [{ name: 'Server verification', passed: false, detail: 'Pending' }],
|
|
406
|
+
timestamp: Date.now(),
|
|
407
|
+
},
|
|
408
|
+
verificationSourceUrl,
|
|
409
|
+
extra: { text: `Awaiting deposit to ${depositAddr.slice(0, 16)}...`, type: 'message' },
|
|
410
|
+
});
|
|
411
|
+
const populated = this.requirePopulated('awaiting-deposit');
|
|
412
|
+
if (!populated)
|
|
413
|
+
return;
|
|
414
|
+
this.transition({ phase: 'awaiting-deposit', snapshot: populated });
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Hydrate flow context and pick the initial phase based on the actual
|
|
418
|
+
* server status. Called from resume() so a swap that's already past the
|
|
419
|
+
* deposit stage doesn't briefly flash through `awaiting-deposit` before
|
|
420
|
+
* the first poll tick catches up.
|
|
421
|
+
*/
|
|
422
|
+
async emitInitialResumePhase(detail, params, provider) {
|
|
423
|
+
// Fast path for pre-deposit statuses: reuse the existing emit that
|
|
424
|
+
// emits `awaiting-deposit` with QR + verification context.
|
|
425
|
+
const preDepositStatuses = new Set([
|
|
426
|
+
'initializing',
|
|
427
|
+
'pending',
|
|
428
|
+
'awaiting_funding',
|
|
429
|
+
]);
|
|
430
|
+
if (preDepositStatuses.has(detail.status)) {
|
|
431
|
+
await this.emitAwaitingDeposit(detail, params, provider);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
// Post-deposit: hydrate the context enough to satisfy `requireVerified`
|
|
435
|
+
// for later transitions, then jump straight to the right phase.
|
|
436
|
+
let verification = null;
|
|
437
|
+
if (detail.depositAddress && detail.verification) {
|
|
438
|
+
try {
|
|
439
|
+
verification = await verifyDepositAddress({
|
|
440
|
+
depositAddress: detail.depositAddress,
|
|
441
|
+
verification: detail.verification,
|
|
442
|
+
destAddress: params.destAddress,
|
|
443
|
+
refundAddress: params.refundAddress,
|
|
444
|
+
toToken: params.toToken,
|
|
445
|
+
amount: params.amount ?? detail.amountIn,
|
|
446
|
+
fromChain: params.fromChain,
|
|
447
|
+
toChain: params.toChain,
|
|
448
|
+
fromToken: params.fromToken,
|
|
449
|
+
network: this.config.network,
|
|
450
|
+
protocol: isAtomicProtocolData(detail.protocolData)
|
|
451
|
+
? {
|
|
452
|
+
psbt: detail.protocolData.psbt,
|
|
453
|
+
lock_address: detail.protocolData.lock_address,
|
|
454
|
+
timelock_blocks: detail.protocolData.timelock_blocks,
|
|
455
|
+
}
|
|
456
|
+
: undefined,
|
|
457
|
+
expectedAmountOut: detail.expectedAmountOut ?? undefined,
|
|
458
|
+
expectedDestAddress: params.destAddress,
|
|
459
|
+
fetchFn: this.config.fetchFn,
|
|
460
|
+
signal: this.signal,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
// Best-effort on resume — don't block if verification upstream fails.
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
// Symmetric to `emitAwaitingDeposit`: log the full check list so the
|
|
468
|
+
// operator can see exactly which check fired and why. Resume's
|
|
469
|
+
// post-deposit path doesn't gate on verification (the swap is already
|
|
470
|
+
// past that point), but the diagnostic value of the log is the same.
|
|
471
|
+
this.logger.info({
|
|
472
|
+
swapNumber: detail.swapNumber,
|
|
473
|
+
status: detail.status,
|
|
474
|
+
verified: verification?.verified ?? null,
|
|
475
|
+
provider: verification?.provider ?? null,
|
|
476
|
+
checks: verification?.checks ?? [],
|
|
477
|
+
}, 'Deposit address verification (resume post-deposit)');
|
|
478
|
+
let verificationSourceUrl = null;
|
|
479
|
+
if (detail.verification) {
|
|
480
|
+
if ('status_url' in detail.verification)
|
|
481
|
+
verificationSourceUrl = detail.verification.status_url;
|
|
482
|
+
else if ('inbound_addresses_url' in detail.verification)
|
|
483
|
+
verificationSourceUrl = detail.verification.inbound_addresses_url;
|
|
484
|
+
}
|
|
485
|
+
const depositAddr = detail.depositAddress ?? '';
|
|
486
|
+
let qr = null;
|
|
487
|
+
if (depositAddr) {
|
|
488
|
+
try {
|
|
489
|
+
qr = await this.platform.generateQr(depositAddr);
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
// Non-fatal — a post-deposit resume can tolerate a missing QR.
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
this.setFlowContext({
|
|
496
|
+
swapId: detail.swapNumber,
|
|
497
|
+
swapNumber: detail.swapNumber,
|
|
498
|
+
provider,
|
|
499
|
+
depositAddr,
|
|
500
|
+
depositAmount: detail.amountIn,
|
|
501
|
+
destAddress: params.destAddress,
|
|
502
|
+
refundAddress: params.refundAddress,
|
|
503
|
+
amountInUsd: detail.amountInUsd ?? null,
|
|
504
|
+
expectedOut: detail.expectedAmountOut ?? null,
|
|
505
|
+
expectedOutUsd: detail.expectedAmountOutUsd ?? null,
|
|
506
|
+
expiresAt: detail.expiresAt ?? null,
|
|
507
|
+
qr,
|
|
508
|
+
verification: verification ?? {
|
|
509
|
+
verified: true,
|
|
510
|
+
provider,
|
|
511
|
+
checks: [{ name: 'Server verification', passed: true, detail: 'Resumed' }],
|
|
512
|
+
timestamp: Date.now(),
|
|
513
|
+
},
|
|
514
|
+
verificationSourceUrl,
|
|
515
|
+
});
|
|
516
|
+
const verified = this.requireVerified(`resume:${detail.status}`);
|
|
517
|
+
if (!verified)
|
|
518
|
+
return;
|
|
519
|
+
switch (detail.status) {
|
|
520
|
+
case 'deposited':
|
|
521
|
+
this.setFlowContext({ extra: { text: 'Confirming deposit...', type: 'message' } });
|
|
522
|
+
this.transition({
|
|
523
|
+
phase: 'confirming',
|
|
524
|
+
snapshot: verified,
|
|
525
|
+
requiredAction: detail.requiredAction ?? null,
|
|
526
|
+
});
|
|
527
|
+
return;
|
|
528
|
+
case 'swapping':
|
|
529
|
+
this.setFlowContext({ extra: { text: 'Swapping...', type: 'message' } });
|
|
530
|
+
this.transition({
|
|
531
|
+
phase: 'swapping',
|
|
532
|
+
snapshot: verified,
|
|
533
|
+
requiredAction: detail.requiredAction ?? null,
|
|
534
|
+
});
|
|
535
|
+
return;
|
|
536
|
+
case 'sending':
|
|
537
|
+
this.setFlowContext({ extra: { text: 'Sending output...', type: 'message' } });
|
|
538
|
+
this.transition({ phase: 'sending', snapshot: verified });
|
|
539
|
+
return;
|
|
540
|
+
case 'cancelling': {
|
|
541
|
+
const msg = detail.requiredAction?.message ??
|
|
542
|
+
'Swap is cancelling — refund in progress. Waiting for terminal status.';
|
|
543
|
+
this.setFlowContext({ extra: { text: msg, type: 'message' } });
|
|
544
|
+
this.transition({
|
|
545
|
+
phase: 'cancelling',
|
|
546
|
+
snapshot: verified,
|
|
547
|
+
requiredAction: detail.requiredAction ?? null,
|
|
548
|
+
});
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
default:
|
|
552
|
+
// Unknown non-terminal status — land on awaiting-deposit as a
|
|
553
|
+
// safe default (poll loop will correct on first tick).
|
|
554
|
+
await this.emitAwaitingDeposit(detail, params, provider);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async pollLoop(swapId) {
|
|
559
|
+
const deadline = Date.now() + this.pollTimeoutMs;
|
|
560
|
+
let tick = 0;
|
|
561
|
+
while (Date.now() < deadline && !this.signal.aborted) {
|
|
562
|
+
await delay(this.pollMs, this.signal).catch(() => { });
|
|
563
|
+
if (this.signal.aborted) {
|
|
564
|
+
this.logger.info({ swapId }, 'Poll cancelled');
|
|
565
|
+
this.transition({ phase: 'cancelled', snapshot: this.flowCtx });
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
tick++;
|
|
569
|
+
let detail;
|
|
570
|
+
try {
|
|
571
|
+
detail = await this.api.getSwapDetail(swapId);
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
if (detail.verification && !this.flowCtx?.verification?.verified) {
|
|
577
|
+
this.setFlowContext({ verification: {
|
|
578
|
+
verified: true,
|
|
579
|
+
provider: detail.verification.provider,
|
|
580
|
+
checks: [{ name: 'Server verification', passed: true, detail: 'Populated' }],
|
|
581
|
+
timestamp: Date.now(),
|
|
582
|
+
} });
|
|
583
|
+
}
|
|
584
|
+
const mapped = mapServerStatus(detail.status);
|
|
585
|
+
const pollKey = `${detail.status}|${mapped ?? ''}`;
|
|
586
|
+
if (pollKey !== this.lastPollKey) {
|
|
587
|
+
this.logger.debug({ swapId, serverStatus: detail.status, mappedStage: mapped, tick }, 'Poll tick');
|
|
588
|
+
this.lastPollKey = pollKey;
|
|
589
|
+
}
|
|
590
|
+
if (TERMINAL_STATUSES.has(detail.status)) {
|
|
591
|
+
this.logger.info({ swapId, status: detail.status }, 'Poll detected terminal status');
|
|
592
|
+
this.emitTerminalFromDetail(detail);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (!mapped)
|
|
596
|
+
continue;
|
|
597
|
+
switch (detail.status) {
|
|
598
|
+
case 'deposited': {
|
|
599
|
+
this.setFlowContext({ extra: { text: 'Confirming deposit...', type: 'message' } });
|
|
600
|
+
const v = this.requireVerified('confirming');
|
|
601
|
+
if (v)
|
|
602
|
+
this.transition({ phase: 'confirming', snapshot: v, requiredAction: detail.requiredAction ?? null });
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
case 'swapping': {
|
|
606
|
+
this.setFlowContext({ extra: { text: 'Swapping...', type: 'message' } });
|
|
607
|
+
const v = this.requireVerified('swapping');
|
|
608
|
+
if (v)
|
|
609
|
+
this.transition({ phase: 'swapping', snapshot: v, requiredAction: detail.requiredAction ?? null });
|
|
610
|
+
break;
|
|
611
|
+
}
|
|
612
|
+
case 'cancelling': {
|
|
613
|
+
// Atomicswap cancel path. Emit the dedicated `cancelling` phase so
|
|
614
|
+
// the UI can render "TxCancel broadcast, waiting for refund" text
|
|
615
|
+
// without pretending the swap is still making forward progress.
|
|
616
|
+
const msg = detail.requiredAction?.message ??
|
|
617
|
+
'Swap is cancelling — refund in progress. Waiting for terminal status.';
|
|
618
|
+
this.setFlowContext({ extra: { text: msg, type: 'message' } });
|
|
619
|
+
const v = this.requireVerified('cancelling');
|
|
620
|
+
if (v) {
|
|
621
|
+
this.transition({
|
|
622
|
+
phase: 'cancelling',
|
|
623
|
+
snapshot: v,
|
|
624
|
+
requiredAction: detail.requiredAction ?? null,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
case 'sending': {
|
|
630
|
+
this.setFlowContext({ extra: { text: 'Sending output...', type: 'message' } });
|
|
631
|
+
const v = this.requireVerified('sending');
|
|
632
|
+
if (v)
|
|
633
|
+
this.transition({ phase: 'sending', snapshot: v });
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
default: break;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
emitTerminal(detail, fromToken, toToken) {
|
|
641
|
+
this.setFlowContext({
|
|
642
|
+
swapId: detail.swapNumber,
|
|
643
|
+
swapNumber: detail.swapNumber,
|
|
644
|
+
fromToken,
|
|
645
|
+
toToken,
|
|
646
|
+
depositAmount: detail.amountIn,
|
|
647
|
+
});
|
|
648
|
+
switch (detail.status) {
|
|
649
|
+
case 'completed':
|
|
650
|
+
this.setFlowContext({ extra: { text: 'Swap completed', type: 'message' } });
|
|
651
|
+
this.transition({
|
|
652
|
+
phase: 'completed',
|
|
653
|
+
snapshot: this.flowCtx,
|
|
654
|
+
actualOut: detail.actualAmountOut ?? '',
|
|
655
|
+
outputTxHash: detail.outputTxHash ?? null,
|
|
656
|
+
durationSec: detail.durationSeconds ?? null,
|
|
657
|
+
});
|
|
658
|
+
return;
|
|
659
|
+
case 'refunded':
|
|
660
|
+
this.setFlowContext({
|
|
661
|
+
extra: { text: 'BTC refunded to your address.', type: 'message' },
|
|
662
|
+
});
|
|
663
|
+
this.transition({
|
|
664
|
+
phase: 'refunded',
|
|
665
|
+
snapshot: this.flowCtx,
|
|
666
|
+
refundTxid: detail.refundTxHash ?? null,
|
|
667
|
+
actualOut: detail.actualAmountOut ?? '',
|
|
668
|
+
durationSec: detail.durationSeconds ?? null,
|
|
669
|
+
});
|
|
670
|
+
return;
|
|
671
|
+
case 'punished':
|
|
672
|
+
this.setFlowContext({
|
|
673
|
+
extra: { text: 'Refund deadline missed — BTC was punished.', type: 'error' },
|
|
674
|
+
});
|
|
675
|
+
this.transition({ phase: 'punished', snapshot: this.flowCtx });
|
|
676
|
+
return;
|
|
677
|
+
case 'expired':
|
|
678
|
+
this.setFlowContext({ extra: { text: 'Swap expired.', type: 'error' } });
|
|
679
|
+
this.transition({ phase: 'expired', snapshot: this.flowCtx });
|
|
680
|
+
return;
|
|
681
|
+
default:
|
|
682
|
+
this.setFlowContext({
|
|
683
|
+
extra: { text: `Swap ended: ${detail.status}`, type: 'error' },
|
|
684
|
+
});
|
|
685
|
+
this.transition({
|
|
686
|
+
phase: 'failed',
|
|
687
|
+
snapshot: this.flowCtx,
|
|
688
|
+
error: `Swap ended with status: ${detail.status}`,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
emitTerminalFromDetail(detail) {
|
|
693
|
+
this.emitTerminal(detail, detail.fromToken, detail.toToken);
|
|
694
|
+
}
|
|
695
|
+
checkAborted() {
|
|
696
|
+
if (this.signal.aborted) {
|
|
697
|
+
this.transition({ phase: 'cancelled', snapshot: this.flowCtx });
|
|
698
|
+
throw new SwapFlowCancelledError();
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
handleError(err) {
|
|
702
|
+
if (err instanceof SwapFlowCancelledError || this.signal.aborted) {
|
|
703
|
+
this.logger.info({ swapId: this.flowCtx?.swapId ?? null }, 'SwapFlow cancelled');
|
|
704
|
+
this.transition({ phase: 'cancelled', snapshot: this.flowCtx });
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const rawMessage = err instanceof Error ? err.message : String(err);
|
|
708
|
+
const userMessage = humanizeApiError(err);
|
|
709
|
+
this.logger.error({ swapId: this.flowCtx?.swapId ?? null, error: rawMessage }, 'SwapFlow error');
|
|
710
|
+
this.setFlowContext({ extra: { text: userMessage, type: 'error' } });
|
|
711
|
+
this.transition({
|
|
712
|
+
phase: 'failed', snapshot: this.flowCtx,
|
|
713
|
+
error: userMessage,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
class SwapFlowCancelledError extends Error {
|
|
718
|
+
constructor() { super('Swap flow cancelled'); this.name = 'SwapFlowCancelledError'; }
|
|
719
|
+
}
|
|
720
|
+
//# sourceMappingURL=swap-flow.js.map
|