@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,713 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main action loop: polls the server, inspects requiredAction, and performs
|
|
3
|
+
* the appropriate client-side operation. Handles fund, encsig, sweep,
|
|
4
|
+
* sidecar retries, and terminal states in one unified loop.
|
|
5
|
+
*/
|
|
6
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
7
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
|
|
8
|
+
// `crypto.randomUUID` is gated to secure contexts in some browser versions —
|
|
9
|
+
// self-hosted miradex-web on plain HTTP (LAN IP, .local, onion) hits
|
|
10
|
+
// "globalThis.crypto.randomUUID is not a function" before any swap code can
|
|
11
|
+
// run. `crypto.getRandomValues` has no such restriction (it's the underlying
|
|
12
|
+
// entropy primitive), so synthesise a v4 UUID from 16 random bytes when the
|
|
13
|
+
// shortcut isn't available.
|
|
14
|
+
function randomUuidV4() {
|
|
15
|
+
const c = globalThis.crypto;
|
|
16
|
+
if (c?.randomUUID)
|
|
17
|
+
return c.randomUUID();
|
|
18
|
+
if (!c?.getRandomValues) {
|
|
19
|
+
throw new Error('Missing crypto entropy source (getRandomValues)');
|
|
20
|
+
}
|
|
21
|
+
const b = new Uint8Array(16);
|
|
22
|
+
c.getRandomValues(b);
|
|
23
|
+
// RFC 4122 §4.4 — set version (4) and variant (10xx) bits.
|
|
24
|
+
b[6] = ((b[6] ?? 0) & 0x0f) | 0x40;
|
|
25
|
+
b[8] = ((b[8] ?? 0) & 0x3f) | 0x80;
|
|
26
|
+
const h = Array.from(b, (n) => n.toString(16).padStart(2, '0')).join('');
|
|
27
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
|
|
28
|
+
}
|
|
29
|
+
import { Point } from '@noble/ed25519';
|
|
30
|
+
import { buildUnsignedFundingPsbt, signFundingPsbt } from '../lib/bitcoin/wallet.js';
|
|
31
|
+
import { buildProtocolSnapshot } from './snapshot.js';
|
|
32
|
+
import { computePreSigs, computeRedeemDigest, computeRedeemDigestFromTxHex, deriveLockAddress, } from './presign.js';
|
|
33
|
+
import { sweepMonero } from './monero-sweep/index.js';
|
|
34
|
+
import { verifyXmrLocked } from '../lib/monero/verify-lock.js';
|
|
35
|
+
import { TERMINAL_STATUSES, VerificationError, ProtocolError, } from '../types/index.js';
|
|
36
|
+
import { constantTimeEqualHex } from '../lib/crypto/bytes.js';
|
|
37
|
+
import { encsignDigest, verifyDleqProof, ensureWasm } from '../lib/crypto/wasm.js';
|
|
38
|
+
import { verifyDepositAddress } from '../verification/index.js';
|
|
39
|
+
import { delay } from '../lib/delay.js';
|
|
40
|
+
import { SwapCancelledError } from './types.js';
|
|
41
|
+
import { extractProtocolData } from './extract.js';
|
|
42
|
+
import { FUNDING_POLL_MS, FUNDING_SETTLE_MS, SWEEP_TIMEOUT_MS, MIN_XMR_CONFIRMATIONS as CFG_MIN_XMR_CONFIRMATIONS, } from '../lib/default-config.js';
|
|
43
|
+
const POLL_MS = FUNDING_POLL_MS * 3; // driver polls less often than the UI deposit watcher
|
|
44
|
+
const TIMEOUT_MS = SWEEP_TIMEOUT_MS;
|
|
45
|
+
const FUND_SETTLE_MS = FUNDING_SETTLE_MS;
|
|
46
|
+
const MIN_XMR_CONFIRMATIONS = CFG_MIN_XMR_CONFIRMATIONS;
|
|
47
|
+
const MAX_ENCSIG_FAILURES = 3;
|
|
48
|
+
/**
|
|
49
|
+
* Drive a created swap through every phase until a terminal state. The swap
|
|
50
|
+
* must already exist on the server; callers are `runAtomicSwap` and
|
|
51
|
+
* `resumeAtomicSwap`.
|
|
52
|
+
*
|
|
53
|
+
* @internal Not part of the stable public surface.
|
|
54
|
+
*/
|
|
55
|
+
export async function driveSwapToCompletion(options) {
|
|
56
|
+
const { api, swapId, keystoreId, wallet: _wallet, deposit, keystore, network, onProgress, signal, logger: log, blockchain, saveProtocolSnapshot, loadProtocolSnapshot, makerPeerId, makerMultiaddrs, clientVersion, } = options;
|
|
57
|
+
const deadline = Date.now() + TIMEOUT_MS;
|
|
58
|
+
let lastSignedPsbt = '';
|
|
59
|
+
let lastFundedAddress = '';
|
|
60
|
+
let fundedAt = 0;
|
|
61
|
+
let verificationEmitted = false;
|
|
62
|
+
let encsigFailures = 0;
|
|
63
|
+
let aliceDleqVerified = false;
|
|
64
|
+
let xmrLockVerified = false;
|
|
65
|
+
// Bail if the sidecar keeps rejecting /presigs with "no pending PSBT" /
|
|
66
|
+
// "cannot perform presigs" while the swap is still pre-funding. That
|
|
67
|
+
// pattern means the broker entry was consumed (or never armed) and we
|
|
68
|
+
// can't progress without sidecar cooperation. Looping until TIMEOUT_MS
|
|
69
|
+
// burns minutes and starves the failed-receipt restart UX, so cap the
|
|
70
|
+
// retries explicitly.
|
|
71
|
+
let presigsAlreadyDoneStreak = 0;
|
|
72
|
+
const PRESIGS_ALREADY_DONE_MAX = 6;
|
|
73
|
+
log.info({ swapId, network, deadline: new Date(deadline).toISOString() }, '[drive] loop started');
|
|
74
|
+
let lastLoggedAction;
|
|
75
|
+
while (Date.now() < deadline) {
|
|
76
|
+
if (signal?.aborted)
|
|
77
|
+
throw new SwapCancelledError();
|
|
78
|
+
const detail = await api.getSwapDetail(swapId);
|
|
79
|
+
const action = detail.requiredAction?.type;
|
|
80
|
+
const pdx = extractProtocolData(detail);
|
|
81
|
+
if (action !== lastLoggedAction) {
|
|
82
|
+
log.info({ swapId, status: detail.status, action }, '[drive] required-action changed');
|
|
83
|
+
lastLoggedAction = action;
|
|
84
|
+
}
|
|
85
|
+
if (TERMINAL_STATUSES.has(detail.status)) {
|
|
86
|
+
log.info({ swapId, status: detail.status }, '[drive] terminal status reached');
|
|
87
|
+
onProgress({ stage: detail.status, message: `Swap ${detail.status}`, swapId });
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (action === 'fund') {
|
|
91
|
+
const pastFunding = detail.status !== 'initializing'
|
|
92
|
+
&& detail.status !== 'awaiting_funding'
|
|
93
|
+
&& detail.status !== 'pending';
|
|
94
|
+
// "Funding still pending" is now derived from the deposit address being
|
|
95
|
+
// present and the swap being in a pre-deposit state. The legacy
|
|
96
|
+
// top-level `pendingPsbt` field has been absorbed into the typed
|
|
97
|
+
// atomicswap_parameters row (unsigned_lock_psbt) surfaced via
|
|
98
|
+
// protocolData.params.
|
|
99
|
+
const hasPending = !!detail.depositAddress && !pastFunding;
|
|
100
|
+
if (pastFunding && !hasPending && fundedAt > 0 && Date.now() - fundedAt > FUND_SETTLE_MS) {
|
|
101
|
+
await delay(POLL_MS, signal);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (hasPending && fundedAt > 0)
|
|
105
|
+
fundedAt = 0;
|
|
106
|
+
// Lock address is always the primary deposit address (the scanner
|
|
107
|
+
// overwrites this on every poll with the freshest value from the
|
|
108
|
+
// sidecar; retry-with-new-maker updates it automatically).
|
|
109
|
+
const lockAddress = detail.depositAddress;
|
|
110
|
+
if (lockAddress && lockAddress !== lastFundedAddress) {
|
|
111
|
+
if (!aliceDleqVerified) {
|
|
112
|
+
// V-2: DLEQ verification is mandatory before broadcasting TxLock.
|
|
113
|
+
// The proof binds S_a_bitcoin and S_a_monero to the same scalar; it
|
|
114
|
+
// is the only thing that lets Bob recover s_a_xmr autonomously from
|
|
115
|
+
// an on-chain TxRedeem signature if the sidecar later refuses to
|
|
116
|
+
// hand back s_a. Skipping it (the previous fall-through on null /
|
|
117
|
+
// missing) leaves Bob trusting the sidecar end-to-end for sweep.
|
|
118
|
+
const pp = pdx.params;
|
|
119
|
+
if (!pp?.dleq_proof_s_a) {
|
|
120
|
+
log.debug({ swapId }, 'Alice DLEQ proof not yet available; awaiting protocol params');
|
|
121
|
+
await delay(POLL_MS, signal);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (!pp.S_a_monero || !pp.S_a_bitcoin) {
|
|
125
|
+
throw new VerificationError('E_DLEQ_PROOF_REQUIRED', 'Alice DLEQ proof was provided but S_a_bitcoin / S_a_monero is missing; refusing to fund without a complete cross-curve binding');
|
|
126
|
+
}
|
|
127
|
+
log.info({ swapId }, 'Verifying Alice DLEQ proof client-side');
|
|
128
|
+
await ensureWasm();
|
|
129
|
+
const dleqValid = verifyDleqProof(pp.S_a_bitcoin, pp.S_a_monero, pp.dleq_proof_s_a);
|
|
130
|
+
if (!dleqValid) {
|
|
131
|
+
log.error({ swapId }, 'Alice DLEQ proof verification failed');
|
|
132
|
+
throw new VerificationError('E_DLEQ_PROOF_INVALID', 'Alice DLEQ proof is invalid; her Bitcoin and Monero keys are not linked, refusing to fund');
|
|
133
|
+
}
|
|
134
|
+
aliceDleqVerified = true;
|
|
135
|
+
}
|
|
136
|
+
if (!verificationEmitted && detail.verification && detail.depositAddress) {
|
|
137
|
+
verificationEmitted = true;
|
|
138
|
+
const vr = await verifyDepositAddress({
|
|
139
|
+
depositAddress: detail.depositAddress,
|
|
140
|
+
verification: detail.verification,
|
|
141
|
+
destAddress: keystore.swap.receiveAddress,
|
|
142
|
+
refundAddress: keystore.swap.refundAddress,
|
|
143
|
+
toToken: 'XMR',
|
|
144
|
+
amount: '',
|
|
145
|
+
// lock_address is the top-level depositAddress; timelock_blocks
|
|
146
|
+
// comes from the typed atomicswap params when present.
|
|
147
|
+
protocol: detail.depositAddress &&
|
|
148
|
+
detail.protocolData?.type === 'atomicswap' &&
|
|
149
|
+
detail.protocolData.params
|
|
150
|
+
? {
|
|
151
|
+
lock_address: detail.depositAddress,
|
|
152
|
+
timelock_blocks: detail.protocolData.params.cancel_timelock,
|
|
153
|
+
}
|
|
154
|
+
: undefined,
|
|
155
|
+
expectedDestAddress: keystore.swap.receiveAddress,
|
|
156
|
+
});
|
|
157
|
+
onProgress({
|
|
158
|
+
stage: 'signing_psbt',
|
|
159
|
+
message: vr.verified ? 'Contract verified' : 'verification failed',
|
|
160
|
+
swapId,
|
|
161
|
+
verification: vr,
|
|
162
|
+
});
|
|
163
|
+
if (!vr.verified) {
|
|
164
|
+
throw new VerificationError('E_XMR_LOCK_FAILED', 'lock contract verification failed, refusing to sign PSBT');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const protocolParams = pdx.params;
|
|
168
|
+
if (!protocolParams) {
|
|
169
|
+
// The sidecar populates protocolData.depositAddress and
|
|
170
|
+
// protocolData.protocolParams in separate writes. It's normal for
|
|
171
|
+
// the client to see requiredAction=='fund' + a depositAddress
|
|
172
|
+
// before protocolParams has landed. Wait and re-poll rather than
|
|
173
|
+
// bail — the outer TIMEOUT_MS bounds the total wait.
|
|
174
|
+
log.debug({ swapId }, 'Fund requested but protocolParams not yet populated; waiting');
|
|
175
|
+
onProgress({
|
|
176
|
+
stage: 'signing_psbt',
|
|
177
|
+
message: 'Waiting for protocol params from sidecar',
|
|
178
|
+
swapId,
|
|
179
|
+
});
|
|
180
|
+
await delay(POLL_MS, signal);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
// AV-B.1: derive P2WSH from (A, B) locally and reject anything else.
|
|
184
|
+
const derivedLockAddress = deriveLockAddress({
|
|
185
|
+
aHex: protocolParams.A,
|
|
186
|
+
bHex: keystore.keys.B,
|
|
187
|
+
network,
|
|
188
|
+
});
|
|
189
|
+
if (derivedLockAddress !== lockAddress) {
|
|
190
|
+
log.error({ swapId, derivedLockAddress, claimedLockAddress: lockAddress }, 'Lock address derived from (A, B) does not match sidecar');
|
|
191
|
+
throw new VerificationError('E_LOCK_ADDR_MISMATCH', 'derived P2WSH address does not match sidecar, refusing to fund');
|
|
192
|
+
}
|
|
193
|
+
onProgress({
|
|
194
|
+
stage: 'signing_psbt',
|
|
195
|
+
message: `Building lock tx to ${lockAddress.slice(0, 16)}`,
|
|
196
|
+
swapId,
|
|
197
|
+
});
|
|
198
|
+
// Lock amount in sats — derived from the swap's amountIn. The
|
|
199
|
+
// legacy top-level pendingPsbt.amount was a 1:1 synthesis of this.
|
|
200
|
+
const lockAmountSats = detail.amountIn
|
|
201
|
+
? Math.round(parseFloat(detail.amountIn) * 1e8)
|
|
202
|
+
: undefined;
|
|
203
|
+
const utxoInputs = deposit.utxos ?? [deposit];
|
|
204
|
+
log.info({
|
|
205
|
+
swapId,
|
|
206
|
+
lockAddress,
|
|
207
|
+
lockAmountSats,
|
|
208
|
+
utxoCount: utxoInputs.length,
|
|
209
|
+
totalInputSats: utxoInputs.reduce((sum, u) => sum + u.value, 0),
|
|
210
|
+
}, '[fund] Phase 1 — building unsigned lock PSBT');
|
|
211
|
+
// Three-step lock-tx flow matching the Rust binary's order:
|
|
212
|
+
// 1. Build UNSIGNED PSBT — that's what the sidecar puts on the wire
|
|
213
|
+
// in Message2.
|
|
214
|
+
// 2. Submit /presigs (unsigned PSBT + Bob's pre-sigs). Sidecar drives
|
|
215
|
+
// Message2/3/4 with Alice and returns the encsig in the response.
|
|
216
|
+
// 3. Snapshot to disk, then sign locally and submit /fund. The
|
|
217
|
+
// broadcast only happens after the snapshot exists, so the client
|
|
218
|
+
// can never end up with BTC on-chain and no autonomous-refund
|
|
219
|
+
// material.
|
|
220
|
+
const unsigned = buildUnsignedFundingPsbt(_wallet, utxoInputs, lockAddress, network, lockAmountSats);
|
|
221
|
+
log.info({ swapId, txid: unsigned.txid, vout: unsigned.vout, amountSats: unsigned.amountSats }, '[fund] Phase 1 — unsigned PSBT built');
|
|
222
|
+
const signedPsbt = signFundingPsbt(unsigned.psbtBase64, _wallet, network);
|
|
223
|
+
log.info({ swapId }, '[fund] Phase 1 — PSBT signed locally (held in memory)');
|
|
224
|
+
if (!keystore.keys.b || !keystore.swap.refundAddress) {
|
|
225
|
+
throw new ProtocolError('E_PROTOCOL_PARAMS_MISSING', 'keystore is missing b or refund address; cannot compute pre-sigs');
|
|
226
|
+
}
|
|
227
|
+
const preSigs = computePreSigs({
|
|
228
|
+
bHex: keystore.keys.b,
|
|
229
|
+
signedPsbtBase64: signedPsbt,
|
|
230
|
+
protocolParams,
|
|
231
|
+
refundAddress: keystore.swap.refundAddress,
|
|
232
|
+
network,
|
|
233
|
+
});
|
|
234
|
+
log.info({ swapId }, '[fund] Phase 1 — pre-sigs computed');
|
|
235
|
+
onProgress({ stage: 'funding', message: 'Submitting pre-sigs (unsigned PSBT)', swapId });
|
|
236
|
+
log.info({ swapId }, '[fund] Phase 2 — POST /presigs (unsigned PSBT + pre-sigs → awaiting Message3)');
|
|
237
|
+
// If the sidecar already processed /presigs on a prior attempt (state
|
|
238
|
+
// now past AWAITING_DEPOSIT with the encsig on protocolParams), skip
|
|
239
|
+
// the call — it would 409 "no pending PSBT" because the sidecar is
|
|
240
|
+
// waiting for /fund. Idempotent recovery from a schema-mismatch retry.
|
|
241
|
+
// Either encsig counts: amnesty (Partial-only) swaps never get a
|
|
242
|
+
// tx_full_refund_encsig, so gating on full alone would force a
|
|
243
|
+
// re-attempt of /presigs that the sidecar would reject.
|
|
244
|
+
const alreadyHasEncsig = !!(protocolParams.tx_full_refund_encsig || protocolParams.tx_partial_refund_encsig) &&
|
|
245
|
+
!!pdx.redeemDigestHex;
|
|
246
|
+
let presigsResponse = null;
|
|
247
|
+
if (alreadyHasEncsig) {
|
|
248
|
+
log.info({ swapId }, '[fund] Phase 2 — sidecar already past /presigs (encsig on status); skipping presigs call');
|
|
249
|
+
presigsAlreadyDoneStreak = 0;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
try {
|
|
253
|
+
presigsResponse = await api.executeAction(swapId, {
|
|
254
|
+
type: 'presigs',
|
|
255
|
+
unsignedPsbt: unsigned.psbtBase64,
|
|
256
|
+
targetAddress: lockAddress,
|
|
257
|
+
preSigs,
|
|
258
|
+
});
|
|
259
|
+
presigsAlreadyDoneStreak = 0;
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
263
|
+
// "No pending PSBT request" means the sidecar already drove past
|
|
264
|
+
// the presigs step. Re-poll to pick up the encsig off status —
|
|
265
|
+
// DO NOT retry /presigs (the sidecar is past that state).
|
|
266
|
+
if (msg.includes('no pending PSBT') || msg.includes('cannot perform presigs')) {
|
|
267
|
+
presigsAlreadyDoneStreak += 1;
|
|
268
|
+
if (presigsAlreadyDoneStreak >= PRESIGS_ALREADY_DONE_MAX) {
|
|
269
|
+
// The sidecar's PsbtBroker entry was consumed (or never
|
|
270
|
+
// armed after a sidecar restart) and the next iteration of
|
|
271
|
+
// /getSwapDetail still doesn't surface the encsig that
|
|
272
|
+
// would let us skip /presigs. Bail to failed so the
|
|
273
|
+
// failed-receipt restart UX takes over instead of
|
|
274
|
+
// burning the rest of TIMEOUT_MS in a tight retry loop.
|
|
275
|
+
throw new ProtocolError('E_PRESIGS_WEDGED', `sidecar rejected /presigs ${presigsAlreadyDoneStreak} times with "${msg}" — swap is wedged (broker entry consumed but encsig never surfaced on /getSwapDetail)`);
|
|
276
|
+
}
|
|
277
|
+
log.warn({
|
|
278
|
+
swapId,
|
|
279
|
+
error: msg,
|
|
280
|
+
streak: presigsAlreadyDoneStreak,
|
|
281
|
+
cap: PRESIGS_ALREADY_DONE_MAX,
|
|
282
|
+
}, '[fund] Phase 2 — /presigs rejected because sidecar is past that step; re-polling for encsig');
|
|
283
|
+
await delay(POLL_MS, signal);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
log.warn({ swapId, error: msg }, '[fund] Phase 2 — presigs attempt failed, will retry');
|
|
287
|
+
await delay(POLL_MS, signal);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const encsigData = alreadyHasEncsig
|
|
292
|
+
? {
|
|
293
|
+
txFullRefundEncsig: protocolParams.tx_full_refund_encsig ?? null,
|
|
294
|
+
txPartialRefundEncsig: protocolParams.tx_partial_refund_encsig ?? null,
|
|
295
|
+
// Resume / replay path: pull from the typed protocolParams that
|
|
296
|
+
// the sidecar mirrored to /getSwapDetail.
|
|
297
|
+
txCancelSig: protocolParams.tx_cancel_sig ?? null,
|
|
298
|
+
txLockTxid: unsigned.txid,
|
|
299
|
+
txLockVout: unsigned.vout,
|
|
300
|
+
txLockAmountSats: String(unsigned.amountSats),
|
|
301
|
+
}
|
|
302
|
+
: extractPresigsEncsig(presigsResponse);
|
|
303
|
+
log.info({
|
|
304
|
+
swapId,
|
|
305
|
+
hasFullEncsig: !!encsigData.txFullRefundEncsig,
|
|
306
|
+
hasPartialEncsig: !!encsigData.txPartialRefundEncsig,
|
|
307
|
+
txLockTxid: encsigData.txLockTxid,
|
|
308
|
+
}, '[fund] Phase 2 — /presigs response received');
|
|
309
|
+
// Refuse only when neither escape hatch is available. The sidecar
|
|
310
|
+
// returns tx_full_refund_encsig for Legacy/Full variants and
|
|
311
|
+
// tx_partial_refund_encsig (with the amnesty triple on protocolParams)
|
|
312
|
+
// for Partial/Full variants. A Partial-only swap legitimately has
|
|
313
|
+
// a null full encsig — its refund path is TxPartialRefund + TxReclaim.
|
|
314
|
+
if (!encsigData.txFullRefundEncsig && !encsigData.txPartialRefundEncsig) {
|
|
315
|
+
throw new ProtocolError('E_ENCSIG_REFUND_MISSING', 'sidecar accepted /presigs but returned no refund encsig (neither tx_full_refund_encsig nor tx_partial_refund_encsig); refusing to fund without a refund escape hatch');
|
|
316
|
+
}
|
|
317
|
+
log.info({ swapId }, '[fund] Phase 3 — writing recovery snapshot before broadcast');
|
|
318
|
+
// V-16 enabling-work: prefer the value the sidecar returned in the
|
|
319
|
+
// /presigs response, fall back to whatever's already on
|
|
320
|
+
// protocolParams (resume path), then null. Persisting it to the
|
|
321
|
+
// snapshot is a no-op for the current sidecar-publishes-cancel flow,
|
|
322
|
+
// but seeds future autonomous-cancel work with everything it needs.
|
|
323
|
+
const aliceTxCancelSig = encsigData.txCancelSig ?? protocolParams.tx_cancel_sig ?? pdx.aliceTxCancelSig ?? null;
|
|
324
|
+
await maybeWriteSnapshot({
|
|
325
|
+
saveProtocolSnapshot,
|
|
326
|
+
loadProtocolSnapshot,
|
|
327
|
+
swapId,
|
|
328
|
+
externalSwapId: detail.swapNumber,
|
|
329
|
+
keystoreId,
|
|
330
|
+
network,
|
|
331
|
+
clientVersion,
|
|
332
|
+
unsignedPsbtBase64: unsigned.psbtBase64,
|
|
333
|
+
lockAddress,
|
|
334
|
+
lockTxid: unsigned.txid || (encsigData.txLockTxid ?? ''),
|
|
335
|
+
lockVout: encsigData.txLockVout ?? unsigned.vout,
|
|
336
|
+
lockAmountSats: encsigData.txLockAmountSats ?? String(unsigned.amountSats),
|
|
337
|
+
protocolParams,
|
|
338
|
+
encsigFull: encsigData.txFullRefundEncsig,
|
|
339
|
+
encsigPartial: encsigData.txPartialRefundEncsig,
|
|
340
|
+
aliceTxCancelSig,
|
|
341
|
+
makerPeerId: pdx.makerPeerId ?? makerPeerId,
|
|
342
|
+
makerMultiaddrs: pdx.makerMultiaddrs ?? makerMultiaddrs,
|
|
343
|
+
xmrVerification: pdx.xmrVerification,
|
|
344
|
+
sidecarStateAtCapture: detail.status,
|
|
345
|
+
log,
|
|
346
|
+
});
|
|
347
|
+
log.info({ swapId }, '[fund] Phase 3 — snapshot persisted');
|
|
348
|
+
onProgress({ stage: 'funding', message: 'Submitting signed PSBT for broadcast', swapId });
|
|
349
|
+
log.info({ swapId }, '[fund] Phase 4 — POST /fund (signed PSBT → broadcast)');
|
|
350
|
+
// Retry /fund locally with backoff on the "broker not yet awaiting"
|
|
351
|
+
// error. This should be rare since the sidecar now pre-arms the
|
|
352
|
+
// broker in /presigs, but flaky networks or a re-entry from resume
|
|
353
|
+
// can still produce a short window. Retrying /fund is cheap; looping
|
|
354
|
+
// back to the top of drive() would rebuild the PSBT with a stale
|
|
355
|
+
// lockAmountSats (pendingPsbt is gone after /presigs) and produce a
|
|
356
|
+
// different txid — the sidecar would then reject it.
|
|
357
|
+
let fundSucceeded = false;
|
|
358
|
+
for (let attempt = 0; attempt < 6 && !fundSucceeded; attempt++) {
|
|
359
|
+
try {
|
|
360
|
+
await api.executeAction(swapId, { type: 'fund', signedPsbt });
|
|
361
|
+
lastFundedAddress = lockAddress;
|
|
362
|
+
lastSignedPsbt = signedPsbt;
|
|
363
|
+
fundedAt = Date.now();
|
|
364
|
+
fundSucceeded = true;
|
|
365
|
+
onProgress({ stage: 'funding', message: 'Broadcast triggered, protocol resuming', swapId });
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
369
|
+
const brokerNotReady = msg.includes('no swap awaiting broadcast');
|
|
370
|
+
if (!brokerNotReady) {
|
|
371
|
+
log.warn({ swapId, error: msg, attempt }, '[fund] /fund failed; aborting local retry');
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
const backoffMs = Math.min(2000, 250 * 2 ** attempt);
|
|
375
|
+
log.info({ swapId, attempt, backoffMs }, '[fund] broker not yet armed, retrying /fund');
|
|
376
|
+
await delay(backoffMs);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (!fundSucceeded) {
|
|
380
|
+
log.warn({ swapId }, '[fund] /fund did not succeed after local retries, will re-enter drive loop');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
await delay(POLL_MS, signal);
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (action === 'submit_encsig') {
|
|
387
|
+
onProgress({ stage: 'submit_encsig', message: 'Computing encrypted signature', swapId });
|
|
388
|
+
const protocolParams = pdx.params;
|
|
389
|
+
if (!protocolParams) {
|
|
390
|
+
// Mirror of the fund-branch fix: the sidecar may briefly return
|
|
391
|
+
// action='submit_encsig' before protocolParams is flushed into
|
|
392
|
+
// provider_metadata. Wait and re-poll; the outer TIMEOUT_MS bounds
|
|
393
|
+
// the total wait.
|
|
394
|
+
log.debug({ swapId }, 'Submit-encsig requested but protocolParams not yet populated; waiting');
|
|
395
|
+
onProgress({
|
|
396
|
+
stage: 'submit_encsig',
|
|
397
|
+
message: 'Waiting for protocol params from sidecar',
|
|
398
|
+
swapId,
|
|
399
|
+
});
|
|
400
|
+
await delay(POLL_MS, signal);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (!xmrLockVerified) {
|
|
404
|
+
const xmrVerif = pdx.xmrVerification;
|
|
405
|
+
if (!xmrVerif) {
|
|
406
|
+
onProgress({ stage: 'submit_encsig', message: 'Waiting for XMR lock verification data', swapId });
|
|
407
|
+
await delay(POLL_MS, signal);
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
onProgress({ stage: 'verifying_xmr', message: 'Verifying XMR lock on public node', swapId });
|
|
411
|
+
log.info({ swapId, txHash: xmrVerif.lock_transfer_proof.tx_hash }, 'Verifying XMR lock');
|
|
412
|
+
// AV-C.3: missing S_a_monero is a hard error.
|
|
413
|
+
const sAMon = protocolParams.S_a_monero;
|
|
414
|
+
const sBMon = keystore.keys.S_b_monero;
|
|
415
|
+
if (!sAMon || !sBMon) {
|
|
416
|
+
log.error({ swapId }, 'S_a_monero unavailable, cannot verify XMR lock');
|
|
417
|
+
throw new ProtocolError('E_S_A_MONERO_MISSING', 'S_a_monero not available; refusing to release encsig without XMR lock verification');
|
|
418
|
+
}
|
|
419
|
+
const combinedSpendPub = bytesToHex(Point.fromHex(sAMon).add(Point.fromHex(sBMon)).toBytes());
|
|
420
|
+
const lockResult = await verifyXmrLocked({
|
|
421
|
+
lockTxHash: xmrVerif.lock_transfer_proof.tx_hash,
|
|
422
|
+
viewKeyHex: xmrVerif.combined_view_key,
|
|
423
|
+
spendPubHex: combinedSpendPub,
|
|
424
|
+
expectedAmount: BigInt(xmrVerif.xmr_amount_pico),
|
|
425
|
+
minConfirmations: MIN_XMR_CONFIRMATIONS,
|
|
426
|
+
txKeyHex: xmrVerif.lock_transfer_proof.tx_key ?? undefined,
|
|
427
|
+
monerodNodes: options.monerodNodes,
|
|
428
|
+
logger: log,
|
|
429
|
+
});
|
|
430
|
+
if (!lockResult.verified) {
|
|
431
|
+
if (lockResult.retryable) {
|
|
432
|
+
onProgress({
|
|
433
|
+
stage: 'verifying_xmr',
|
|
434
|
+
message: `Waiting for XMR confirmations: ${lockResult.reason ?? 'pending'}`,
|
|
435
|
+
swapId,
|
|
436
|
+
});
|
|
437
|
+
await delay(POLL_MS, signal);
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
log.error({
|
|
441
|
+
swapId,
|
|
442
|
+
reason: lockResult.reason,
|
|
443
|
+
confirmations: lockResult.confirmations,
|
|
444
|
+
txHash: xmrVerif.lock_transfer_proof.tx_hash,
|
|
445
|
+
txKey: xmrVerif.lock_transfer_proof.tx_key,
|
|
446
|
+
lockAddress: xmrVerif.monero_lock_address,
|
|
447
|
+
expectedPico: xmrVerif.xmr_amount_pico,
|
|
448
|
+
viewKey: xmrVerif.combined_view_key,
|
|
449
|
+
combinedSpendPub,
|
|
450
|
+
}, 'XMR lock verification failed');
|
|
451
|
+
onProgress({
|
|
452
|
+
stage: 'error',
|
|
453
|
+
message: `XMR verification failed: ${lockResult.reason ?? 'unknown'}`,
|
|
454
|
+
swapId,
|
|
455
|
+
});
|
|
456
|
+
throw new VerificationError('E_XMR_LOCK_FAILED', lockResult.reason ?? 'XMR lock verification failed');
|
|
457
|
+
}
|
|
458
|
+
xmrLockVerified = true;
|
|
459
|
+
}
|
|
460
|
+
// The sidecar no longer surfaces `redeemDigestHex` over the wire (see
|
|
461
|
+
// extract.ts). When present we still compare byte-for-byte as defense in
|
|
462
|
+
// depth; when absent we fall back to the local computation alone — the
|
|
463
|
+
// signed PSBT or on-chain TxLock is the authoritative source either way.
|
|
464
|
+
const sidecarDigestHex = pdx.redeemDigestHex;
|
|
465
|
+
// AV-B.2: recompute the digest locally and (optionally) compare byte-for-byte before signing.
|
|
466
|
+
// Fresh-fund path: reuse the signed PSBT we kept in memory.
|
|
467
|
+
// Resume path: the PSBT is gone but TxLock is on-chain; fetch via the
|
|
468
|
+
// injected blockchain provider and recompute from the raw tx. Either
|
|
469
|
+
// path preserves the Fix 1 guarantee.
|
|
470
|
+
let localDigestHex;
|
|
471
|
+
if (lastSignedPsbt) {
|
|
472
|
+
localDigestHex = computeRedeemDigest({
|
|
473
|
+
signedPsbtBase64: lastSignedPsbt,
|
|
474
|
+
protocolParams,
|
|
475
|
+
bPubHex: keystore.keys.B,
|
|
476
|
+
network,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
if (!blockchain) {
|
|
481
|
+
throw new ProtocolError('E_NO_SIGNED_PSBT', 'resume path needs a blockchain provider to reconstruct the redeem digest');
|
|
482
|
+
}
|
|
483
|
+
const depositTxHash = detail.depositTxHash;
|
|
484
|
+
if (!depositTxHash) {
|
|
485
|
+
onProgress({
|
|
486
|
+
stage: 'submit_encsig',
|
|
487
|
+
message: 'Waiting for deposit tx hash from sidecar',
|
|
488
|
+
swapId,
|
|
489
|
+
});
|
|
490
|
+
await delay(POLL_MS, signal);
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
const lockTxRawHex = await blockchain.getTransaction(depositTxHash);
|
|
494
|
+
if (!lockTxRawHex) {
|
|
495
|
+
onProgress({
|
|
496
|
+
stage: 'submit_encsig',
|
|
497
|
+
message: 'Fetching TxLock from blockchain provider',
|
|
498
|
+
swapId,
|
|
499
|
+
});
|
|
500
|
+
await delay(POLL_MS, signal);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
log.info({ swapId, depositTxHash, lockTxLen: lockTxRawHex.length }, 'Reconstructing redeem digest from on-chain TxLock (resume path)');
|
|
504
|
+
localDigestHex = computeRedeemDigestFromTxHex({
|
|
505
|
+
lockTxRawHex,
|
|
506
|
+
protocolParams,
|
|
507
|
+
bPubHex: keystore.keys.B,
|
|
508
|
+
network,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
log.info({
|
|
512
|
+
swapId,
|
|
513
|
+
localDigestHex,
|
|
514
|
+
sidecarDigestHex,
|
|
515
|
+
source: lastSignedPsbt ? 'signed-psbt' : 'on-chain-txlock',
|
|
516
|
+
}, sidecarDigestHex
|
|
517
|
+
? 'Comparing local redeem digest against sidecar claim'
|
|
518
|
+
: 'Sidecar digest unavailable; using local digest only');
|
|
519
|
+
if (sidecarDigestHex && !constantTimeEqualHex(localDigestHex, sidecarDigestHex)) {
|
|
520
|
+
log.error({ swapId, localDigestHex, sidecarDigestHex }, 'Redeem digest mismatch, refusing to release encsig');
|
|
521
|
+
throw new VerificationError('E_REDEEM_DIGEST_MISMATCH', 'local redeem digest does not match sidecar');
|
|
522
|
+
}
|
|
523
|
+
const txRedeemEncsig = encsignDigest(keystore.keys.b, protocolParams.S_a_bitcoin, localDigestHex);
|
|
524
|
+
onProgress({ stage: 'submit_encsig', message: 'Submitting encrypted signature', swapId });
|
|
525
|
+
try {
|
|
526
|
+
await api.executeAction(swapId, { type: 'submit_encsig', tx_redeem_encsig: txRedeemEncsig });
|
|
527
|
+
onProgress({ stage: 'confirming', message: 'Signature submitted, swap progressing', swapId });
|
|
528
|
+
encsigFailures = 0;
|
|
529
|
+
}
|
|
530
|
+
catch (err) {
|
|
531
|
+
encsigFailures++;
|
|
532
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
533
|
+
log.warn({ swapId, error: msg, attempt: encsigFailures }, 'Encsig submission failed');
|
|
534
|
+
if (encsigFailures >= MAX_ENCSIG_FAILURES) {
|
|
535
|
+
throw new ProtocolError('E_ENCSIG_SUBMIT_FAILED', `encsig submission failed ${String(MAX_ENCSIG_FAILURES)} times: ${msg}`);
|
|
536
|
+
}
|
|
537
|
+
onProgress({
|
|
538
|
+
stage: 'confirming',
|
|
539
|
+
message: `Encsig retry ${String(encsigFailures)}/${String(MAX_ENCSIG_FAILURES)}: ${msg}`,
|
|
540
|
+
swapId,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
await delay(POLL_MS, signal);
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (action === 'sweep' || detail.status === 'sending') {
|
|
547
|
+
const pp = pdx.params;
|
|
548
|
+
if (!pp?.S_a_monero) {
|
|
549
|
+
throw new ProtocolError('E_PROTOCOL_PARAMS_MISSING', 'sweep requires S_a_monero in protocol params');
|
|
550
|
+
}
|
|
551
|
+
onProgress({ stage: 'sweeping', message: 'Sweeping XMR', swapId });
|
|
552
|
+
const sweepResult = await sweepMonero(api, {
|
|
553
|
+
swapId,
|
|
554
|
+
s_b: hexToBytes(keystore.keys.s_b),
|
|
555
|
+
receiveAddress: keystore.swap.receiveAddress,
|
|
556
|
+
expectedSAMonero: pp.S_a_monero,
|
|
557
|
+
monerodNodes: options.monerodNodes,
|
|
558
|
+
logger: log,
|
|
559
|
+
});
|
|
560
|
+
onProgress({
|
|
561
|
+
stage: 'complete',
|
|
562
|
+
message: `Sweep complete: ${sweepResult.txHash}`,
|
|
563
|
+
swapId,
|
|
564
|
+
txHash: sweepResult.txHash,
|
|
565
|
+
});
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (action === 'cancel' || action === 'refund') {
|
|
569
|
+
onProgress({
|
|
570
|
+
stage: detail.status,
|
|
571
|
+
message: detail.requiredAction?.message ?? `Action: ${action}`,
|
|
572
|
+
swapId,
|
|
573
|
+
});
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
onProgress({
|
|
577
|
+
stage: detail.status,
|
|
578
|
+
message: detail.requiredAction?.message ?? `Status: ${detail.status}`,
|
|
579
|
+
swapId,
|
|
580
|
+
});
|
|
581
|
+
await delay(POLL_MS, signal);
|
|
582
|
+
}
|
|
583
|
+
throw new ProtocolError('E_DRIVE_TIMEOUT', 'swap timed out waiting for progress');
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Build a funding-proof payload (nonce + signature per UTXO) that the server
|
|
587
|
+
* requires when creating a swap.
|
|
588
|
+
*
|
|
589
|
+
* @internal
|
|
590
|
+
*/
|
|
591
|
+
export function createFundingProof(wallet, deposit) {
|
|
592
|
+
const nonce = randomUuidV4();
|
|
593
|
+
const msgHash = sha256(new TextEncoder().encode(`${nonce}${wallet.address}`));
|
|
594
|
+
const sig = wallet.keyPair.sign(msgHash);
|
|
595
|
+
const signature = bytesToHex(new Uint8Array(sig));
|
|
596
|
+
const utxoList = deposit.utxos ?? [deposit];
|
|
597
|
+
const fundingProof = utxoList.map((u) => ({
|
|
598
|
+
txid: u.txid,
|
|
599
|
+
vout: u.vout,
|
|
600
|
+
value: u.value,
|
|
601
|
+
address: wallet.address,
|
|
602
|
+
nonce,
|
|
603
|
+
signature,
|
|
604
|
+
}));
|
|
605
|
+
return { fundingProof, nonce };
|
|
606
|
+
}
|
|
607
|
+
function extractPresigsEncsig(response) {
|
|
608
|
+
if (typeof response !== 'object' || response === null) {
|
|
609
|
+
return emptyPresigsEncsig();
|
|
610
|
+
}
|
|
611
|
+
const r = response;
|
|
612
|
+
if (typeof r.protocolData !== 'object' || r.protocolData === null) {
|
|
613
|
+
return emptyPresigsEncsig();
|
|
614
|
+
}
|
|
615
|
+
const pd = r.protocolData;
|
|
616
|
+
return {
|
|
617
|
+
txFullRefundEncsig: typeof pd['txFullRefundEncsig'] === 'string' ? pd['txFullRefundEncsig'] : null,
|
|
618
|
+
txPartialRefundEncsig: typeof pd['txPartialRefundEncsig'] === 'string' ? pd['txPartialRefundEncsig'] : null,
|
|
619
|
+
txCancelSig: typeof pd['txCancelSig'] === 'string' ? pd['txCancelSig'] : null,
|
|
620
|
+
txLockTxid: typeof pd['txLockTxid'] === 'string' ? pd['txLockTxid'] : null,
|
|
621
|
+
txLockVout: typeof pd['txLockVout'] === 'number' ? pd['txLockVout'] : null,
|
|
622
|
+
txLockAmountSats: typeof pd['txLockAmountSats'] === 'string' ? pd['txLockAmountSats'] : null,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
function emptyPresigsEncsig() {
|
|
626
|
+
return {
|
|
627
|
+
txFullRefundEncsig: null,
|
|
628
|
+
txPartialRefundEncsig: null,
|
|
629
|
+
txCancelSig: null,
|
|
630
|
+
txLockTxid: null,
|
|
631
|
+
txLockVout: null,
|
|
632
|
+
txLockAmountSats: null,
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
async function maybeWriteSnapshot(input) {
|
|
636
|
+
if (!input.saveProtocolSnapshot)
|
|
637
|
+
return;
|
|
638
|
+
if (input.loadProtocolSnapshot) {
|
|
639
|
+
const existing = await input.loadProtocolSnapshot(input.swapId).catch(() => null);
|
|
640
|
+
if (existing) {
|
|
641
|
+
input.log.debug({ swapId: input.swapId }, 'Snapshot already on disk; skipping');
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
const pp = input.protocolParams;
|
|
646
|
+
const snapshot = buildProtocolSnapshot({
|
|
647
|
+
swapId: input.swapId,
|
|
648
|
+
externalSwapId: input.externalSwapId,
|
|
649
|
+
keystoreId: input.keystoreId,
|
|
650
|
+
network: input.network,
|
|
651
|
+
capturedByVersion: input.clientVersion ?? 'unknown',
|
|
652
|
+
capturedAtEpochMs: Date.now(),
|
|
653
|
+
protocolParams: {
|
|
654
|
+
A: pp.A,
|
|
655
|
+
S_a_bitcoin: pp.S_a_bitcoin,
|
|
656
|
+
S_a_monero: pp.S_a_monero ?? '',
|
|
657
|
+
v_a: pp.v_a ?? null,
|
|
658
|
+
cancel_timelock: pp.cancel_timelock,
|
|
659
|
+
punish_timelock: pp.punish_timelock,
|
|
660
|
+
remaining_refund_timelock: pp.remaining_refund_timelock ?? null,
|
|
661
|
+
redeem_address: pp.redeem_address,
|
|
662
|
+
punish_address: pp.punish_address,
|
|
663
|
+
tx_cancel_fee_sats: String(pp.tx_cancel_fee_sats),
|
|
664
|
+
tx_refund_fee_sats: String(pp.tx_refund_fee_sats),
|
|
665
|
+
tx_redeem_fee_sats: String(pp.tx_redeem_fee_sats),
|
|
666
|
+
tx_punish_fee_sats: String(pp.tx_punish_fee_sats),
|
|
667
|
+
amnesty_amount_sats: pp.amnesty_amount_sats !== undefined ? String(pp.amnesty_amount_sats) : null,
|
|
668
|
+
tx_partial_refund_fee_sats: pp.tx_partial_refund_fee_sats !== undefined && pp.tx_partial_refund_fee_sats !== null
|
|
669
|
+
? String(pp.tx_partial_refund_fee_sats)
|
|
670
|
+
: null,
|
|
671
|
+
tx_reclaim_fee_sats: pp.tx_reclaim_fee_sats !== undefined && pp.tx_reclaim_fee_sats !== null
|
|
672
|
+
? String(pp.tx_reclaim_fee_sats)
|
|
673
|
+
: null,
|
|
674
|
+
tx_withhold_fee_sats: pp.tx_withhold_fee_sats !== undefined && pp.tx_withhold_fee_sats !== null
|
|
675
|
+
? String(pp.tx_withhold_fee_sats)
|
|
676
|
+
: null,
|
|
677
|
+
tx_mercy_fee_sats: pp.tx_mercy_fee_sats !== undefined && pp.tx_mercy_fee_sats !== null
|
|
678
|
+
? String(pp.tx_mercy_fee_sats)
|
|
679
|
+
: null,
|
|
680
|
+
monero_lock_address: pp.monero_lock_address ?? null,
|
|
681
|
+
xmr_amount_pico: pp.xmr_amount_pico ?? '0',
|
|
682
|
+
tx_full_refund_encsig: input.encsigFull,
|
|
683
|
+
tx_partial_refund_encsig: input.encsigPartial,
|
|
684
|
+
tx_cancel_sig: input.aliceTxCancelSig,
|
|
685
|
+
},
|
|
686
|
+
lockTx: {
|
|
687
|
+
txid: input.lockTxid,
|
|
688
|
+
vout: input.lockVout,
|
|
689
|
+
amountSats: input.lockAmountSats,
|
|
690
|
+
unsignedPsbtBase64: input.unsignedPsbtBase64,
|
|
691
|
+
lockAddress: input.lockAddress,
|
|
692
|
+
},
|
|
693
|
+
maker: {
|
|
694
|
+
peerId: input.makerPeerId ?? '',
|
|
695
|
+
multiaddrs: input.makerMultiaddrs ?? [],
|
|
696
|
+
},
|
|
697
|
+
chain: {
|
|
698
|
+
moneroWalletRestoreBlockheight: input.xmrVerification?.monero_restore_height ?? null,
|
|
699
|
+
lockTransferProof: input.xmrVerification?.lock_transfer_proof ?? null,
|
|
700
|
+
sidecarStateAtCapture: input.sidecarStateAtCapture,
|
|
701
|
+
},
|
|
702
|
+
});
|
|
703
|
+
try {
|
|
704
|
+
await input.saveProtocolSnapshot(input.swapId, JSON.stringify(snapshot, null, 2));
|
|
705
|
+
input.log.info({ swapId: input.swapId, externalSwapId: input.externalSwapId }, 'Snapshot persisted');
|
|
706
|
+
}
|
|
707
|
+
catch (err) {
|
|
708
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
709
|
+
input.log.warn({ swapId: input.swapId, error: msg }, 'Failed to persist snapshot');
|
|
710
|
+
throw new ProtocolError('E_SNAPSHOT_WRITE_FAILED', `Refusing to broadcast lock tx without a persisted snapshot: ${msg}`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
//# sourceMappingURL=drive.js.map
|