From 9aafb6c4cc8ac780b226ecc4c04bb8e3b52a136f Mon Sep 17 00:00:00 2001 From: rickalon Date: Wed, 24 Jun 2026 10:34:40 -0400 Subject: [PATCH 01/27] feat(protocols): add SDA (Smart Deposit Address) protocol interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the generic `SdaProtocol` interface for "Smart Deposit Address" providers — services that issue a deposit address, accept a stablecoin (or native token) from a supported source chain, convert it, and deliver a chosen asset (e.g. USDT) to a chosen destination chain and address. - New `src/protocols/sda-protocol.js`: `ISdaProtocol` + abstract `SdaProtocol`, with normalized types (route, deposit address, quote, transfer) and a `getCapabilities()` descriptor so consumers can adapt to provider differences without trial-and-error. - Required core every provider implements: `getSupportedRoutes`, `createDepositAddress`. Everything else (quote, get-address, status, history, recovery, disable) is optional and capability-gated. - Re-exported from `@tetherto/wdk-wallet/protocols`. Part 1 of 3 (interface). Companion PRs: getSdaProtocol wiring in tetherto/wdk and the `sda` module type in tetherto/create-wdk-module. Generated `.d.ts` are produced by `npm run build:types`. --- src/protocols/index.js | 22 ++ src/protocols/sda-protocol.js | 557 ++++++++++++++++++++++++++++++++++ 2 files changed, 579 insertions(+) create mode 100644 src/protocols/sda-protocol.js diff --git a/src/protocols/index.js b/src/protocols/index.js index 4ca8ff6..37ef39c 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -68,6 +68,26 @@ /** @typedef {import('./swidge-protocol.js').SwidgeSupportedToken} SwidgeSupportedToken */ /** @typedef {import('./swidge-protocol.js').SwidgeSupportedTokensOptions} SwidgeSupportedTokensOptions */ +/** @typedef {import('./sda-protocol.js').SdaProtocolConfig} SdaProtocolConfig */ +/** @typedef {import('./sda-protocol.js').SdaCapabilities} SdaCapabilities */ +/** @typedef {import('./sda-protocol.js').SdaRecoveryMode} SdaRecoveryMode */ +/** @typedef {import('./sda-protocol.js').SdaRouteDiscoveryMode} SdaRouteDiscoveryMode */ +/** @typedef {import('./sda-protocol.js').SdaToken} SdaToken */ +/** @typedef {import('./sda-protocol.js').SdaLimits} SdaLimits */ +/** @typedef {import('./sda-protocol.js').SdaRoutesOptions} SdaRoutesOptions */ +/** @typedef {import('./sda-protocol.js').SdaRoute} SdaRoute */ +/** @typedef {import('./sda-protocol.js').SdaQuoteOptions} SdaQuoteOptions */ +/** @typedef {import('./sda-protocol.js').SdaFeeType} SdaFeeType */ +/** @typedef {import('./sda-protocol.js').SdaFee} SdaFee */ +/** @typedef {import('./sda-protocol.js').SdaQuote} SdaQuote */ +/** @typedef {import('./sda-protocol.js').SdaCreateOptions} SdaCreateOptions */ +/** @typedef {import('./sda-protocol.js').SdaDepositAddress} SdaDepositAddress */ +/** @typedef {import('./sda-protocol.js').SdaTransferStatus} SdaTransferStatus */ +/** @typedef {import('./sda-protocol.js').SdaTransfer} SdaTransfer */ +/** @typedef {import('./sda-protocol.js').SdaTransfersOptions} SdaTransfersOptions */ +/** @typedef {import('./sda-protocol.js').SdaRecoveryOptions} SdaRecoveryOptions */ +/** @typedef {import('./sda-protocol.js').SdaRecoveryResult} SdaRecoveryResult */ + export { default as SwapProtocol, ISwapProtocol } from './swap-protocol.js' export { default as BridgeProtocol, IBridgeProtocol } from './bridge-protocol.js' @@ -77,3 +97,5 @@ export { default as LendingProtocol, ILendingProtocol } from './lending-protocol export { default as FiatProtocol, IFiatProtocol } from './fiat-protocol.js' export { default as SwidgeProtocol, ISwidgeProtocol } from './swidge-protocol.js' + +export { default as SdaProtocol, ISdaProtocol } from './sda-protocol.js' diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js new file mode 100644 index 0000000..a7a651b --- /dev/null +++ b/src/protocols/sda-protocol.js @@ -0,0 +1,557 @@ +// Copyright 2024 Tether Operations Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +'use strict' + +import { NotImplementedError } from '../errors.js' + +/** @typedef {import('../wallet-account-read-only.js').IWalletAccountReadOnly} IWalletAccountReadOnly */ + +/** @typedef {import('../wallet-account.js').IWalletAccount} IWalletAccount */ + +/** + * Describes which optional parts of the SDA interface a provider implements, so + * consumers can adapt their UI/flow without trial-and-error. The required core + * (`getSupportedRoutes`, `createDepositAddress`) is always available and + * therefore not listed here. + * + * @typedef {Object} SdaCapabilities + * @property {boolean} quoting - Whether {@link ISdaProtocol#quoteDeposit} is supported. + * @property {boolean} quoteRequired - Whether a quote must be fetched (via {@link ISdaProtocol#quoteDeposit}) and passed to {@link ISdaProtocol#createDepositAddress} via `options.quote` before an address can be issued (e.g., Relay, whose deposit address is bound to a quote/amount). + * @property {boolean} reusableAddresses - Whether issued addresses can receive more than one deposit. + * @property {boolean} multiChainAddress - Whether a single address is valid across several source chains of the same VM family. + * @property {SdaRouteDiscoveryMode} routeDiscovery - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). + * @property {boolean} getAddress - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. + * @property {boolean} transferStatus - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. + * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history) is supported. Push-only providers (webhook-based) report `false`. + * @property {SdaRecoveryMode} recovery - The recovery mechanism the provider exposes via {@link ISdaProtocol#recoverDepositAddress}, or `'none'`. + * @property {boolean} disableAddress - Whether {@link ISdaProtocol#disableDepositAddress} is supported. + * @property {boolean} refund - Whether the provider honours a `refundAddress`. + */ + +/** + * The recovery mechanism a provider exposes for deposits/addresses that were + * not picked up automatically. `reindex` re-scans a known source transaction + * (Orchestra, Relay); `reactivate` re-enables an idle address (Rhino); + * `none` means recovery is not supported. + * + * @typedef {'reindex' | 'reactivate' | 'none'} SdaRecoveryMode + */ + +/** + * How a provider lets routes be discovered. `'full'` means + * {@link ISdaProtocol#getSupportedRoutes} returns every route with no filters; + * `'by-chain-pair'` means a source and destination chain must be supplied. + * + * @typedef {'full' | 'by-chain-pair'} SdaRouteDiscoveryMode + */ + +/** + * Connection and default settings shared by every SDA provider. Concrete + * providers extend this with their own fields (endpoints, credentials, etc.). + * + * @typedef {Object} SdaProtocolConfig + * @property {string} [apiUrl] - Overrides the provider's API base URL. + * @property {string} [apiKey] - Provider API key or credentials, when required. + * @property {string} [defaultRefundAddress] - Refund address used when a call omits one. + */ + +/** + * A normalized, protocol-agnostic token reference. `token` is the identifier + * the provider expects in SDA calls; `address` is the on-chain contract address + * when applicable (absent for native gas tokens). + * + * @typedef {Object} SdaToken + * @property {string} token - The provider-specific token identifier to use in SDA calls. + * @property {string | number} chain - The chain on which the token lives. + * @property {string} symbol - The token symbol (e.g., 'USDC', 'USDT'). + * @property {number} decimals - The number of decimal places for the token's base unit. + * @property {string} [address] - The token contract address, if applicable. + * @property {string} [name] - The token's full name. + */ + +/** + * Per-route deposit limits, denominated in the base unit of the route's input + * token. Either bound may be absent when the provider does not enforce it. + * + * @typedef {Object} SdaLimits + * @property {bigint} [min] - Minimum deposit amount, in the input token's base unit. + * @property {bigint} [max] - Maximum deposit amount, in the input token's base unit. + */ + +/** + * Optional filters for narrowing route discovery. + * + * @typedef {Object} SdaRoutesOptions + * @property {string | number} [sourceChain] - Restrict to routes that accept deposits from this chain. + * @property {string} [sourceToken] - Restrict to routes that accept this input token. + * @property {string | number} [destinationChain] - Restrict to routes that deliver to this chain. + * @property {string} [destinationAsset] - Restrict to routes that deliver this asset. + */ + +/** + * A supported conversion route: one or more source chains and their accepted + * input tokens, the destination chain, and the asset delivered there. + * + * @typedef {Object} SdaRoute + * @property {(string | number)[]} sourceChains - The source chains this route accepts deposits from. A list because some providers issue one address valid across a VM family. + * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. + * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {SdaToken} destinationAsset - The asset delivered to the destination (e.g., USDT). + * @property {SdaLimits} [limits] - Deposit limits for this route. + * @property {boolean} [reusable] - Whether addresses issued for this route can receive more than one deposit. + * @property {number} [estimatedDuration] - Typical end-to-end duration in seconds. + */ + +/** + * Options for fetching a deposit quote. Required up front by providers whose + * addresses are bound to a quote (e.g., Rhino); optional otherwise. + * + * @typedef {Object} SdaQuoteOptions + * @property {string | number} sourceChain - The chain the deposit originates from. + * @property {string} inputToken - The provider identifier of the token being deposited. + * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {string} destinationAsset - The provider identifier of the asset to deliver. + * @property {number | bigint} inputAmount - The amount to deposit, in the input token's base unit. + */ + +/** + * The category of a fee charged by the provider. + * + * @typedef {'network' | 'protocol' | 'affiliate' | 'other'} SdaFeeType + */ + +/** + * A single itemised fee. + * + * @typedef {Object} SdaFee + * @property {SdaFeeType} type - The category of the fee. + * @property {bigint} amount - The fee amount, in the fee token's base unit. + * @property {string} token - The token in which the fee is denominated. + * @property {string | number} [chain] - The chain on which the fee is charged. + * @property {boolean} [included] - Whether the fee is already reflected in the quoted output amount. + * @property {string} [description] - A human-readable description of the fee. + */ + +/** + * A non-binding estimate of the asset delivered for a given deposit. Some + * providers return an `id` that must be passed to {@link ISdaProtocol#createDepositAddress} + * to bind the address to this quote. + * + * @typedef {Object} SdaQuote + * @property {string | number} inputChain - The chain the deposit originates from. + * @property {string} inputToken - The provider identifier of the deposited token. + * @property {bigint} inputAmount - The amount deposited, in the input token's base unit. + * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {string} destinationAsset - The provider identifier of the delivered asset. + * @property {bigint} outputAmount - The estimated amount delivered, in the destination asset's base unit. + * @property {SdaFee[]} fees - Itemised fee breakdown. + * @property {string} [rate] - The effective conversion rate as a string, to avoid precision loss. + * @property {number} [expiry] - Unix timestamp (seconds) at which the quote expires. + * @property {string} [id] - The provider quote identifier, when an address must be bound to this quote. + */ + +/** + * Options for creating a deposit address. + * + * @typedef {Object} SdaCreateOptions + * @property {(string | number)[]} sourceChains - One or more source chains the address should accept deposits from. Providers that issue one address per VM family use the full list; single-chain providers use a one-element list. + * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). + * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. + * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed. + * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). + * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. + */ + +/** + * A deposit address plus its normalized descriptor: where it accepts deposits + * from, what it accepts, where it delivers, and its lifecycle metadata. + * + * @typedef {Object} SdaDepositAddress + * @property {string} address - The deposit address the user sends funds to. + * @property {string} [id] - The provider identifier for this SDA, used for status, recovery and disabling. + * @property {(string | number)[]} sourceChains - The chains this address accepts deposits from. + * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. + * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {SdaToken} destinationAsset - The asset delivered to the destination. + * @property {string} destinationAddress - The resolved address that receives the delivered asset. + * @property {SdaQuote} [quote] - The quote bound to this address, if any. + * @property {SdaLimits} [limits] - Deposit limits for this address. + * @property {boolean} reusable - Whether the address can receive more than one deposit. + * @property {string} [refundAddress] - The refund address bound to this address. + * @property {number} [expiry] - Unix timestamp (seconds) at which the address expires/goes idle, if applicable. + */ + +/** + * The lifecycle status of a deposit/transfer through an SDA. + * + * @typedef {'pending' | 'detected' | 'processing' | 'completed' | 'failed' + * | 'refund-pending' | 'refunded' | 'expired'} SdaTransferStatus + */ + +/** + * A single deposit observed at, and processed through, an SDA. + * + * @typedef {Object} SdaTransfer + * @property {string} id - The provider identifier for this transfer. + * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). + * @property {SdaTransferStatus} status - The current status of the transfer. + * @property {SdaToken} [inputToken] - The token that was deposited. + * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. + * @property {SdaToken} [destinationAsset] - The asset delivered to the destination. + * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. + * @property {string} [sourceTxHash] - The hash of the deposit transaction on the source chain. + * @property {string} [destinationTxHash] - The hash of the delivery transaction on the destination chain. + * @property {SdaFee[]} [fees] - Itemised fees applied to this transfer. + * @property {number} [createdAt] - Unix timestamp (seconds) when the transfer was first observed. + * @property {number} [updatedAt] - Unix timestamp (seconds) when the transfer was last updated. + */ + +/** + * Optional pagination/filtering for transfer history. + * + * @typedef {Object} SdaTransfersOptions + * @property {string | number} [sourceChain] - The source chain of the deposit address, required by providers that key addresses by (address, chain). + * @property {number} [limit] - The maximum number of transfers to return. + * @property {string} [cursor] - An opaque pagination cursor returned by a previous call. + * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. + */ + +/** + * Options for recovering a deposit/address that was not picked up automatically. + * Callers supply whatever the provider's recovery mode needs: `reindex` modes + * use the source transaction; `reactivate` modes use the address or id. + * + * @typedef {Object} SdaRecoveryOptions + * @property {string} [address] - The deposit address to recover/reactivate. + * @property {string} [id] - The provider SDA identifier. + * @property {string} [sourceTxHash] - The deposit transaction to re-index. + * @property {string | number} [sourceChain] - The chain of the deposit transaction. + */ + +/** + * The outcome of a recovery attempt. + * + * @typedef {Object} SdaRecoveryResult + * @property {'reactivated' | 'reindexed' | 'pending' | 'failed'} status - The result of the recovery attempt. + * @property {string} [address] - The address that was recovered/reactivated. + * @property {string} [id] - The provider SDA identifier. + * @property {SdaTransfer} [transfer] - The transfer that was recovered, if one resulted. + * @property {string} [message] - A human-readable description of the outcome. + */ + +/** + * Interface for "Smart Deposit Address" (SDA) providers: services that issue a + * deposit address, accept a stablecoin (or native token) from a supported + * source chain, convert it, and deliver a chosen asset (e.g., USDT) to a chosen + * destination chain and address. + * + * The required core every provider implements is route discovery and address + * creation. Everything else is optional and advertised through + * {@link ISdaProtocol#getCapabilities}. + * + * @interface + */ +export class ISdaProtocol { + /** + * Returns which optional parts of the interface this provider implements. + * + * @returns {SdaCapabilities} The provider's capabilities. + */ + getCapabilities () { + throw new NotImplementedError('getCapabilities()') + } + + /** + * Lists the conversion routes the provider supports: source chains, accepted + * input tokens, destination assets and per-route deposit limits. When + * {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'`, `sourceChain` + * and `destinationChain` must be supplied. + * + * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. + * @returns {Promise} The supported routes. + * @throws {Error} If {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + */ + async getSupportedRoutes (options) { + throw new NotImplementedError('getSupportedRoutes(options)') + } + + /** + * Fetches a non-binding quote for a deposit. Optional: only supported when + * {@link SdaCapabilities#quoting} is `true`, and required up front when + * {@link SdaCapabilities#quoteRequired} is `true`. + * + * @param {SdaQuoteOptions} options - The quote options. + * @returns {Promise} The quoted deposit details. + * @throws {NotImplementedError} If this provider does not support quoting (check `getCapabilities().quoting`). + */ + async quoteDeposit (options) { + throw new NotImplementedError('quoteDeposit(options)') + } + + /** + * Creates deposit addresses for the given route and destination. Returns one + * entry per distinct address: a provider that issues a single address across a + * chain family returns one entry covering all of `sourceChains`, while a + * provider that issues one address per source chain returns one entry each. + * + * @param {SdaCreateOptions} options - The address creation options. + * @returns {Promise} The created deposit addresses, one per distinct address. + */ + async createDepositAddress (options) { + throw new NotImplementedError('createDepositAddress(options)') + } + + /** + * Looks up an existing deposit address by its identifier — the + * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, + * which round-trips any chain context the provider needs. Optional: + * only supported when {@link SdaCapabilities#getAddress} is `true`. + * + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @returns {Promise} The deposit address descriptor. + * @throws {NotImplementedError} If this provider does not support address lookup (check `getCapabilities().getAddress`). + * @throws {Error} If no such address exists. + */ + async getDepositAddress (id) { + throw new NotImplementedError('getDepositAddress(id)') + } + + /** + * Lists the deposits observed at a deposit address. Optional: only supported + * when {@link SdaCapabilities#historyByAddress} is `true`. + * + * @param {string} address - The deposit address to list transfers for. + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). + * @returns {Promise} The transfers for the address. + * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). + */ + async getDepositAddressTransfers (address, options) { + throw new NotImplementedError('getDepositAddressTransfers(address, options)') + } + + /** + * Retrieves the status of a single transfer by its identifier. Optional: + * only supported when {@link SdaCapabilities#transferStatus} is `true`. + * + * @param {string} id - The transfer identifier. + * @returns {Promise} The transfer's current status. + * @throws {NotImplementedError} If this provider does not support transfer-status lookup (check `getCapabilities().transferStatus`). + * @throws {Error} If no such transfer exists. + */ + async getTransferStatus (id) { + throw new NotImplementedError('getTransferStatus(id)') + } + + /** + * Recovers a deposit or address that was not picked up automatically, using + * the provider's recovery mode (see {@link SdaCapabilities#recovery}). + * Optional: only supported when `recovery` is not `'none'`. + * + * @param {SdaRecoveryOptions} options - The recovery options. + * @returns {Promise} The recovery outcome. + * @throws {NotImplementedError} If this provider does not support recovery (check `getCapabilities().recovery`). + */ + async recoverDepositAddress (options) { + throw new NotImplementedError('recoverDepositAddress(options)') + } + + /** + * Disables a deposit address so it no longer accepts deposits. Optional: + * only supported when {@link SdaCapabilities#disableAddress} is `true`. + * + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @returns {Promise} Resolves once the address has been disabled. + * @throws {NotImplementedError} If this provider does not support disabling addresses (check `getCapabilities().disableAddress`). + */ + async disableDepositAddress (id) { + throw new NotImplementedError('disableDepositAddress(id)') + } +} + +/** + * Abstract base class for "Smart Deposit Address" (SDA) providers. Concrete + * providers extend this and implement the provider-specific calls. + * + * @abstract + * @implements {ISdaProtocol} + */ +export default class SdaProtocol { + /** + * Creates a new SDA protocol without binding it to a wallet account. + * + * @overload + * @param {undefined} [account] - The wallet account to use to interact with the protocol. + * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. + */ + + /** + * Creates a new read-only SDA protocol. + * + * @overload + * @param {IWalletAccountReadOnly} account - The wallet account to use to interact with the protocol. + * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. + */ + + /** + * Creates a new SDA protocol. + * + * @overload + * @param {IWalletAccount} account - The wallet account to use to interact with the protocol. + * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. + */ + constructor (account, config = {}) { + /** + * The wallet account to use to interact with the protocol. The account's + * address is the default delivery destination for created addresses. + * + * @protected + * @type {IWalletAccountReadOnly | IWalletAccount | undefined} + */ + this._account = account + + /** + * The SDA protocol configuration. + * + * @protected + * @type {SdaProtocolConfig} + */ + this._config = config + } + + /** + * Returns which optional parts of the interface this provider implements. + * + * @abstract + * @returns {SdaCapabilities} The provider's capabilities. + */ + getCapabilities () { + throw new NotImplementedError('getCapabilities()') + } + + /** + * Lists the conversion routes the provider supports: source chains, accepted + * input tokens, destination assets and per-route deposit limits. When + * {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'`, `sourceChain` + * and `destinationChain` must be supplied. + * + * @abstract + * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. + * @returns {Promise} The supported routes. + * @throws {Error} If {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + */ + async getSupportedRoutes (options) { + throw new NotImplementedError('getSupportedRoutes(options)') + } + + /** + * Fetches a non-binding quote for a deposit. Optional: only supported when + * {@link SdaCapabilities#quoting} is `true`, and required up front when + * {@link SdaCapabilities#quoteRequired} is `true`. + * + * @abstract + * @param {SdaQuoteOptions} options - The quote options. + * @returns {Promise} The quoted deposit details. + * @throws {NotImplementedError} If this provider does not support quoting (check `getCapabilities().quoting`). + */ + async quoteDeposit (options) { + throw new NotImplementedError('quoteDeposit(options)') + } + + /** + * Creates deposit addresses for the given route and destination. Returns one + * entry per distinct address: a provider that issues a single address across a + * chain family returns one entry covering all of `sourceChains`, while a + * provider that issues one address per source chain returns one entry each. + * + * @abstract + * @param {SdaCreateOptions} options - The address creation options. + * @returns {Promise} The created deposit addresses, one per distinct address. + */ + async createDepositAddress (options) { + throw new NotImplementedError('createDepositAddress(options)') + } + + /** + * Looks up an existing deposit address by its identifier — the + * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, + * which round-trips any chain context the provider needs. Optional: + * only supported when {@link SdaCapabilities#getAddress} is `true`. + * + * @abstract + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @returns {Promise} The deposit address descriptor. + * @throws {NotImplementedError} If this provider does not support address lookup (check `getCapabilities().getAddress`). + * @throws {Error} If no such address exists. + */ + async getDepositAddress (id) { + throw new NotImplementedError('getDepositAddress(id)') + } + + /** + * Lists the deposits observed at a deposit address. Optional: only supported + * when {@link SdaCapabilities#historyByAddress} is `true`. + * + * @abstract + * @param {string} address - The deposit address to list transfers for. + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). + * @returns {Promise} The transfers for the address. + * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). + */ + async getDepositAddressTransfers (address, options) { + throw new NotImplementedError('getDepositAddressTransfers(address, options)') + } + + /** + * Retrieves the status of a single transfer by its identifier. Optional: + * only supported when {@link SdaCapabilities#transferStatus} is `true`. + * + * @abstract + * @param {string} id - The transfer identifier. + * @returns {Promise} The transfer's current status. + * @throws {NotImplementedError} If this provider does not support transfer-status lookup (check `getCapabilities().transferStatus`). + * @throws {Error} If no such transfer exists. + */ + async getTransferStatus (id) { + throw new NotImplementedError('getTransferStatus(id)') + } + + /** + * Recovers a deposit or address that was not picked up automatically, using + * the provider's recovery mode (see {@link SdaCapabilities#recovery}). + * Optional: only supported when `recovery` is not `'none'`. + * + * @abstract + * @param {SdaRecoveryOptions} options - The recovery options. + * @returns {Promise} The recovery outcome. + * @throws {NotImplementedError} If this provider does not support recovery (check `getCapabilities().recovery`). + */ + async recoverDepositAddress (options) { + throw new NotImplementedError('recoverDepositAddress(options)') + } + + /** + * Disables a deposit address so it no longer accepts deposits. Optional: + * only supported when {@link SdaCapabilities#disableAddress} is `true`. + * + * @abstract + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @returns {Promise} Resolves once the address has been disabled. + * @throws {NotImplementedError} If this provider does not support disabling addresses (check `getCapabilities().disableAddress`). + */ + async disableDepositAddress (id) { + throw new NotImplementedError('disableDepositAddress(id)') + } +} From 066400b09d9c1c12ac7a214aa1504fceb85526ca Mon Sep 17 00:00:00 2001 From: rickalon Date: Tue, 30 Jun 2026 14:35:56 -0400 Subject: [PATCH 02/27] feat(protocols): add custody model + recipient-keyed history to SDA interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address Candide's review on #46 (their self-custodial Forwarding Address): - `custodyModel` ('self-custodial' | 'trusted-operator') and `clientDerivableAddress` capabilities — distinguish operator-held from on-chain self-custodial deposit addresses. - Clarify that `recovery` is provider-side missed-deposit reprocessing, NOT fund custody; `recovery: 'none'` no longer implies funds are unrecoverable (that's `custodyModel`). - Optional `getTransfersByRecipient` + `historyByRecipient` capability for providers that aggregate status by recipient across many addresses. - Optional `withdrawer` and `salt` on `SdaCreateOptions` (inputs to a deterministic CREATE2 address derivation). Validated against a 4th reference provider (self-custodial, client-derivable). --- src/protocols/index.js | 1 + src/protocols/sda-protocol.js | 65 +++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/protocols/index.js b/src/protocols/index.js index 37ef39c..e34a02f 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -72,6 +72,7 @@ /** @typedef {import('./sda-protocol.js').SdaCapabilities} SdaCapabilities */ /** @typedef {import('./sda-protocol.js').SdaRecoveryMode} SdaRecoveryMode */ /** @typedef {import('./sda-protocol.js').SdaRouteDiscoveryMode} SdaRouteDiscoveryMode */ +/** @typedef {import('./sda-protocol.js').SdaCustodyModel} SdaCustodyModel */ /** @typedef {import('./sda-protocol.js').SdaToken} SdaToken */ /** @typedef {import('./sda-protocol.js').SdaLimits} SdaLimits */ /** @typedef {import('./sda-protocol.js').SdaRoutesOptions} SdaRoutesOptions */ diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index a7a651b..ab584de 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -30,24 +30,40 @@ import { NotImplementedError } from '../errors.js' * @property {boolean} quoteRequired - Whether a quote must be fetched (via {@link ISdaProtocol#quoteDeposit}) and passed to {@link ISdaProtocol#createDepositAddress} via `options.quote` before an address can be issued (e.g., Relay, whose deposit address is bound to a quote/amount). * @property {boolean} reusableAddresses - Whether issued addresses can receive more than one deposit. * @property {boolean} multiChainAddress - Whether a single address is valid across several source chains of the same VM family. + * @property {SdaCustodyModel} custodyModel - Who controls deposited funds while in flight: `'trusted-operator'` (the provider holds the address and funds) or `'self-custodial'` (the address is an on-chain contract with withdrawal rights fixed in code, so funds are recoverable on-chain without the provider). + * @property {boolean} clientDerivableAddress - Whether the deposit address is deterministically derivable and verifiable client-side, before any provider call. * @property {SdaRouteDiscoveryMode} routeDiscovery - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). * @property {boolean} getAddress - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. * @property {boolean} transferStatus - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. - * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history) is supported. Push-only providers (webhook-based) report `false`. - * @property {SdaRecoveryMode} recovery - The recovery mechanism the provider exposes via {@link ISdaProtocol#recoverDepositAddress}, or `'none'`. + * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. + * @property {boolean} historyByRecipient - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. + * @property {SdaRecoveryMode} recovery - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'`, `'reactivate'`, or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). * @property {boolean} disableAddress - Whether {@link ISdaProtocol#disableDepositAddress} is supported. * @property {boolean} refund - Whether the provider honours a `refundAddress`. */ /** - * The recovery mechanism a provider exposes for deposits/addresses that were - * not picked up automatically. `reindex` re-scans a known source transaction - * (Orchestra, Relay); `reactivate` re-enables an idle address (Rhino); - * `none` means recovery is not supported. + * How a provider re-processes a deposit that was not picked up automatically. + * `reindex` re-scans a known source transaction (Orchestra, Relay); `reactivate` + * re-enables an idle/expired address (Rhino); `none` means no such call is + * exposed. This is about provider-side reprocessing of a missed deposit, not + * about fund custody — whether deposited funds are recoverable is governed by + * {@link SdaCapabilities#custodyModel}. * * @typedef {'reindex' | 'reactivate' | 'none'} SdaRecoveryMode */ +/** + * Who controls deposited funds while a deposit is in flight. `'trusted-operator'` + * — the provider holds the deposit address and the funds (recovery means asking + * the provider to reprocess). `'self-custodial'` — the address is an on-chain + * contract whose withdrawal rights are fixed in code (e.g. the recipient can + * withdraw immediately, an optional custodial withdrawer only after a timelock), + * so funds are recoverable on-chain without the provider. + * + * @typedef {'self-custodial' | 'trusted-operator'} SdaCustodyModel + */ + /** * How a provider lets routes be discovered. `'full'` means * {@link ISdaProtocol#getSupportedRoutes} returns every route with no filters; @@ -170,7 +186,9 @@ import { NotImplementedError } from '../errors.js' * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed. + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). + * @property {string} [withdrawer] - For self-custodial / client-derivable providers, the party granted on-chain withdrawal rights (e.g. a custodial recovery wallet, withdrawable after a timelock). Unlike `refundAddress`, this is an input to the address derivation and changes the resulting address. + * @property {string} [salt] - A caller-supplied salt to deterministically derive a specific address (e.g. one address per user vs one per transaction). Only meaningful when {@link SdaCapabilities#clientDerivableAddress} is `true`. * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. */ @@ -342,6 +360,22 @@ export class ISdaProtocol { throw new NotImplementedError('getDepositAddressTransfers(address, options)') } + /** + * Lists transfers aggregated by recipient — every deposit routed to the given + * recipient across all of that recipient's deposit addresses and source + * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} + * is `true`. + * + * @param {string} recipient - The recipient (destination) address to aggregate transfers for. + * @param {string | number} destinationChain - The destination chain the transfers are delivered to. + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. + * @returns {Promise} The transfers routed to the recipient. + * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). + */ + async getTransfersByRecipient (recipient, destinationChain, options) { + throw new NotImplementedError('getTransfersByRecipient(recipient, destinationChain, options)') + } + /** * Retrieves the status of a single transfer by its identifier. Optional: * only supported when {@link SdaCapabilities#transferStatus} is `true`. @@ -514,6 +548,23 @@ export default class SdaProtocol { throw new NotImplementedError('getDepositAddressTransfers(address, options)') } + /** + * Lists transfers aggregated by recipient — every deposit routed to the given + * recipient across all of that recipient's deposit addresses and source + * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} + * is `true`. + * + * @abstract + * @param {string} recipient - The recipient (destination) address to aggregate transfers for. + * @param {string | number} destinationChain - The destination chain the transfers are delivered to. + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. + * @returns {Promise} The transfers routed to the recipient. + * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). + */ + async getTransfersByRecipient (recipient, destinationChain, options) { + throw new NotImplementedError('getTransfersByRecipient(recipient, destinationChain, options)') + } + /** * Retrieves the status of a single transfer by its identifier. Optional: * only supported when {@link SdaCapabilities#transferStatus} is `true`. From 7accd53f09d6060132c7bcc5a701a54c261fc331 Mon Sep 17 00:00:00 2001 From: rickalon Date: Wed, 1 Jul 2026 08:51:25 -0400 Subject: [PATCH 03/27] feat(protocols): add activation model + generic derivation to SDA interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to Candide's review (#46): generalise self-custodial support so the interface does not hard-code any provider's derivation tuple. - New `activation: 'none' | 'required' | 'ttl'` capability (address lifecycle). `recovery` no longer carries 'reactivate' — keep-alive of an expired address is now `activation: 'ttl'`. - Replace the typed `withdrawer`/`salt` create options with a generic, provider-specific `derivation` payload, echoed back on `SdaDepositAddress` so an address can be re-derived / verified / recovered later. - New optional `deriveDepositAddress` (pure client-side derivation) and `renewDepositAddress` (refresh a `ttl` activation). - `createDepositAddress` documented as returning a ready-to-receive address (activating it where the model requires). Validated with a live self-custodial reference (Candide Forwarding Address). --- src/protocols/index.js | 1 + src/protocols/sda-protocol.js | 123 +++++++++++++++++++++++++++------- 2 files changed, 99 insertions(+), 25 deletions(-) diff --git a/src/protocols/index.js b/src/protocols/index.js index e34a02f..045d67b 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -73,6 +73,7 @@ /** @typedef {import('./sda-protocol.js').SdaRecoveryMode} SdaRecoveryMode */ /** @typedef {import('./sda-protocol.js').SdaRouteDiscoveryMode} SdaRouteDiscoveryMode */ /** @typedef {import('./sda-protocol.js').SdaCustodyModel} SdaCustodyModel */ +/** @typedef {import('./sda-protocol.js').SdaActivationModel} SdaActivationModel */ /** @typedef {import('./sda-protocol.js').SdaToken} SdaToken */ /** @typedef {import('./sda-protocol.js').SdaLimits} SdaLimits */ /** @typedef {import('./sda-protocol.js').SdaRoutesOptions} SdaRoutesOptions */ diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index ab584de..8408d8d 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -31,26 +31,37 @@ import { NotImplementedError } from '../errors.js' * @property {boolean} reusableAddresses - Whether issued addresses can receive more than one deposit. * @property {boolean} multiChainAddress - Whether a single address is valid across several source chains of the same VM family. * @property {SdaCustodyModel} custodyModel - Who controls deposited funds while in flight: `'trusted-operator'` (the provider holds the address and funds) or `'self-custodial'` (the address is an on-chain contract with withdrawal rights fixed in code, so funds are recoverable on-chain without the provider). - * @property {boolean} clientDerivableAddress - Whether the deposit address is deterministically derivable and verifiable client-side, before any provider call. + * @property {boolean} clientDerivableAddress - Whether the deposit address is deterministically derivable and verifiable client-side, before any provider call (via {@link ISdaProtocol#deriveDepositAddress}). + * @property {SdaActivationModel} activation - The address activation lifecycle: `'none'` (live as soon as it is created), `'required'` (must be activated before the provider monitors it), or `'ttl'` (activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}). * @property {SdaRouteDiscoveryMode} routeDiscovery - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). * @property {boolean} getAddress - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. * @property {boolean} transferStatus - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. * @property {boolean} historyByRecipient - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. - * @property {SdaRecoveryMode} recovery - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'`, `'reactivate'`, or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). + * @property {SdaRecoveryMode} recovery - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'` or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). Keep-alive of an expired address is the activation lifecycle (`activation: 'ttl'`), not recovery. * @property {boolean} disableAddress - Whether {@link ISdaProtocol#disableDepositAddress} is supported. * @property {boolean} refund - Whether the provider honours a `refundAddress`. */ /** * How a provider re-processes a deposit that was not picked up automatically. - * `reindex` re-scans a known source transaction (Orchestra, Relay); `reactivate` - * re-enables an idle/expired address (Rhino); `none` means no such call is - * exposed. This is about provider-side reprocessing of a missed deposit, not - * about fund custody — whether deposited funds are recoverable is governed by - * {@link SdaCapabilities#custodyModel}. + * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means + * no such call is exposed. This is about provider-side reprocessing of a missed + * deposit, not about fund custody — whether deposited funds are recoverable is + * governed by {@link SdaCapabilities#custodyModel}. Re-enabling an idle/expired + * address is the activation lifecycle ({@link SdaActivationModel} `'ttl'` + + * {@link ISdaProtocol#renewDepositAddress}), not recovery. * - * @typedef {'reindex' | 'reactivate' | 'none'} SdaRecoveryMode + * @typedef {'reindex' | 'none'} SdaRecoveryMode + */ + +/** + * The activation lifecycle of a deposit address. `'none'` — the address is live + * as soon as it is created. `'required'` — the address must be activated (so the + * provider starts monitoring it) before it can receive deposits. `'ttl'` — + * activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}. + * + * @typedef {'none' | 'required' | 'ttl'} SdaActivationModel */ /** @@ -187,8 +198,7 @@ import { NotImplementedError } from '../errors.js' * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). - * @property {string} [withdrawer] - For self-custodial / client-derivable providers, the party granted on-chain withdrawal rights (e.g. a custodial recovery wallet, withdrawable after a timelock). Unlike `refundAddress`, this is an input to the address derivation and changes the resulting address. - * @property {string} [salt] - A caller-supplied salt to deterministically derive a specific address (e.g. one address per user vs one per transaction). Only meaningful when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * @property {Record} [derivation] - Provider-specific inputs a self-custodial / client-derivable provider folds into the deposit-address derivation (e.g. a custodial withdrawer, salt, or execution parameters). The interface passes this through untyped; each provider documents its own shape. Distinct from `refundAddress` (push-refund) — these inputs change the resulting address. * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. */ @@ -209,7 +219,8 @@ import { NotImplementedError } from '../errors.js' * @property {SdaLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address expires/goes idle, if applicable. + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. + * @property {Record} [derivation] - The provider-specific derivation inputs this address was created from (echoed back), so a `clientDerivableAddress` provider can re-derive, verify or recover the address later. */ /** @@ -248,12 +259,12 @@ import { NotImplementedError } from '../errors.js' */ /** - * Options for recovering a deposit/address that was not picked up automatically. - * Callers supply whatever the provider's recovery mode needs: `reindex` modes - * use the source transaction; `reactivate` modes use the address or id. + * Options for re-processing a deposit that was not picked up automatically + * (`reindex`). Callers supply whatever the provider needs — typically the source + * transaction, or the deposit address / SDA id. * * @typedef {Object} SdaRecoveryOptions - * @property {string} [address] - The deposit address to recover/reactivate. + * @property {string} [address] - The deposit address to reindex. * @property {string} [id] - The provider SDA identifier. * @property {string} [sourceTxHash] - The deposit transaction to re-index. * @property {string | number} [sourceChain] - The chain of the deposit transaction. @@ -263,8 +274,8 @@ import { NotImplementedError } from '../errors.js' * The outcome of a recovery attempt. * * @typedef {Object} SdaRecoveryResult - * @property {'reactivated' | 'reindexed' | 'pending' | 'failed'} status - The result of the recovery attempt. - * @property {string} [address] - The address that was recovered/reactivated. + * @property {'reindexed' | 'pending' | 'failed'} status - The result of the reindex attempt. + * @property {string} [address] - The address that was reindexed. * @property {string} [id] - The provider SDA identifier. * @property {SdaTransfer} [transfer] - The transfer that was recovered, if one resulted. * @property {string} [message] - A human-readable description of the outcome. @@ -320,10 +331,13 @@ export class ISdaProtocol { } /** - * Creates deposit addresses for the given route and destination. Returns one - * entry per distinct address: a provider that issues a single address across a - * chain family returns one entry covering all of `sourceChains`, while a - * provider that issues one address per source chain returns one entry each. + * Creates deposit addresses for the given route and destination, ready to + * receive per the provider's {@link SdaCapabilities#activation} model (for a + * `'required'` / `'ttl'` provider this also activates the address so it is + * monitored). Returns one entry per distinct address: a provider that issues a + * single address across a chain family returns one entry covering all of + * `sourceChains`, while a provider that issues one address per source chain + * returns one entry each. * * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. @@ -332,6 +346,20 @@ export class ISdaProtocol { throw new NotImplementedError('createDepositAddress(options)') } + /** + * Derives a deposit address client-side, without any provider call and + * without activating or monitoring it — used to verify (derive + compare) or + * recover an address for a self-custodial provider. Optional: only supported + * when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * + * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; the provider-specific `derivation` inputs are folded into the result. + * @returns {Promise} The derived deposit address. + * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). + */ + async deriveDepositAddress (options) { + throw new NotImplementedError('deriveDepositAddress(options)') + } + /** * Looks up an existing deposit address by its identifier — the * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, @@ -347,6 +375,19 @@ export class ISdaProtocol { throw new NotImplementedError('getDepositAddress(id)') } + /** + * Refreshes the activation of a deposit address so the provider keeps + * monitoring it. Optional: only relevant when {@link SdaCapabilities#activation} + * is `'ttl'`. + * + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. + * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). + * @throws {NotImplementedError} If this provider does not use activation TTLs (check `getCapabilities().activation`). + */ + async renewDepositAddress (id) { + throw new NotImplementedError('renewDepositAddress(id)') + } + /** * Lists the deposits observed at a deposit address. Optional: only supported * when {@link SdaCapabilities#historyByAddress} is `true`. @@ -505,10 +546,13 @@ export default class SdaProtocol { } /** - * Creates deposit addresses for the given route and destination. Returns one - * entry per distinct address: a provider that issues a single address across a - * chain family returns one entry covering all of `sourceChains`, while a - * provider that issues one address per source chain returns one entry each. + * Creates deposit addresses for the given route and destination, ready to + * receive per the provider's {@link SdaCapabilities#activation} model (for a + * `'required'` / `'ttl'` provider this also activates the address so it is + * monitored). Returns one entry per distinct address: a provider that issues a + * single address across a chain family returns one entry covering all of + * `sourceChains`, while a provider that issues one address per source chain + * returns one entry each. * * @abstract * @param {SdaCreateOptions} options - The address creation options. @@ -518,6 +562,21 @@ export default class SdaProtocol { throw new NotImplementedError('createDepositAddress(options)') } + /** + * Derives a deposit address client-side, without any provider call and + * without activating or monitoring it — used to verify (derive + compare) or + * recover an address for a self-custodial provider. Optional: only supported + * when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * + * @abstract + * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; the provider-specific `derivation` inputs are folded into the result. + * @returns {Promise} The derived deposit address. + * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). + */ + async deriveDepositAddress (options) { + throw new NotImplementedError('deriveDepositAddress(options)') + } + /** * Looks up an existing deposit address by its identifier — the * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, @@ -534,6 +593,20 @@ export default class SdaProtocol { throw new NotImplementedError('getDepositAddress(id)') } + /** + * Refreshes the activation of a deposit address so the provider keeps + * monitoring it. Optional: only relevant when {@link SdaCapabilities#activation} + * is `'ttl'`. + * + * @abstract + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. + * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). + * @throws {NotImplementedError} If this provider does not use activation TTLs (check `getCapabilities().activation`). + */ + async renewDepositAddress (id) { + throw new NotImplementedError('renewDepositAddress(id)') + } + /** * Lists the deposits observed at a deposit address. Optional: only supported * when {@link SdaCapabilities#historyByAddress} is `true`. From d6f7658ec082ad78dfd55d9590328e03d04fc1d0 Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 2 Jul 2026 09:37:12 -0400 Subject: [PATCH 04/27] refactor(protocols): add Blockchain type alias for SDA chain fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `string | number` on every chain field with a shared `Blockchain` type alias (PR #46 review). Also commits the generated SDA `.d.ts` (previously missing) — unrelated pre-existing type drift in other protocols' declarations is left untouched. --- src/protocols/index.js | 1 + src/protocols/sda-protocol.js | 43 +- types/src/protocols/index.d.ts | 23 + types/src/protocols/sda-protocol.d.ts | 1129 +++++++++++++++++++++++++ 4 files changed, 1178 insertions(+), 18 deletions(-) create mode 100644 types/src/protocols/sda-protocol.d.ts diff --git a/src/protocols/index.js b/src/protocols/index.js index 045d67b..91844fa 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -74,6 +74,7 @@ /** @typedef {import('./sda-protocol.js').SdaRouteDiscoveryMode} SdaRouteDiscoveryMode */ /** @typedef {import('./sda-protocol.js').SdaCustodyModel} SdaCustodyModel */ /** @typedef {import('./sda-protocol.js').SdaActivationModel} SdaActivationModel */ +/** @typedef {import('./sda-protocol.js').Blockchain} Blockchain */ /** @typedef {import('./sda-protocol.js').SdaToken} SdaToken */ /** @typedef {import('./sda-protocol.js').SdaLimits} SdaLimits */ /** @typedef {import('./sda-protocol.js').SdaRoutesOptions} SdaRoutesOptions */ diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 8408d8d..3cc289c 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -19,6 +19,13 @@ import { NotImplementedError } from '../errors.js' /** @typedef {import('../wallet-account.js').IWalletAccount} IWalletAccount */ +/** + * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific + * chain name (e.g. `'ethereum'`). + * + * @typedef {string | number} Blockchain + */ + /** * Describes which optional parts of the SDA interface a provider implements, so * consumers can adapt their UI/flow without trial-and-error. The required core @@ -100,7 +107,7 @@ import { NotImplementedError } from '../errors.js' * * @typedef {Object} SdaToken * @property {string} token - The provider-specific token identifier to use in SDA calls. - * @property {string | number} chain - The chain on which the token lives. + * @property {Blockchain} chain - The chain on which the token lives. * @property {string} symbol - The token symbol (e.g., 'USDC', 'USDT'). * @property {number} decimals - The number of decimal places for the token's base unit. * @property {string} [address] - The token contract address, if applicable. @@ -120,9 +127,9 @@ import { NotImplementedError } from '../errors.js' * Optional filters for narrowing route discovery. * * @typedef {Object} SdaRoutesOptions - * @property {string | number} [sourceChain] - Restrict to routes that accept deposits from this chain. + * @property {Blockchain} [sourceChain] - Restrict to routes that accept deposits from this chain. * @property {string} [sourceToken] - Restrict to routes that accept this input token. - * @property {string | number} [destinationChain] - Restrict to routes that deliver to this chain. + * @property {Blockchain} [destinationChain] - Restrict to routes that deliver to this chain. * @property {string} [destinationAsset] - Restrict to routes that deliver this asset. */ @@ -131,9 +138,9 @@ import { NotImplementedError } from '../errors.js' * input tokens, the destination chain, and the asset delivered there. * * @typedef {Object} SdaRoute - * @property {(string | number)[]} sourceChains - The source chains this route accepts deposits from. A list because some providers issue one address valid across a VM family. + * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some providers issue one address valid across a VM family. * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. - * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {SdaToken} destinationAsset - The asset delivered to the destination (e.g., USDT). * @property {SdaLimits} [limits] - Deposit limits for this route. * @property {boolean} [reusable] - Whether addresses issued for this route can receive more than one deposit. @@ -145,9 +152,9 @@ import { NotImplementedError } from '../errors.js' * addresses are bound to a quote (e.g., Rhino); optional otherwise. * * @typedef {Object} SdaQuoteOptions - * @property {string | number} sourceChain - The chain the deposit originates from. + * @property {Blockchain} sourceChain - The chain the deposit originates from. * @property {string} inputToken - The provider identifier of the token being deposited. - * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {string} destinationAsset - The provider identifier of the asset to deliver. * @property {number | bigint} inputAmount - The amount to deposit, in the input token's base unit. */ @@ -165,7 +172,7 @@ import { NotImplementedError } from '../errors.js' * @property {SdaFeeType} type - The category of the fee. * @property {bigint} amount - The fee amount, in the fee token's base unit. * @property {string} token - The token in which the fee is denominated. - * @property {string | number} [chain] - The chain on which the fee is charged. + * @property {Blockchain} [chain] - The chain on which the fee is charged. * @property {boolean} [included] - Whether the fee is already reflected in the quoted output amount. * @property {string} [description] - A human-readable description of the fee. */ @@ -176,10 +183,10 @@ import { NotImplementedError } from '../errors.js' * to bind the address to this quote. * * @typedef {Object} SdaQuote - * @property {string | number} inputChain - The chain the deposit originates from. + * @property {Blockchain} inputChain - The chain the deposit originates from. * @property {string} inputToken - The provider identifier of the deposited token. * @property {bigint} inputAmount - The amount deposited, in the input token's base unit. - * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {string} destinationAsset - The provider identifier of the delivered asset. * @property {bigint} outputAmount - The estimated amount delivered, in the destination asset's base unit. * @property {SdaFee[]} fees - Itemised fee breakdown. @@ -192,8 +199,8 @@ import { NotImplementedError } from '../errors.js' * Options for creating a deposit address. * * @typedef {Object} SdaCreateOptions - * @property {(string | number)[]} sourceChains - One or more source chains the address should accept deposits from. Providers that issue one address per VM family use the full list; single-chain providers use a one-element list. - * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Providers that issue one address per VM family use the full list; single-chain providers use a one-element list. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. @@ -210,9 +217,9 @@ import { NotImplementedError } from '../errors.js' * @typedef {Object} SdaDepositAddress * @property {string} address - The deposit address the user sends funds to. * @property {string} [id] - The provider identifier for this SDA, used for status, recovery and disabling. - * @property {(string | number)[]} sourceChains - The chains this address accepts deposits from. + * @property {Blockchain[]} sourceChains - The chains this address accepts deposits from. * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. - * @property {string | number} destinationChain - The chain the converted asset is delivered to. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {SdaToken} destinationAsset - The asset delivered to the destination. * @property {string} destinationAddress - The resolved address that receives the delivered asset. * @property {SdaQuote} [quote] - The quote bound to this address, if any. @@ -252,7 +259,7 @@ import { NotImplementedError } from '../errors.js' * Optional pagination/filtering for transfer history. * * @typedef {Object} SdaTransfersOptions - * @property {string | number} [sourceChain] - The source chain of the deposit address, required by providers that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by providers that key addresses by (address, chain). * @property {number} [limit] - The maximum number of transfers to return. * @property {string} [cursor] - An opaque pagination cursor returned by a previous call. * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. @@ -267,7 +274,7 @@ import { NotImplementedError } from '../errors.js' * @property {string} [address] - The deposit address to reindex. * @property {string} [id] - The provider SDA identifier. * @property {string} [sourceTxHash] - The deposit transaction to re-index. - * @property {string | number} [sourceChain] - The chain of the deposit transaction. + * @property {Blockchain} [sourceChain] - The chain of the deposit transaction. */ /** @@ -408,7 +415,7 @@ export class ISdaProtocol { * is `true`. * * @param {string} recipient - The recipient (destination) address to aggregate transfers for. - * @param {string | number} destinationChain - The destination chain the transfers are delivered to. + * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). @@ -629,7 +636,7 @@ export default class SdaProtocol { * * @abstract * @param {string} recipient - The recipient (destination) address to aggregate transfers for. - * @param {string | number} destinationChain - The destination chain the transfers are delivered to. + * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). diff --git a/types/src/protocols/index.d.ts b/types/src/protocols/index.d.ts index e8647b3..e3a2ceb 100644 --- a/types/src/protocols/index.d.ts +++ b/types/src/protocols/index.d.ts @@ -44,8 +44,31 @@ export type SwidgeStatusResult = import("./swidge-protocol.js").SwidgeStatusResu export type SwidgeSupportedChain = import("./swidge-protocol.js").SwidgeSupportedChain; export type SwidgeSupportedToken = import("./swidge-protocol.js").SwidgeSupportedToken; export type SwidgeSupportedTokensOptions = import("./swidge-protocol.js").SwidgeSupportedTokensOptions; +export type SdaProtocolConfig = import("./sda-protocol.js").SdaProtocolConfig; +export type SdaCapabilities = import("./sda-protocol.js").SdaCapabilities; +export type SdaRecoveryMode = import("./sda-protocol.js").SdaRecoveryMode; +export type SdaRouteDiscoveryMode = import("./sda-protocol.js").SdaRouteDiscoveryMode; +export type SdaCustodyModel = import("./sda-protocol.js").SdaCustodyModel; +export type SdaActivationModel = import("./sda-protocol.js").SdaActivationModel; +export type Blockchain = import("./sda-protocol.js").Blockchain; +export type SdaToken = import("./sda-protocol.js").SdaToken; +export type SdaLimits = import("./sda-protocol.js").SdaLimits; +export type SdaRoutesOptions = import("./sda-protocol.js").SdaRoutesOptions; +export type SdaRoute = import("./sda-protocol.js").SdaRoute; +export type SdaQuoteOptions = import("./sda-protocol.js").SdaQuoteOptions; +export type SdaFeeType = import("./sda-protocol.js").SdaFeeType; +export type SdaFee = import("./sda-protocol.js").SdaFee; +export type SdaQuote = import("./sda-protocol.js").SdaQuote; +export type SdaCreateOptions = import("./sda-protocol.js").SdaCreateOptions; +export type SdaDepositAddress = import("./sda-protocol.js").SdaDepositAddress; +export type SdaTransferStatus = import("./sda-protocol.js").SdaTransferStatus; +export type SdaTransfer = import("./sda-protocol.js").SdaTransfer; +export type SdaTransfersOptions = import("./sda-protocol.js").SdaTransfersOptions; +export type SdaRecoveryOptions = import("./sda-protocol.js").SdaRecoveryOptions; +export type SdaRecoveryResult = import("./sda-protocol.js").SdaRecoveryResult; export { default as SwapProtocol, ISwapProtocol } from "./swap-protocol.js"; export { default as BridgeProtocol, IBridgeProtocol } from "./bridge-protocol.js"; export { default as LendingProtocol, ILendingProtocol } from "./lending-protocol.js"; export { default as FiatProtocol, IFiatProtocol } from "./fiat-protocol.js"; export { default as SwidgeProtocol, ISwidgeProtocol } from "./swidge-protocol.js"; +export { default as SdaProtocol, ISdaProtocol } from "./sda-protocol.js"; diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts new file mode 100644 index 0000000..4ae4a3f --- /dev/null +++ b/types/src/protocols/sda-protocol.d.ts @@ -0,0 +1,1129 @@ +/** @typedef {import('../wallet-account-read-only.js').IWalletAccountReadOnly} IWalletAccountReadOnly */ +/** @typedef {import('../wallet-account.js').IWalletAccount} IWalletAccount */ +/** + * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific + * chain name (e.g. `'ethereum'`). + * + * @typedef {string | number} Blockchain + */ +/** + * Describes which optional parts of the SDA interface a provider implements, so + * consumers can adapt their UI/flow without trial-and-error. The required core + * (`getSupportedRoutes`, `createDepositAddress`) is always available and + * therefore not listed here. + * + * @typedef {Object} SdaCapabilities + * @property {boolean} quoting - Whether {@link ISdaProtocol#quoteDeposit} is supported. + * @property {boolean} quoteRequired - Whether a quote must be fetched (via {@link ISdaProtocol#quoteDeposit}) and passed to {@link ISdaProtocol#createDepositAddress} via `options.quote` before an address can be issued (e.g., Relay, whose deposit address is bound to a quote/amount). + * @property {boolean} reusableAddresses - Whether issued addresses can receive more than one deposit. + * @property {boolean} multiChainAddress - Whether a single address is valid across several source chains of the same VM family. + * @property {SdaCustodyModel} custodyModel - Who controls deposited funds while in flight: `'trusted-operator'` (the provider holds the address and funds) or `'self-custodial'` (the address is an on-chain contract with withdrawal rights fixed in code, so funds are recoverable on-chain without the provider). + * @property {boolean} clientDerivableAddress - Whether the deposit address is deterministically derivable and verifiable client-side, before any provider call (via {@link ISdaProtocol#deriveDepositAddress}). + * @property {SdaActivationModel} activation - The address activation lifecycle: `'none'` (live as soon as it is created), `'required'` (must be activated before the provider monitors it), or `'ttl'` (activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}). + * @property {SdaRouteDiscoveryMode} routeDiscovery - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). + * @property {boolean} getAddress - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. + * @property {boolean} transferStatus - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. + * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. + * @property {boolean} historyByRecipient - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. + * @property {SdaRecoveryMode} recovery - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'` or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). Keep-alive of an expired address is the activation lifecycle (`activation: 'ttl'`), not recovery. + * @property {boolean} disableAddress - Whether {@link ISdaProtocol#disableDepositAddress} is supported. + * @property {boolean} refund - Whether the provider honours a `refundAddress`. + */ +/** + * How a provider re-processes a deposit that was not picked up automatically. + * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means + * no such call is exposed. This is about provider-side reprocessing of a missed + * deposit, not about fund custody — whether deposited funds are recoverable is + * governed by {@link SdaCapabilities#custodyModel}. Re-enabling an idle/expired + * address is the activation lifecycle ({@link SdaActivationModel} `'ttl'` + + * {@link ISdaProtocol#renewDepositAddress}), not recovery. + * + * @typedef {'reindex' | 'none'} SdaRecoveryMode + */ +/** + * The activation lifecycle of a deposit address. `'none'` — the address is live + * as soon as it is created. `'required'` — the address must be activated (so the + * provider starts monitoring it) before it can receive deposits. `'ttl'` — + * activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}. + * + * @typedef {'none' | 'required' | 'ttl'} SdaActivationModel + */ +/** + * Who controls deposited funds while a deposit is in flight. `'trusted-operator'` + * — the provider holds the deposit address and the funds (recovery means asking + * the provider to reprocess). `'self-custodial'` — the address is an on-chain + * contract whose withdrawal rights are fixed in code (e.g. the recipient can + * withdraw immediately, an optional custodial withdrawer only after a timelock), + * so funds are recoverable on-chain without the provider. + * + * @typedef {'self-custodial' | 'trusted-operator'} SdaCustodyModel + */ +/** + * How a provider lets routes be discovered. `'full'` means + * {@link ISdaProtocol#getSupportedRoutes} returns every route with no filters; + * `'by-chain-pair'` means a source and destination chain must be supplied. + * + * @typedef {'full' | 'by-chain-pair'} SdaRouteDiscoveryMode + */ +/** + * Connection and default settings shared by every SDA provider. Concrete + * providers extend this with their own fields (endpoints, credentials, etc.). + * + * @typedef {Object} SdaProtocolConfig + * @property {string} [apiUrl] - Overrides the provider's API base URL. + * @property {string} [apiKey] - Provider API key or credentials, when required. + * @property {string} [defaultRefundAddress] - Refund address used when a call omits one. + */ +/** + * A normalized, protocol-agnostic token reference. `token` is the identifier + * the provider expects in SDA calls; `address` is the on-chain contract address + * when applicable (absent for native gas tokens). + * + * @typedef {Object} SdaToken + * @property {string} token - The provider-specific token identifier to use in SDA calls. + * @property {Blockchain} chain - The chain on which the token lives. + * @property {string} symbol - The token symbol (e.g., 'USDC', 'USDT'). + * @property {number} decimals - The number of decimal places for the token's base unit. + * @property {string} [address] - The token contract address, if applicable. + * @property {string} [name] - The token's full name. + */ +/** + * Per-route deposit limits, denominated in the base unit of the route's input + * token. Either bound may be absent when the provider does not enforce it. + * + * @typedef {Object} SdaLimits + * @property {bigint} [min] - Minimum deposit amount, in the input token's base unit. + * @property {bigint} [max] - Maximum deposit amount, in the input token's base unit. + */ +/** + * Optional filters for narrowing route discovery. + * + * @typedef {Object} SdaRoutesOptions + * @property {Blockchain} [sourceChain] - Restrict to routes that accept deposits from this chain. + * @property {string} [sourceToken] - Restrict to routes that accept this input token. + * @property {Blockchain} [destinationChain] - Restrict to routes that deliver to this chain. + * @property {string} [destinationAsset] - Restrict to routes that deliver this asset. + */ +/** + * A supported conversion route: one or more source chains and their accepted + * input tokens, the destination chain, and the asset delivered there. + * + * @typedef {Object} SdaRoute + * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some providers issue one address valid across a VM family. + * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. + * @property {SdaToken} destinationAsset - The asset delivered to the destination (e.g., USDT). + * @property {SdaLimits} [limits] - Deposit limits for this route. + * @property {boolean} [reusable] - Whether addresses issued for this route can receive more than one deposit. + * @property {number} [estimatedDuration] - Typical end-to-end duration in seconds. + */ +/** + * Options for fetching a deposit quote. Required up front by providers whose + * addresses are bound to a quote (e.g., Rhino); optional otherwise. + * + * @typedef {Object} SdaQuoteOptions + * @property {Blockchain} sourceChain - The chain the deposit originates from. + * @property {string} inputToken - The provider identifier of the token being deposited. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. + * @property {string} destinationAsset - The provider identifier of the asset to deliver. + * @property {number | bigint} inputAmount - The amount to deposit, in the input token's base unit. + */ +/** + * The category of a fee charged by the provider. + * + * @typedef {'network' | 'protocol' | 'affiliate' | 'other'} SdaFeeType + */ +/** + * A single itemised fee. + * + * @typedef {Object} SdaFee + * @property {SdaFeeType} type - The category of the fee. + * @property {bigint} amount - The fee amount, in the fee token's base unit. + * @property {string} token - The token in which the fee is denominated. + * @property {Blockchain} [chain] - The chain on which the fee is charged. + * @property {boolean} [included] - Whether the fee is already reflected in the quoted output amount. + * @property {string} [description] - A human-readable description of the fee. + */ +/** + * A non-binding estimate of the asset delivered for a given deposit. Some + * providers return an `id` that must be passed to {@link ISdaProtocol#createDepositAddress} + * to bind the address to this quote. + * + * @typedef {Object} SdaQuote + * @property {Blockchain} inputChain - The chain the deposit originates from. + * @property {string} inputToken - The provider identifier of the deposited token. + * @property {bigint} inputAmount - The amount deposited, in the input token's base unit. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. + * @property {string} destinationAsset - The provider identifier of the delivered asset. + * @property {bigint} outputAmount - The estimated amount delivered, in the destination asset's base unit. + * @property {SdaFee[]} fees - Itemised fee breakdown. + * @property {string} [rate] - The effective conversion rate as a string, to avoid precision loss. + * @property {number} [expiry] - Unix timestamp (seconds) at which the quote expires. + * @property {string} [id] - The provider quote identifier, when an address must be bound to this quote. + */ +/** + * Options for creating a deposit address. + * + * @typedef {Object} SdaCreateOptions + * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Providers that issue one address per VM family use the full list; single-chain providers use a one-element list. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. + * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). + * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. + * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). + * @property {Record} [derivation] - Provider-specific inputs a self-custodial / client-derivable provider folds into the deposit-address derivation (e.g. a custodial withdrawer, salt, or execution parameters). The interface passes this through untyped; each provider documents its own shape. Distinct from `refundAddress` (push-refund) — these inputs change the resulting address. + * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). + * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. + */ +/** + * A deposit address plus its normalized descriptor: where it accepts deposits + * from, what it accepts, where it delivers, and its lifecycle metadata. + * + * @typedef {Object} SdaDepositAddress + * @property {string} address - The deposit address the user sends funds to. + * @property {string} [id] - The provider identifier for this SDA, used for status, recovery and disabling. + * @property {Blockchain[]} sourceChains - The chains this address accepts deposits from. + * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. + * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. + * @property {SdaToken} destinationAsset - The asset delivered to the destination. + * @property {string} destinationAddress - The resolved address that receives the delivered asset. + * @property {SdaQuote} [quote] - The quote bound to this address, if any. + * @property {SdaLimits} [limits] - Deposit limits for this address. + * @property {boolean} reusable - Whether the address can receive more than one deposit. + * @property {string} [refundAddress] - The refund address bound to this address. + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. + * @property {Record} [derivation] - The provider-specific derivation inputs this address was created from (echoed back), so a `clientDerivableAddress` provider can re-derive, verify or recover the address later. + */ +/** + * The lifecycle status of a deposit/transfer through an SDA. + * + * @typedef {'pending' | 'detected' | 'processing' | 'completed' | 'failed' + * | 'refund-pending' | 'refunded' | 'expired'} SdaTransferStatus + */ +/** + * A single deposit observed at, and processed through, an SDA. + * + * @typedef {Object} SdaTransfer + * @property {string} id - The provider identifier for this transfer. + * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). + * @property {SdaTransferStatus} status - The current status of the transfer. + * @property {SdaToken} [inputToken] - The token that was deposited. + * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. + * @property {SdaToken} [destinationAsset] - The asset delivered to the destination. + * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. + * @property {string} [sourceTxHash] - The hash of the deposit transaction on the source chain. + * @property {string} [destinationTxHash] - The hash of the delivery transaction on the destination chain. + * @property {SdaFee[]} [fees] - Itemised fees applied to this transfer. + * @property {number} [createdAt] - Unix timestamp (seconds) when the transfer was first observed. + * @property {number} [updatedAt] - Unix timestamp (seconds) when the transfer was last updated. + */ +/** + * Optional pagination/filtering for transfer history. + * + * @typedef {Object} SdaTransfersOptions + * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by providers that key addresses by (address, chain). + * @property {number} [limit] - The maximum number of transfers to return. + * @property {string} [cursor] - An opaque pagination cursor returned by a previous call. + * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. + */ +/** + * Options for re-processing a deposit that was not picked up automatically + * (`reindex`). Callers supply whatever the provider needs — typically the source + * transaction, or the deposit address / SDA id. + * + * @typedef {Object} SdaRecoveryOptions + * @property {string} [address] - The deposit address to reindex. + * @property {string} [id] - The provider SDA identifier. + * @property {string} [sourceTxHash] - The deposit transaction to re-index. + * @property {Blockchain} [sourceChain] - The chain of the deposit transaction. + */ +/** + * The outcome of a recovery attempt. + * + * @typedef {Object} SdaRecoveryResult + * @property {'reindexed' | 'pending' | 'failed'} status - The result of the reindex attempt. + * @property {string} [address] - The address that was reindexed. + * @property {string} [id] - The provider SDA identifier. + * @property {SdaTransfer} [transfer] - The transfer that was recovered, if one resulted. + * @property {string} [message] - A human-readable description of the outcome. + */ +/** + * Interface for "Smart Deposit Address" (SDA) providers: services that issue a + * deposit address, accept a stablecoin (or native token) from a supported + * source chain, convert it, and deliver a chosen asset (e.g., USDT) to a chosen + * destination chain and address. + * + * The required core every provider implements is route discovery and address + * creation. Everything else is optional and advertised through + * {@link ISdaProtocol#getCapabilities}. + * + * @interface + */ +export class ISdaProtocol { + /** + * Returns which optional parts of the interface this provider implements. + * + * @returns {SdaCapabilities} The provider's capabilities. + */ + getCapabilities(): SdaCapabilities; + /** + * Lists the conversion routes the provider supports: source chains, accepted + * input tokens, destination assets and per-route deposit limits. When + * {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'`, `sourceChain` + * and `destinationChain` must be supplied. + * + * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. + * @returns {Promise} The supported routes. + * @throws {Error} If {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + */ + getSupportedRoutes(options?: SdaRoutesOptions): Promise; + /** + * Fetches a non-binding quote for a deposit. Optional: only supported when + * {@link SdaCapabilities#quoting} is `true`, and required up front when + * {@link SdaCapabilities#quoteRequired} is `true`. + * + * @param {SdaQuoteOptions} options - The quote options. + * @returns {Promise} The quoted deposit details. + * @throws {NotImplementedError} If this provider does not support quoting (check `getCapabilities().quoting`). + */ + quoteDeposit(options: SdaQuoteOptions): Promise; + /** + * Creates deposit addresses for the given route and destination, ready to + * receive per the provider's {@link SdaCapabilities#activation} model (for a + * `'required'` / `'ttl'` provider this also activates the address so it is + * monitored). Returns one entry per distinct address: a provider that issues a + * single address across a chain family returns one entry covering all of + * `sourceChains`, while a provider that issues one address per source chain + * returns one entry each. + * + * @param {SdaCreateOptions} options - The address creation options. + * @returns {Promise} The created deposit addresses, one per distinct address. + */ + createDepositAddress(options: SdaCreateOptions): Promise; + /** + * Derives a deposit address client-side, without any provider call and + * without activating or monitoring it — used to verify (derive + compare) or + * recover an address for a self-custodial provider. Optional: only supported + * when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * + * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; the provider-specific `derivation` inputs are folded into the result. + * @returns {Promise} The derived deposit address. + * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). + */ + deriveDepositAddress(options: SdaCreateOptions): Promise; + /** + * Looks up an existing deposit address by its identifier — the + * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, + * which round-trips any chain context the provider needs. Optional: + * only supported when {@link SdaCapabilities#getAddress} is `true`. + * + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @returns {Promise} The deposit address descriptor. + * @throws {NotImplementedError} If this provider does not support address lookup (check `getCapabilities().getAddress`). + * @throws {Error} If no such address exists. + */ + getDepositAddress(id: string): Promise; + /** + * Refreshes the activation of a deposit address so the provider keeps + * monitoring it. Optional: only relevant when {@link SdaCapabilities#activation} + * is `'ttl'`. + * + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. + * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). + * @throws {NotImplementedError} If this provider does not use activation TTLs (check `getCapabilities().activation`). + */ + renewDepositAddress(id: string): Promise; + /** + * Lists the deposits observed at a deposit address. Optional: only supported + * when {@link SdaCapabilities#historyByAddress} is `true`. + * + * @param {string} address - The deposit address to list transfers for. + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). + * @returns {Promise} The transfers for the address. + * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). + */ + getDepositAddressTransfers(address: string, options?: SdaTransfersOptions): Promise; + /** + * Lists transfers aggregated by recipient — every deposit routed to the given + * recipient across all of that recipient's deposit addresses and source + * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} + * is `true`. + * + * @param {string} recipient - The recipient (destination) address to aggregate transfers for. + * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. + * @returns {Promise} The transfers routed to the recipient. + * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). + */ + getTransfersByRecipient(recipient: string, destinationChain: Blockchain, options?: SdaTransfersOptions): Promise; + /** + * Retrieves the status of a single transfer by its identifier. Optional: + * only supported when {@link SdaCapabilities#transferStatus} is `true`. + * + * @param {string} id - The transfer identifier. + * @returns {Promise} The transfer's current status. + * @throws {NotImplementedError} If this provider does not support transfer-status lookup (check `getCapabilities().transferStatus`). + * @throws {Error} If no such transfer exists. + */ + getTransferStatus(id: string): Promise; + /** + * Recovers a deposit or address that was not picked up automatically, using + * the provider's recovery mode (see {@link SdaCapabilities#recovery}). + * Optional: only supported when `recovery` is not `'none'`. + * + * @param {SdaRecoveryOptions} options - The recovery options. + * @returns {Promise} The recovery outcome. + * @throws {NotImplementedError} If this provider does not support recovery (check `getCapabilities().recovery`). + */ + recoverDepositAddress(options: SdaRecoveryOptions): Promise; + /** + * Disables a deposit address so it no longer accepts deposits. Optional: + * only supported when {@link SdaCapabilities#disableAddress} is `true`. + * + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @returns {Promise} Resolves once the address has been disabled. + * @throws {NotImplementedError} If this provider does not support disabling addresses (check `getCapabilities().disableAddress`). + */ + disableDepositAddress(id: string): Promise; +} +/** + * Abstract base class for "Smart Deposit Address" (SDA) providers. Concrete + * providers extend this and implement the provider-specific calls. + * + * @abstract + * @implements {ISdaProtocol} + */ +export default class SdaProtocol implements ISdaProtocol { + /** + * Creates a new SDA protocol without binding it to a wallet account. + * + * @overload + * @param {undefined} [account] - The wallet account to use to interact with the protocol. + * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. + */ + constructor(account?: undefined, config?: SdaProtocolConfig); + /** + * Creates a new read-only SDA protocol. + * + * @overload + * @param {IWalletAccountReadOnly} account - The wallet account to use to interact with the protocol. + * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. + */ + constructor(account: IWalletAccountReadOnly, config?: SdaProtocolConfig); + /** + * Creates a new SDA protocol. + * + * @overload + * @param {IWalletAccount} account - The wallet account to use to interact with the protocol. + * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. + */ + constructor(account: IWalletAccount, config?: SdaProtocolConfig); + /** + * The wallet account to use to interact with the protocol. The account's + * address is the default delivery destination for created addresses. + * + * @protected + * @type {IWalletAccountReadOnly | IWalletAccount | undefined} + */ + protected _account: IWalletAccountReadOnly | IWalletAccount | undefined; + /** + * The SDA protocol configuration. + * + * @protected + * @type {SdaProtocolConfig} + */ + protected _config: SdaProtocolConfig; + /** + * Returns which optional parts of the interface this provider implements. + * + * @abstract + * @returns {SdaCapabilities} The provider's capabilities. + */ + getCapabilities(): SdaCapabilities; + /** + * Lists the conversion routes the provider supports: source chains, accepted + * input tokens, destination assets and per-route deposit limits. When + * {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'`, `sourceChain` + * and `destinationChain` must be supplied. + * + * @abstract + * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. + * @returns {Promise} The supported routes. + * @throws {Error} If {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + */ + getSupportedRoutes(options?: SdaRoutesOptions): Promise; + /** + * Fetches a non-binding quote for a deposit. Optional: only supported when + * {@link SdaCapabilities#quoting} is `true`, and required up front when + * {@link SdaCapabilities#quoteRequired} is `true`. + * + * @abstract + * @param {SdaQuoteOptions} options - The quote options. + * @returns {Promise} The quoted deposit details. + * @throws {NotImplementedError} If this provider does not support quoting (check `getCapabilities().quoting`). + */ + quoteDeposit(options: SdaQuoteOptions): Promise; + /** + * Creates deposit addresses for the given route and destination, ready to + * receive per the provider's {@link SdaCapabilities#activation} model (for a + * `'required'` / `'ttl'` provider this also activates the address so it is + * monitored). Returns one entry per distinct address: a provider that issues a + * single address across a chain family returns one entry covering all of + * `sourceChains`, while a provider that issues one address per source chain + * returns one entry each. + * + * @abstract + * @param {SdaCreateOptions} options - The address creation options. + * @returns {Promise} The created deposit addresses, one per distinct address. + */ + createDepositAddress(options: SdaCreateOptions): Promise; + /** + * Derives a deposit address client-side, without any provider call and + * without activating or monitoring it — used to verify (derive + compare) or + * recover an address for a self-custodial provider. Optional: only supported + * when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * + * @abstract + * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; the provider-specific `derivation` inputs are folded into the result. + * @returns {Promise} The derived deposit address. + * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). + */ + deriveDepositAddress(options: SdaCreateOptions): Promise; + /** + * Looks up an existing deposit address by its identifier — the + * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, + * which round-trips any chain context the provider needs. Optional: + * only supported when {@link SdaCapabilities#getAddress} is `true`. + * + * @abstract + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @returns {Promise} The deposit address descriptor. + * @throws {NotImplementedError} If this provider does not support address lookup (check `getCapabilities().getAddress`). + * @throws {Error} If no such address exists. + */ + getDepositAddress(id: string): Promise; + /** + * Refreshes the activation of a deposit address so the provider keeps + * monitoring it. Optional: only relevant when {@link SdaCapabilities#activation} + * is `'ttl'`. + * + * @abstract + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. + * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). + * @throws {NotImplementedError} If this provider does not use activation TTLs (check `getCapabilities().activation`). + */ + renewDepositAddress(id: string): Promise; + /** + * Lists the deposits observed at a deposit address. Optional: only supported + * when {@link SdaCapabilities#historyByAddress} is `true`. + * + * @abstract + * @param {string} address - The deposit address to list transfers for. + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). + * @returns {Promise} The transfers for the address. + * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). + */ + getDepositAddressTransfers(address: string, options?: SdaTransfersOptions): Promise; + /** + * Lists transfers aggregated by recipient — every deposit routed to the given + * recipient across all of that recipient's deposit addresses and source + * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} + * is `true`. + * + * @abstract + * @param {string} recipient - The recipient (destination) address to aggregate transfers for. + * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. + * @returns {Promise} The transfers routed to the recipient. + * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). + */ + getTransfersByRecipient(recipient: string, destinationChain: Blockchain, options?: SdaTransfersOptions): Promise; + /** + * Retrieves the status of a single transfer by its identifier. Optional: + * only supported when {@link SdaCapabilities#transferStatus} is `true`. + * + * @abstract + * @param {string} id - The transfer identifier. + * @returns {Promise} The transfer's current status. + * @throws {NotImplementedError} If this provider does not support transfer-status lookup (check `getCapabilities().transferStatus`). + * @throws {Error} If no such transfer exists. + */ + getTransferStatus(id: string): Promise; + /** + * Recovers a deposit or address that was not picked up automatically, using + * the provider's recovery mode (see {@link SdaCapabilities#recovery}). + * Optional: only supported when `recovery` is not `'none'`. + * + * @abstract + * @param {SdaRecoveryOptions} options - The recovery options. + * @returns {Promise} The recovery outcome. + * @throws {NotImplementedError} If this provider does not support recovery (check `getCapabilities().recovery`). + */ + recoverDepositAddress(options: SdaRecoveryOptions): Promise; + /** + * Disables a deposit address so it no longer accepts deposits. Optional: + * only supported when {@link SdaCapabilities#disableAddress} is `true`. + * + * @abstract + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @returns {Promise} Resolves once the address has been disabled. + * @throws {NotImplementedError} If this provider does not support disabling addresses (check `getCapabilities().disableAddress`). + */ + disableDepositAddress(id: string): Promise; +} +export type IWalletAccountReadOnly = import("../wallet-account-read-only.js").IWalletAccountReadOnly; +export type IWalletAccount = import("../wallet-account.js").IWalletAccount; +/** + * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific + * chain name (e.g. `'ethereum'`). + */ +export type Blockchain = string | number; +/** + * Describes which optional parts of the SDA interface a provider implements, so + * consumers can adapt their UI/flow without trial-and-error. The required core + * (`getSupportedRoutes`, `createDepositAddress`) is always available and + * therefore not listed here. + */ +export type SdaCapabilities = { + /** + * - Whether {@link ISdaProtocol#quoteDeposit} is supported. + */ + quoting: boolean; + /** + * - Whether a quote must be fetched (via {@link ISdaProtocol#quoteDeposit}) and passed to {@link ISdaProtocol#createDepositAddress} via `options.quote` before an address can be issued (e.g., Relay, whose deposit address is bound to a quote/amount). + */ + quoteRequired: boolean; + /** + * - Whether issued addresses can receive more than one deposit. + */ + reusableAddresses: boolean; + /** + * - Whether a single address is valid across several source chains of the same VM family. + */ + multiChainAddress: boolean; + /** + * - Who controls deposited funds while in flight: `'trusted-operator'` (the provider holds the address and funds) or `'self-custodial'` (the address is an on-chain contract with withdrawal rights fixed in code, so funds are recoverable on-chain without the provider). + */ + custodyModel: SdaCustodyModel; + /** + * - Whether the deposit address is deterministically derivable and verifiable client-side, before any provider call (via {@link ISdaProtocol#deriveDepositAddress}). + */ + clientDerivableAddress: boolean; + /** + * - The address activation lifecycle: `'none'` (live as soon as it is created), `'required'` (must be activated before the provider monitors it), or `'ttl'` (activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}). + */ + activation: SdaActivationModel; + /** + * - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). + */ + routeDiscovery: SdaRouteDiscoveryMode; + /** + * - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. + */ + getAddress: boolean; + /** + * - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. + */ + transferStatus: boolean; + /** + * - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. + */ + historyByAddress: boolean; + /** + * - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. + */ + historyByRecipient: boolean; + /** + * - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'` or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). Keep-alive of an expired address is the activation lifecycle (`activation: 'ttl'`), not recovery. + */ + recovery: SdaRecoveryMode; + /** + * - Whether {@link ISdaProtocol#disableDepositAddress} is supported. + */ + disableAddress: boolean; + /** + * - Whether the provider honours a `refundAddress`. + */ + refund: boolean; +}; +/** + * How a provider re-processes a deposit that was not picked up automatically. + * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means + * no such call is exposed. This is about provider-side reprocessing of a missed + * deposit, not about fund custody — whether deposited funds are recoverable is + * governed by {@link SdaCapabilities#custodyModel}. Re-enabling an idle/expired + * address is the activation lifecycle ({@link SdaActivationModel} `'ttl'` + + * {@link ISdaProtocol#renewDepositAddress}), not recovery. + */ +export type SdaRecoveryMode = "reindex" | "none"; +/** + * The activation lifecycle of a deposit address. `'none'` — the address is live + * as soon as it is created. `'required'` — the address must be activated (so the + * provider starts monitoring it) before it can receive deposits. `'ttl'` — + * activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}. + */ +export type SdaActivationModel = "none" | "required" | "ttl"; +/** + * Who controls deposited funds while a deposit is in flight. `'trusted-operator'` + * — the provider holds the deposit address and the funds (recovery means asking + * the provider to reprocess). `'self-custodial'` — the address is an on-chain + * contract whose withdrawal rights are fixed in code (e.g. the recipient can + * withdraw immediately, an optional custodial withdrawer only after a timelock), + * so funds are recoverable on-chain without the provider. + */ +export type SdaCustodyModel = "self-custodial" | "trusted-operator"; +/** + * How a provider lets routes be discovered. `'full'` means + * {@link ISdaProtocol#getSupportedRoutes} returns every route with no filters; + * `'by-chain-pair'` means a source and destination chain must be supplied. + */ +export type SdaRouteDiscoveryMode = "full" | "by-chain-pair"; +/** + * Connection and default settings shared by every SDA provider. Concrete + * providers extend this with their own fields (endpoints, credentials, etc.). + */ +export type SdaProtocolConfig = { + /** + * - Overrides the provider's API base URL. + */ + apiUrl?: string; + /** + * - Provider API key or credentials, when required. + */ + apiKey?: string; + /** + * - Refund address used when a call omits one. + */ + defaultRefundAddress?: string; +}; +/** + * A normalized, protocol-agnostic token reference. `token` is the identifier + * the provider expects in SDA calls; `address` is the on-chain contract address + * when applicable (absent for native gas tokens). + */ +export type SdaToken = { + /** + * - The provider-specific token identifier to use in SDA calls. + */ + token: string; + /** + * - The chain on which the token lives. + */ + chain: Blockchain; + /** + * - The token symbol (e.g., 'USDC', 'USDT'). + */ + symbol: string; + /** + * - The number of decimal places for the token's base unit. + */ + decimals: number; + /** + * - The token contract address, if applicable. + */ + address?: string; + /** + * - The token's full name. + */ + name?: string; +}; +/** + * Per-route deposit limits, denominated in the base unit of the route's input + * token. Either bound may be absent when the provider does not enforce it. + */ +export type SdaLimits = { + /** + * - Minimum deposit amount, in the input token's base unit. + */ + min?: bigint; + /** + * - Maximum deposit amount, in the input token's base unit. + */ + max?: bigint; +}; +/** + * Optional filters for narrowing route discovery. + */ +export type SdaRoutesOptions = { + /** + * - Restrict to routes that accept deposits from this chain. + */ + sourceChain?: Blockchain; + /** + * - Restrict to routes that accept this input token. + */ + sourceToken?: string; + /** + * - Restrict to routes that deliver to this chain. + */ + destinationChain?: Blockchain; + /** + * - Restrict to routes that deliver this asset. + */ + destinationAsset?: string; +}; +/** + * A supported conversion route: one or more source chains and their accepted + * input tokens, the destination chain, and the asset delivered there. + */ +export type SdaRoute = { + /** + * - The source chains this route accepts deposits from. A list because some providers issue one address valid across a VM family. + */ + sourceChains: Blockchain[]; + /** + * - The deposit tokens accepted on the source side. + */ + inputTokens: SdaToken[]; + /** + * - The chain the converted asset is delivered to. + */ + destinationChain: Blockchain; + /** + * - The asset delivered to the destination (e.g., USDT). + */ + destinationAsset: SdaToken; + /** + * - Deposit limits for this route. + */ + limits?: SdaLimits; + /** + * - Whether addresses issued for this route can receive more than one deposit. + */ + reusable?: boolean; + /** + * - Typical end-to-end duration in seconds. + */ + estimatedDuration?: number; +}; +/** + * Options for fetching a deposit quote. Required up front by providers whose + * addresses are bound to a quote (e.g., Rhino); optional otherwise. + */ +export type SdaQuoteOptions = { + /** + * - The chain the deposit originates from. + */ + sourceChain: Blockchain; + /** + * - The provider identifier of the token being deposited. + */ + inputToken: string; + /** + * - The chain the converted asset is delivered to. + */ + destinationChain: Blockchain; + /** + * - The provider identifier of the asset to deliver. + */ + destinationAsset: string; + /** + * - The amount to deposit, in the input token's base unit. + */ + inputAmount: number | bigint; +}; +/** + * The category of a fee charged by the provider. + */ +export type SdaFeeType = "network" | "protocol" | "affiliate" | "other"; +/** + * A single itemised fee. + */ +export type SdaFee = { + /** + * - The category of the fee. + */ + type: SdaFeeType; + /** + * - The fee amount, in the fee token's base unit. + */ + amount: bigint; + /** + * - The token in which the fee is denominated. + */ + token: string; + /** + * - The chain on which the fee is charged. + */ + chain?: Blockchain; + /** + * - Whether the fee is already reflected in the quoted output amount. + */ + included?: boolean; + /** + * - A human-readable description of the fee. + */ + description?: string; +}; +/** + * A non-binding estimate of the asset delivered for a given deposit. Some + * providers return an `id` that must be passed to {@link ISdaProtocol#createDepositAddress} + * to bind the address to this quote. + */ +export type SdaQuote = { + /** + * - The chain the deposit originates from. + */ + inputChain: Blockchain; + /** + * - The provider identifier of the deposited token. + */ + inputToken: string; + /** + * - The amount deposited, in the input token's base unit. + */ + inputAmount: bigint; + /** + * - The chain the converted asset is delivered to. + */ + destinationChain: Blockchain; + /** + * - The provider identifier of the delivered asset. + */ + destinationAsset: string; + /** + * - The estimated amount delivered, in the destination asset's base unit. + */ + outputAmount: bigint; + /** + * - Itemised fee breakdown. + */ + fees: SdaFee[]; + /** + * - The effective conversion rate as a string, to avoid precision loss. + */ + rate?: string; + /** + * - Unix timestamp (seconds) at which the quote expires. + */ + expiry?: number; + /** + * - The provider quote identifier, when an address must be bound to this quote. + */ + id?: string; +}; +/** + * Options for creating a deposit address. + */ +export type SdaCreateOptions = { + /** + * - One or more source chains the address should accept deposits from. Providers that issue one address per VM family use the full list; single-chain providers use a one-element list. + */ + sourceChains: Blockchain[]; + /** + * - The chain the converted asset is delivered to. + */ + destinationChain: Blockchain; + /** + * - The provider identifier of the asset to deliver (e.g., USDT). + */ + destinationAsset: string; + /** + * - The address that receives the delivered asset. Defaults to the bound account's address. + */ + destinationAddress?: string; + /** + * - The expected input token, when the provider needs it declared up front. + */ + inputToken?: string; + /** + * - The address that receives refunds if a deposit cannot be processed (push-refund style). + */ + refundAddress?: string; + /** + * - Provider-specific inputs a self-custodial / client-derivable provider folds into the deposit-address derivation (e.g. a custodial withdrawer, salt, or execution parameters). The interface passes this through untyped; each provider documents its own shape. Distinct from `refundAddress` (push-refund) — these inputs change the resulting address. + */ + derivation?: Record; + /** + * - Request a reusable address (when the provider supports both modes). + */ + reusable?: boolean; + /** + * - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. + */ + quote?: SdaQuote | string; +}; +/** + * A deposit address plus its normalized descriptor: where it accepts deposits + * from, what it accepts, where it delivers, and its lifecycle metadata. + */ +export type SdaDepositAddress = { + /** + * - The deposit address the user sends funds to. + */ + address: string; + /** + * - The provider identifier for this SDA, used for status, recovery and disabling. + */ + id?: string; + /** + * - The chains this address accepts deposits from. + */ + sourceChains: Blockchain[]; + /** + * - The tokens this address accepts. + */ + supportedInputTokens: SdaToken[]; + /** + * - The chain the converted asset is delivered to. + */ + destinationChain: Blockchain; + /** + * - The asset delivered to the destination. + */ + destinationAsset: SdaToken; + /** + * - The resolved address that receives the delivered asset. + */ + destinationAddress: string; + /** + * - The quote bound to this address, if any. + */ + quote?: SdaQuote; + /** + * - Deposit limits for this address. + */ + limits?: SdaLimits; + /** + * - Whether the address can receive more than one deposit. + */ + reusable: boolean; + /** + * - The refund address bound to this address. + */ + refundAddress?: string; + /** + * - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. + */ + expiry?: number; + /** + * - The provider-specific derivation inputs this address was created from (echoed back), so a `clientDerivableAddress` provider can re-derive, verify or recover the address later. + */ + derivation?: Record; +}; +/** + * The lifecycle status of a deposit/transfer through an SDA. + */ +export type SdaTransferStatus = "pending" | "detected" | "processing" | "completed" | "failed" | "refund-pending" | "refunded" | "expired"; +/** + * A single deposit observed at, and processed through, an SDA. + */ +export type SdaTransfer = { + /** + * - The provider identifier for this transfer. + */ + id: string; + /** + * - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). + */ + depositAddress?: string; + /** + * - The current status of the transfer. + */ + status: SdaTransferStatus; + /** + * - The token that was deposited. + */ + inputToken?: SdaToken; + /** + * - The amount deposited, in the input token's base unit. + */ + inputAmount?: bigint; + /** + * - The asset delivered to the destination. + */ + destinationAsset?: SdaToken; + /** + * - The amount delivered, in the destination asset's base unit. + */ + outputAmount?: bigint; + /** + * - The hash of the deposit transaction on the source chain. + */ + sourceTxHash?: string; + /** + * - The hash of the delivery transaction on the destination chain. + */ + destinationTxHash?: string; + /** + * - Itemised fees applied to this transfer. + */ + fees?: SdaFee[]; + /** + * - Unix timestamp (seconds) when the transfer was first observed. + */ + createdAt?: number; + /** + * - Unix timestamp (seconds) when the transfer was last updated. + */ + updatedAt?: number; +}; +/** + * Optional pagination/filtering for transfer history. + */ +export type SdaTransfersOptions = { + /** + * - The source chain of the deposit address, required by providers that key addresses by (address, chain). + */ + sourceChain?: Blockchain; + /** + * - The maximum number of transfers to return. + */ + limit?: number; + /** + * - An opaque pagination cursor returned by a previous call. + */ + cursor?: string; + /** + * - Restrict to transfers in this status. + */ + status?: SdaTransferStatus; +}; +/** + * Options for re-processing a deposit that was not picked up automatically + * (`reindex`). Callers supply whatever the provider needs — typically the source + * transaction, or the deposit address / SDA id. + */ +export type SdaRecoveryOptions = { + /** + * - The deposit address to reindex. + */ + address?: string; + /** + * - The provider SDA identifier. + */ + id?: string; + /** + * - The deposit transaction to re-index. + */ + sourceTxHash?: string; + /** + * - The chain of the deposit transaction. + */ + sourceChain?: Blockchain; +}; +/** + * The outcome of a recovery attempt. + */ +export type SdaRecoveryResult = { + /** + * - The result of the reindex attempt. + */ + status: "reindexed" | "pending" | "failed"; + /** + * - The address that was reindexed. + */ + address?: string; + /** + * - The provider SDA identifier. + */ + id?: string; + /** + * - The transfer that was recovered, if one resulted. + */ + transfer?: SdaTransfer; + /** + * - A human-readable description of the outcome. + */ + message?: string; +}; From 3545f2a5d773120fc38e697d99c6b8becaab1605 Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 2 Jul 2026 09:42:54 -0400 Subject: [PATCH 05/27] refactor(protocols): accept number | bigint for SDA deposit limits Widen SdaLimits.min/max to `number | bigint` (PR #46 review). --- src/protocols/sda-protocol.js | 4 ++-- types/src/protocols/sda-protocol.d.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 3cc289c..cd920cf 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -119,8 +119,8 @@ import { NotImplementedError } from '../errors.js' * token. Either bound may be absent when the provider does not enforce it. * * @typedef {Object} SdaLimits - * @property {bigint} [min] - Minimum deposit amount, in the input token's base unit. - * @property {bigint} [max] - Maximum deposit amount, in the input token's base unit. + * @property {number | bigint} [min] - Minimum deposit amount, in the input token's base unit. + * @property {number | bigint} [max] - Maximum deposit amount, in the input token's base unit. */ /** diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 4ae4a3f..762f90f 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -92,8 +92,8 @@ * token. Either bound may be absent when the provider does not enforce it. * * @typedef {Object} SdaLimits - * @property {bigint} [min] - Minimum deposit amount, in the input token's base unit. - * @property {bigint} [max] - Maximum deposit amount, in the input token's base unit. + * @property {number | bigint} [min] - Minimum deposit amount, in the input token's base unit. + * @property {number | bigint} [max] - Maximum deposit amount, in the input token's base unit. */ /** * Optional filters for narrowing route discovery. @@ -735,11 +735,11 @@ export type SdaLimits = { /** * - Minimum deposit amount, in the input token's base unit. */ - min?: bigint; + min?: number | bigint; /** * - Maximum deposit amount, in the input token's base unit. */ - max?: bigint; + max?: number | bigint; }; /** * Optional filters for narrowing route discovery. From 0060e75790a9c88d7bf012e8dc1382c4f08ee409 Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 2 Jul 2026 09:49:09 -0400 Subject: [PATCH 06/27] refactor(protocols): make SdaDepositAddress.id required The id keys status/recovery/disable operations, so a created address must always carry one (PR #46 review). --- src/protocols/sda-protocol.js | 2 +- types/src/protocols/sda-protocol.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index cd920cf..728ce53 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -216,7 +216,7 @@ import { NotImplementedError } from '../errors.js' * * @typedef {Object} SdaDepositAddress * @property {string} address - The deposit address the user sends funds to. - * @property {string} [id] - The provider identifier for this SDA, used for status, recovery and disabling. + * @property {string} id - The provider identifier for this SDA, used for status, recovery and disabling. * @property {Blockchain[]} sourceChains - The chains this address accepts deposits from. * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 762f90f..8778878 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -181,7 +181,7 @@ * * @typedef {Object} SdaDepositAddress * @property {string} address - The deposit address the user sends funds to. - * @property {string} [id] - The provider identifier for this SDA, used for status, recovery and disabling. + * @property {string} id - The provider identifier for this SDA, used for status, recovery and disabling. * @property {Blockchain[]} sourceChains - The chains this address accepts deposits from. * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. @@ -955,7 +955,7 @@ export type SdaDepositAddress = { /** * - The provider identifier for this SDA, used for status, recovery and disabling. */ - id?: string; + id: string; /** * - The chains this address accepts deposits from. */ From e299a0e78a591ee5af21db0743c6157d8be7e40c Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 2 Jul 2026 09:54:39 -0400 Subject: [PATCH 07/27] refactor(protocols): use skip offset instead of cursor in SdaTransfersOptions Offset-based pagination (`skip`) is consistent with the wallet modules' get-transfers methods (PR #46 review). --- src/protocols/sda-protocol.js | 2 +- types/src/protocols/sda-protocol.d.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 728ce53..95d8874 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -261,7 +261,7 @@ import { NotImplementedError } from '../errors.js' * @typedef {Object} SdaTransfersOptions * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by providers that key addresses by (address, chain). * @property {number} [limit] - The maximum number of transfers to return. - * @property {string} [cursor] - An opaque pagination cursor returned by a previous call. + * @property {number} [skip] - The number of transfers to skip, for offset-based pagination. * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. */ diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 8778878..f477c5a 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -223,7 +223,7 @@ * @typedef {Object} SdaTransfersOptions * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by providers that key addresses by (address, chain). * @property {number} [limit] - The maximum number of transfers to return. - * @property {string} [cursor] - An opaque pagination cursor returned by a previous call. + * @property {number} [skip] - The number of transfers to skip, for offset-based pagination. * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. */ /** @@ -1071,9 +1071,9 @@ export type SdaTransfersOptions = { */ limit?: number; /** - * - An opaque pagination cursor returned by a previous call. + * - The number of transfers to skip, for offset-based pagination. */ - cursor?: string; + skip?: number; /** * - Restrict to transfers in this status. */ From ae6ab3d7cd8baacc2868a3cdae1d885352e55b10 Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 2 Jul 2026 10:01:45 -0400 Subject: [PATCH 08/27] refactor(protocols): rename getDepositAddressTransfers to getTransfers Shorter, matches the wallet modules' naming (PR #46 review). Coexists with getTransfersByRecipient (by-address vs by-recipient history). --- src/protocols/sda-protocol.js | 10 +++++----- types/src/protocols/sda-protocol.d.ts | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 95d8874..3d21d02 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -43,7 +43,7 @@ import { NotImplementedError } from '../errors.js' * @property {SdaRouteDiscoveryMode} routeDiscovery - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). * @property {boolean} getAddress - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. * @property {boolean} transferStatus - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. - * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. + * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. * @property {boolean} historyByRecipient - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. * @property {SdaRecoveryMode} recovery - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'` or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). Keep-alive of an expired address is the activation lifecycle (`activation: 'ttl'`), not recovery. * @property {boolean} disableAddress - Whether {@link ISdaProtocol#disableDepositAddress} is supported. @@ -404,8 +404,8 @@ export class ISdaProtocol { * @returns {Promise} The transfers for the address. * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). */ - async getDepositAddressTransfers (address, options) { - throw new NotImplementedError('getDepositAddressTransfers(address, options)') + async getTransfers (address, options) { + throw new NotImplementedError('getTransfers(address, options)') } /** @@ -624,8 +624,8 @@ export default class SdaProtocol { * @returns {Promise} The transfers for the address. * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). */ - async getDepositAddressTransfers (address, options) { - throw new NotImplementedError('getDepositAddressTransfers(address, options)') + async getTransfers (address, options) { + throw new NotImplementedError('getTransfers(address, options)') } /** diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index f477c5a..5971627 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -23,7 +23,7 @@ * @property {SdaRouteDiscoveryMode} routeDiscovery - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). * @property {boolean} getAddress - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. * @property {boolean} transferStatus - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. - * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. + * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. * @property {boolean} historyByRecipient - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. * @property {SdaRecoveryMode} recovery - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'` or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). Keep-alive of an expired address is the activation lifecycle (`activation: 'ttl'`), not recovery. * @property {boolean} disableAddress - Whether {@link ISdaProtocol#disableDepositAddress} is supported. @@ -342,7 +342,7 @@ export class ISdaProtocol { * @returns {Promise} The transfers for the address. * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). */ - getDepositAddressTransfers(address: string, options?: SdaTransfersOptions): Promise; + getTransfers(address: string, options?: SdaTransfersOptions): Promise; /** * Lists transfers aggregated by recipient — every deposit routed to the given * recipient across all of that recipient's deposit addresses and source @@ -523,7 +523,7 @@ export default class SdaProtocol implements ISdaProtocol { * @returns {Promise} The transfers for the address. * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). */ - getDepositAddressTransfers(address: string, options?: SdaTransfersOptions): Promise; + getTransfers(address: string, options?: SdaTransfersOptions): Promise; /** * Lists transfers aggregated by recipient — every deposit routed to the given * recipient across all of that recipient's deposit addresses and source @@ -626,7 +626,7 @@ export type SdaCapabilities = { */ transferStatus: boolean; /** - * - Whether {@link ISdaProtocol#getDepositAddressTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. + * - Whether {@link ISdaProtocol#getTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. */ historyByAddress: boolean; /** From be6e8c10af865d90e9c10ca09d3f70884fe02602 Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 2 Jul 2026 10:05:26 -0400 Subject: [PATCH 09/27] refactor(protocols): swap getTransfersByRecipient arg order to (destinationChain, recipient) PR #46 review. --- src/protocols/sda-protocol.js | 12 ++++++------ types/src/protocols/sda-protocol.d.ts | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 3d21d02..8315a2d 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -414,14 +414,14 @@ export class ISdaProtocol { * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} * is `true`. * - * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. + * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). */ - async getTransfersByRecipient (recipient, destinationChain, options) { - throw new NotImplementedError('getTransfersByRecipient(recipient, destinationChain, options)') + async getTransfersByRecipient (destinationChain, recipient, options) { + throw new NotImplementedError('getTransfersByRecipient(destinationChain, recipient, options)') } /** @@ -635,14 +635,14 @@ export default class SdaProtocol { * is `true`. * * @abstract - * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. + * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). */ - async getTransfersByRecipient (recipient, destinationChain, options) { - throw new NotImplementedError('getTransfersByRecipient(recipient, destinationChain, options)') + async getTransfersByRecipient (destinationChain, recipient, options) { + throw new NotImplementedError('getTransfersByRecipient(destinationChain, recipient, options)') } /** diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 5971627..61b0d58 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -349,13 +349,13 @@ export class ISdaProtocol { * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} * is `true`. * - * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. + * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). */ - getTransfersByRecipient(recipient: string, destinationChain: Blockchain, options?: SdaTransfersOptions): Promise; + getTransfersByRecipient(destinationChain: Blockchain, recipient: string, options?: SdaTransfersOptions): Promise; /** * Retrieves the status of a single transfer by its identifier. Optional: * only supported when {@link SdaCapabilities#transferStatus} is `true`. @@ -531,13 +531,13 @@ export default class SdaProtocol implements ISdaProtocol { * is `true`. * * @abstract - * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. + * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). */ - getTransfersByRecipient(recipient: string, destinationChain: Blockchain, options?: SdaTransfersOptions): Promise; + getTransfersByRecipient(destinationChain: Blockchain, recipient: string, options?: SdaTransfersOptions): Promise; /** * Retrieves the status of a single transfer by its identifier. Optional: * only supported when {@link SdaCapabilities#transferStatus} is `true`. From 71e587a24acba9347cc3178f00c11a6b3db0dcd8 Mon Sep 17 00:00:00 2001 From: rickalon Date: Fri, 3 Jul 2026 09:28:18 -0400 Subject: [PATCH 10/27] refactor(protocols): drop config argument from base SdaProtocol SDA has no config fields all providers share, so the base takes only the account; each provider declares its own config and constructor (PR #46 review). --- src/protocols/index.js | 1 - src/protocols/sda-protocol.js | 23 +------------- types/src/protocols/index.d.ts | 1 - types/src/protocols/sda-protocol.d.ts | 43 ++------------------------- 4 files changed, 4 insertions(+), 64 deletions(-) diff --git a/src/protocols/index.js b/src/protocols/index.js index 91844fa..b81aab3 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -68,7 +68,6 @@ /** @typedef {import('./swidge-protocol.js').SwidgeSupportedToken} SwidgeSupportedToken */ /** @typedef {import('./swidge-protocol.js').SwidgeSupportedTokensOptions} SwidgeSupportedTokensOptions */ -/** @typedef {import('./sda-protocol.js').SdaProtocolConfig} SdaProtocolConfig */ /** @typedef {import('./sda-protocol.js').SdaCapabilities} SdaCapabilities */ /** @typedef {import('./sda-protocol.js').SdaRecoveryMode} SdaRecoveryMode */ /** @typedef {import('./sda-protocol.js').SdaRouteDiscoveryMode} SdaRouteDiscoveryMode */ diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 8315a2d..a7e2c01 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -90,16 +90,6 @@ import { NotImplementedError } from '../errors.js' * @typedef {'full' | 'by-chain-pair'} SdaRouteDiscoveryMode */ -/** - * Connection and default settings shared by every SDA provider. Concrete - * providers extend this with their own fields (endpoints, credentials, etc.). - * - * @typedef {Object} SdaProtocolConfig - * @property {string} [apiUrl] - Overrides the provider's API base URL. - * @property {string} [apiKey] - Provider API key or credentials, when required. - * @property {string} [defaultRefundAddress] - Refund address used when a call omits one. - */ - /** * A normalized, protocol-agnostic token reference. `token` is the identifier * the provider expects in SDA calls; `address` is the on-chain contract address @@ -476,7 +466,6 @@ export default class SdaProtocol { * * @overload * @param {undefined} [account] - The wallet account to use to interact with the protocol. - * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. */ /** @@ -484,7 +473,6 @@ export default class SdaProtocol { * * @overload * @param {IWalletAccountReadOnly} account - The wallet account to use to interact with the protocol. - * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. */ /** @@ -492,9 +480,8 @@ export default class SdaProtocol { * * @overload * @param {IWalletAccount} account - The wallet account to use to interact with the protocol. - * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. */ - constructor (account, config = {}) { + constructor (account) { /** * The wallet account to use to interact with the protocol. The account's * address is the default delivery destination for created addresses. @@ -503,14 +490,6 @@ export default class SdaProtocol { * @type {IWalletAccountReadOnly | IWalletAccount | undefined} */ this._account = account - - /** - * The SDA protocol configuration. - * - * @protected - * @type {SdaProtocolConfig} - */ - this._config = config } /** diff --git a/types/src/protocols/index.d.ts b/types/src/protocols/index.d.ts index e3a2ceb..c235722 100644 --- a/types/src/protocols/index.d.ts +++ b/types/src/protocols/index.d.ts @@ -44,7 +44,6 @@ export type SwidgeStatusResult = import("./swidge-protocol.js").SwidgeStatusResu export type SwidgeSupportedChain = import("./swidge-protocol.js").SwidgeSupportedChain; export type SwidgeSupportedToken = import("./swidge-protocol.js").SwidgeSupportedToken; export type SwidgeSupportedTokensOptions = import("./swidge-protocol.js").SwidgeSupportedTokensOptions; -export type SdaProtocolConfig = import("./sda-protocol.js").SdaProtocolConfig; export type SdaCapabilities = import("./sda-protocol.js").SdaCapabilities; export type SdaRecoveryMode = import("./sda-protocol.js").SdaRecoveryMode; export type SdaRouteDiscoveryMode = import("./sda-protocol.js").SdaRouteDiscoveryMode; diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 61b0d58..976aeb8 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -65,15 +65,6 @@ * * @typedef {'full' | 'by-chain-pair'} SdaRouteDiscoveryMode */ -/** - * Connection and default settings shared by every SDA provider. Concrete - * providers extend this with their own fields (endpoints, credentials, etc.). - * - * @typedef {Object} SdaProtocolConfig - * @property {string} [apiUrl] - Overrides the provider's API base URL. - * @property {string} [apiKey] - Provider API key or credentials, when required. - * @property {string} [defaultRefundAddress] - Refund address used when a call omits one. - */ /** * A normalized, protocol-agnostic token reference. `token` is the identifier * the provider expects in SDA calls; `address` is the on-chain contract address @@ -399,25 +390,22 @@ export default class SdaProtocol implements ISdaProtocol { * * @overload * @param {undefined} [account] - The wallet account to use to interact with the protocol. - * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. */ - constructor(account?: undefined, config?: SdaProtocolConfig); + constructor(account?: undefined); /** * Creates a new read-only SDA protocol. * * @overload * @param {IWalletAccountReadOnly} account - The wallet account to use to interact with the protocol. - * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. */ - constructor(account: IWalletAccountReadOnly, config?: SdaProtocolConfig); + constructor(account: IWalletAccountReadOnly); /** * Creates a new SDA protocol. * * @overload * @param {IWalletAccount} account - The wallet account to use to interact with the protocol. - * @param {SdaProtocolConfig} [config] - The SDA protocol configuration. */ - constructor(account: IWalletAccount, config?: SdaProtocolConfig); + constructor(account: IWalletAccount); /** * The wallet account to use to interact with the protocol. The account's * address is the default delivery destination for created addresses. @@ -426,13 +414,6 @@ export default class SdaProtocol implements ISdaProtocol { * @type {IWalletAccountReadOnly | IWalletAccount | undefined} */ protected _account: IWalletAccountReadOnly | IWalletAccount | undefined; - /** - * The SDA protocol configuration. - * - * @protected - * @type {SdaProtocolConfig} - */ - protected _config: SdaProtocolConfig; /** * Returns which optional parts of the interface this provider implements. * @@ -678,24 +659,6 @@ export type SdaCustodyModel = "self-custodial" | "trusted-operator"; * `'by-chain-pair'` means a source and destination chain must be supplied. */ export type SdaRouteDiscoveryMode = "full" | "by-chain-pair"; -/** - * Connection and default settings shared by every SDA provider. Concrete - * providers extend this with their own fields (endpoints, credentials, etc.). - */ -export type SdaProtocolConfig = { - /** - * - Overrides the provider's API base URL. - */ - apiUrl?: string; - /** - * - Provider API key or credentials, when required. - */ - apiKey?: string; - /** - * - Refund address used when a call omits one. - */ - defaultRefundAddress?: string; -}; /** * A normalized, protocol-agnostic token reference. `token` is the identifier * the provider expects in SDA calls; `address` is the on-chain contract address From 386e2ee995758e61d206e7102c45a7e918265819 Mon Sep 17 00:00:00 2001 From: rickalon Date: Fri, 3 Jul 2026 09:32:04 -0400 Subject: [PATCH 11/27] refactor(protocols): drop generic derivation field from SDA types Provider-specific derivation inputs belong on the provider's own options / result type (extend + cast), not a generic Record field on the shared type (PR #46 review / #39). Removed `derivation` from SdaCreateOptions and SdaDepositAddress. --- src/protocols/sda-protocol.js | 12 ++++-------- types/src/protocols/sda-protocol.d.ts | 22 +++++----------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index a7e2c01..a8b8dd3 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -194,9 +194,7 @@ import { NotImplementedError } from '../errors.js' * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). - * @property {Record} [derivation] - Provider-specific inputs a self-custodial / client-derivable provider folds into the deposit-address derivation (e.g. a custodial withdrawer, salt, or execution parameters). The interface passes this through untyped; each provider documents its own shape. Distinct from `refundAddress` (push-refund) — these inputs change the resulting address. - * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. */ @@ -216,9 +214,7 @@ import { NotImplementedError } from '../errors.js' * @property {SdaLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. - * @property {Record} [derivation] - The provider-specific derivation inputs this address was created from (echoed back), so a `clientDerivableAddress` provider can re-derive, verify or recover the address later. - */ + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. */ /** * The lifecycle status of a deposit/transfer through an SDA. @@ -349,7 +345,7 @@ export class ISdaProtocol { * recover an address for a self-custodial provider. Optional: only supported * when {@link SdaCapabilities#clientDerivableAddress} is `true`. * - * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; the provider-specific `derivation` inputs are folded into the result. + * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). */ @@ -555,7 +551,7 @@ export default class SdaProtocol { * when {@link SdaCapabilities#clientDerivableAddress} is `true`. * * @abstract - * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; the provider-specific `derivation` inputs are folded into the result. + * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). */ diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 976aeb8..d11acdd 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -161,9 +161,7 @@ * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). - * @property {Record} [derivation] - Provider-specific inputs a self-custodial / client-derivable provider folds into the deposit-address derivation (e.g. a custodial withdrawer, salt, or execution parameters). The interface passes this through untyped; each provider documents its own shape. Distinct from `refundAddress` (push-refund) — these inputs change the resulting address. - * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. */ /** @@ -182,9 +180,7 @@ * @property {SdaLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. - * @property {Record} [derivation] - The provider-specific derivation inputs this address was created from (echoed back), so a `clientDerivableAddress` provider can re-derive, verify or recover the address later. - */ + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. */ /** * The lifecycle status of a deposit/transfer through an SDA. * @@ -297,7 +293,7 @@ export class ISdaProtocol { * recover an address for a self-custodial provider. Optional: only supported * when {@link SdaCapabilities#clientDerivableAddress} is `true`. * - * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; the provider-specific `derivation` inputs are folded into the result. + * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). */ @@ -465,7 +461,7 @@ export default class SdaProtocol implements ISdaProtocol { * when {@link SdaCapabilities#clientDerivableAddress} is `true`. * * @abstract - * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; the provider-specific `derivation` inputs are folded into the result. + * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). */ @@ -890,13 +886,9 @@ export type SdaCreateOptions = { */ inputToken?: string; /** - * - The address that receives refunds if a deposit cannot be processed (push-refund style). + * - The address that receives refunds if a deposit cannot be processed (push-refund style). * */ refundAddress?: string; - /** - * - Provider-specific inputs a self-custodial / client-derivable provider folds into the deposit-address derivation (e.g. a custodial withdrawer, salt, or execution parameters). The interface passes this through untyped; each provider documents its own shape. Distinct from `refundAddress` (push-refund) — these inputs change the resulting address. - */ - derivation?: Record; /** * - Request a reusable address (when the provider supports both modes). */ @@ -959,10 +951,6 @@ export type SdaDepositAddress = { * - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. */ expiry?: number; - /** - * - The provider-specific derivation inputs this address was created from (echoed back), so a `clientDerivableAddress` provider can re-derive, verify or recover the address later. - */ - derivation?: Record; }; /** * The lifecycle status of a deposit/transfer through an SDA. From 072ea09278900532cf7b3db53e3e0f81d266397b Mon Sep 17 00:00:00 2001 From: rickalon Date: Fri, 3 Jul 2026 14:53:47 -0400 Subject: [PATCH 12/27] feat(errors): add UnsupportedOperationError and AccountRequiredError UnsupportedOperationError is thrown by optional operations a concrete implementation deliberately doesn't support (distinct from a NotImplementedError abstract stub). AccountRequiredError is thrown when an operation needs a wallet account but none was bound at construction. Both support the SDA protocol's capability-by-behavior model (PR #46 review). --- index.js | 7 ++++++- src/errors.js | 29 +++++++++++++++++++++++++++++ types/index.d.ts | 8 ++++---- types/src/errors.d.ts | 19 +++++++++++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 80f0b0d..d69dc2c 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,11 @@ export { export { IWalletAccount } from './src/wallet-account.js' -export { NotImplementedError, SignerError } from './src/errors.js' +export { + NotImplementedError, + SignerError, + UnsupportedOperationError, + AccountRequiredError +} from './src/errors.js' export { ISigner } from './src/signer.js' diff --git a/src/errors.js b/src/errors.js index 8154485..83b09ff 100644 --- a/src/errors.js +++ b/src/errors.js @@ -38,3 +38,32 @@ export class SignerError extends Error { this.name = 'SignerError' } } + +export class UnsupportedOperationError extends Error { + /** + * Create a new unsupported operation error. Thrown by an optional operation + * that the concrete implementation deliberately does not support, so consumers + * can distinguish "not supported here" from an abstract method left unimplemented. + * + * @param {string} operation - The name of the operation that is not supported. + */ + constructor (operation) { + super(`Operation '${operation}' is not supported by this protocol.`) + + this.name = 'UnsupportedOperationError' + } +} + +export class AccountRequiredError extends Error { + /** + * Create a new account required error. Thrown when an operation needs a wallet + * account to run but none was bound at construction. + * + * @param {string} operation - The name of the operation that requires a wallet account. + */ + constructor (operation) { + super(`Operation '${operation}' requires a wallet account.`) + + this.name = 'AccountRequiredError' + } +} diff --git a/types/index.d.ts b/types/index.d.ts index 8f5f271..ebf5a9e 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,3 +1,6 @@ +export { default } from "./src/wallet-manager.js"; +export { IWalletAccount } from "./src/wallet-account.js"; +export { ISigner } from "./src/signer.js"; export type FeeRates = import("./src/wallet-manager.js").FeeRates; export type WalletConfig = import("./src/wallet-manager.js").WalletConfig; export type Transaction = import("./src/wallet-account-read-only.js").Transaction; @@ -5,8 +8,5 @@ export type TransactionResult = import("./src/wallet-account-read-only.js").Tran export type TransferOptions = import("./src/wallet-account-read-only.js").TransferOptions; export type TransferResult = import("./src/wallet-account-read-only.js").TransferResult; export type KeyPair = import("./src/wallet-account.js").KeyPair; -export { default } from "./src/wallet-manager.js"; export { default as WalletAccountReadOnly, IWalletAccountReadOnly } from "./src/wallet-account-read-only.js"; -export { IWalletAccount } from "./src/wallet-account.js"; -export { NotImplementedError, SignerError } from "./src/errors.js"; -export { ISigner } from "./src/signer.js"; +export { NotImplementedError, SignerError, UnsupportedOperationError, AccountRequiredError } from "./src/errors.js"; diff --git a/types/src/errors.d.ts b/types/src/errors.d.ts index a6ab66e..4347e34 100644 --- a/types/src/errors.d.ts +++ b/types/src/errors.d.ts @@ -14,3 +14,22 @@ export class SignerError extends Error { */ constructor(message: string); } +export class UnsupportedOperationError extends Error { + /** + * Create a new unsupported operation error. Thrown by an optional operation + * that the concrete implementation deliberately does not support, so consumers + * can distinguish "not supported here" from an abstract method left unimplemented. + * + * @param {string} operation - The name of the operation that is not supported. + */ + constructor(operation: string); +} +export class AccountRequiredError extends Error { + /** + * Create a new account required error. Thrown when an operation needs a wallet + * account to run but none was bound at construction. + * + * @param {string} operation - The name of the operation that requires a wallet account. + */ + constructor(operation: string); +} From 3a7d9e07090f1b4f04944480291e7a16f3d7c1c4 Mon Sep 17 00:00:00 2001 From: rickalon Date: Fri, 3 Jul 2026 15:04:14 -0400 Subject: [PATCH 13/27] refactor(protocols): drop SdaCapabilities descriptor for behavior-based capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the `getCapabilities()` method and the `SdaCapabilities` typedef. Capability is now discovered by behavior: an optional operation a provider doesn't support inherits the base and throws UnsupportedOperationError, and the descriptive traits (custody / activation / route-discovery models) are documented on each provider rather than queried. Also drop the `reusableAddresses` trait — reusable vs single-use is a per-request choice via `SdaCreateOptions.reusable`, not a static protocol trait (PR #46 review; Jonathan/Davide). --- src/protocols/index.js | 1 - src/protocols/sda-protocol.js | 235 ++++++++++------------ types/src/protocols/index.d.ts | 1 - types/src/protocols/sda-protocol.d.ts | 268 ++++++++------------------ 4 files changed, 184 insertions(+), 321 deletions(-) diff --git a/src/protocols/index.js b/src/protocols/index.js index b81aab3..0505cd0 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -68,7 +68,6 @@ /** @typedef {import('./swidge-protocol.js').SwidgeSupportedToken} SwidgeSupportedToken */ /** @typedef {import('./swidge-protocol.js').SwidgeSupportedTokensOptions} SwidgeSupportedTokensOptions */ -/** @typedef {import('./sda-protocol.js').SdaCapabilities} SdaCapabilities */ /** @typedef {import('./sda-protocol.js').SdaRecoveryMode} SdaRecoveryMode */ /** @typedef {import('./sda-protocol.js').SdaRouteDiscoveryMode} SdaRouteDiscoveryMode */ /** @typedef {import('./sda-protocol.js').SdaCustodyModel} SdaCustodyModel */ diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index a8b8dd3..1b5c2d7 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -13,7 +13,7 @@ // limitations under the License. 'use strict' -import { NotImplementedError } from '../errors.js' +import { NotImplementedError, UnsupportedOperationError } from '../errors.js' /** @typedef {import('../wallet-account-read-only.js').IWalletAccountReadOnly} IWalletAccountReadOnly */ @@ -26,37 +26,13 @@ import { NotImplementedError } from '../errors.js' * @typedef {string | number} Blockchain */ -/** - * Describes which optional parts of the SDA interface a provider implements, so - * consumers can adapt their UI/flow without trial-and-error. The required core - * (`getSupportedRoutes`, `createDepositAddress`) is always available and - * therefore not listed here. - * - * @typedef {Object} SdaCapabilities - * @property {boolean} quoting - Whether {@link ISdaProtocol#quoteDeposit} is supported. - * @property {boolean} quoteRequired - Whether a quote must be fetched (via {@link ISdaProtocol#quoteDeposit}) and passed to {@link ISdaProtocol#createDepositAddress} via `options.quote` before an address can be issued (e.g., Relay, whose deposit address is bound to a quote/amount). - * @property {boolean} reusableAddresses - Whether issued addresses can receive more than one deposit. - * @property {boolean} multiChainAddress - Whether a single address is valid across several source chains of the same VM family. - * @property {SdaCustodyModel} custodyModel - Who controls deposited funds while in flight: `'trusted-operator'` (the provider holds the address and funds) or `'self-custodial'` (the address is an on-chain contract with withdrawal rights fixed in code, so funds are recoverable on-chain without the provider). - * @property {boolean} clientDerivableAddress - Whether the deposit address is deterministically derivable and verifiable client-side, before any provider call (via {@link ISdaProtocol#deriveDepositAddress}). - * @property {SdaActivationModel} activation - The address activation lifecycle: `'none'` (live as soon as it is created), `'required'` (must be activated before the provider monitors it), or `'ttl'` (activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}). - * @property {SdaRouteDiscoveryMode} routeDiscovery - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). - * @property {boolean} getAddress - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. - * @property {boolean} transferStatus - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. - * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. - * @property {boolean} historyByRecipient - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. - * @property {SdaRecoveryMode} recovery - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'` or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). Keep-alive of an expired address is the activation lifecycle (`activation: 'ttl'`), not recovery. - * @property {boolean} disableAddress - Whether {@link ISdaProtocol#disableDepositAddress} is supported. - * @property {boolean} refund - Whether the provider honours a `refundAddress`. - */ - /** * How a provider re-processes a deposit that was not picked up automatically. * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means * no such call is exposed. This is about provider-side reprocessing of a missed * deposit, not about fund custody — whether deposited funds are recoverable is - * governed by {@link SdaCapabilities#custodyModel}. Re-enabling an idle/expired - * address is the activation lifecycle ({@link SdaActivationModel} `'ttl'` + + * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the + * activation lifecycle ({@link SdaActivationModel} `'ttl'` + * {@link ISdaProtocol#renewDepositAddress}), not recovery. * * @typedef {'reindex' | 'none'} SdaRecoveryMode @@ -194,7 +170,8 @@ import { NotImplementedError } from '../errors.js' * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). + * @property {boolean} [reusable] - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. */ @@ -214,7 +191,8 @@ import { NotImplementedError } from '../errors.js' * @property {SdaLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. */ + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, for a provider whose activation model ({@link SdaActivationModel}) is `'ttl'`. + */ /** * The lifecycle status of a deposit/transfer through an SDA. @@ -281,54 +259,49 @@ import { NotImplementedError } from '../errors.js' * destination chain and address. * * The required core every provider implements is route discovery and address - * creation. Everything else is optional and advertised through - * {@link ISdaProtocol#getCapabilities}. + * creation. Every other operation is optional: a provider that does not support + * one leaves the base implementation in place, which throws + * {@link UnsupportedOperationError}. Descriptive traits that are not tied to a + * single method (custody, activation and route-discovery models) are documented + * on each provider — see {@link SdaCustodyModel}, {@link SdaActivationModel} and + * {@link SdaRouteDiscoveryMode}. * * @interface */ export class ISdaProtocol { - /** - * Returns which optional parts of the interface this provider implements. - * - * @returns {SdaCapabilities} The provider's capabilities. - */ - getCapabilities () { - throw new NotImplementedError('getCapabilities()') - } - /** * Lists the conversion routes the provider supports: source chains, accepted - * input tokens, destination assets and per-route deposit limits. When - * {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'`, `sourceChain` - * and `destinationChain` must be supplied. + * input tokens, destination assets and per-route deposit limits. A provider + * whose route discovery is `'by-chain-pair'` ({@link SdaRouteDiscoveryMode}) + * requires `sourceChain` and `destinationChain` to be supplied. * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {Error} If {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + * @throws {Error} If the provider's route discovery is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. */ async getSupportedRoutes (options) { throw new NotImplementedError('getSupportedRoutes(options)') } /** - * Fetches a non-binding quote for a deposit. Optional: only supported when - * {@link SdaCapabilities#quoting} is `true`, and required up front when - * {@link SdaCapabilities#quoteRequired} is `true`. + * Fetches a non-binding quote for a deposit. Optional: only supported by + * providers that offer quoting. Some providers bind the deposit address to a + * quote and require it up front — see that provider's documentation. * * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. - * @throws {NotImplementedError} If this provider does not support quoting (check `getCapabilities().quoting`). + * @throws {UnsupportedOperationError} If this provider does not support quoting. */ async quoteDeposit (options) { - throw new NotImplementedError('quoteDeposit(options)') + throw new UnsupportedOperationError('quoteDeposit(options)') } /** * Creates deposit addresses for the given route and destination, ready to - * receive per the provider's {@link SdaCapabilities#activation} model (for a - * `'required'` / `'ttl'` provider this also activates the address so it is - * monitored). Returns one entry per distinct address: a provider that issues a - * single address across a chain family returns one entry covering all of + * receive per the provider's activation model ({@link SdaActivationModel}) — + * for a `'required'` / `'ttl'` provider this also activates the address so it + * is monitored. Returns one entry per distinct address: a provider that issues + * a single address across a chain family returns one entry covering all of * `sourceChains`, while a provider that issues one address per source chain * returns one entry each. * @@ -343,109 +316,109 @@ export class ISdaProtocol { * Derives a deposit address client-side, without any provider call and * without activating or monitoring it — used to verify (derive + compare) or * recover an address for a self-custodial provider. Optional: only supported - * when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * by providers whose deposit address is client-derivable. * * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. - * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). + * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. */ async deriveDepositAddress (options) { - throw new NotImplementedError('deriveDepositAddress(options)') + throw new UnsupportedOperationError('deriveDepositAddress(options)') } /** * Looks up an existing deposit address by its identifier — the * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, - * which round-trips any chain context the provider needs. Optional: - * only supported when {@link SdaCapabilities#getAddress} is `true`. + * which round-trips any chain context the provider needs. Optional: only + * supported by providers that expose address lookup. * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} The deposit address descriptor. - * @throws {NotImplementedError} If this provider does not support address lookup (check `getCapabilities().getAddress`). + * @throws {UnsupportedOperationError} If this provider does not support address lookup. * @throws {Error} If no such address exists. */ async getDepositAddress (id) { - throw new NotImplementedError('getDepositAddress(id)') + throw new UnsupportedOperationError('getDepositAddress(id)') } /** * Refreshes the activation of a deposit address so the provider keeps - * monitoring it. Optional: only relevant when {@link SdaCapabilities#activation} - * is `'ttl'`. + * monitoring it. Optional: only relevant for providers whose activation model + * ({@link SdaActivationModel}) is `'ttl'`. * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). - * @throws {NotImplementedError} If this provider does not use activation TTLs (check `getCapabilities().activation`). + * @throws {UnsupportedOperationError} If this provider does not use activation TTLs. */ async renewDepositAddress (id) { - throw new NotImplementedError('renewDepositAddress(id)') + throw new UnsupportedOperationError('renewDepositAddress(id)') } /** * Lists the deposits observed at a deposit address. Optional: only supported - * when {@link SdaCapabilities#historyByAddress} is `true`. + * by providers that expose pull-based history keyed by deposit address. * * @param {string} address - The deposit address to list transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). * @returns {Promise} The transfers for the address. - * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). + * @throws {UnsupportedOperationError} If this provider does not support pull-based history. */ async getTransfers (address, options) { - throw new NotImplementedError('getTransfers(address, options)') + throw new UnsupportedOperationError('getTransfers(address, options)') } /** * Lists transfers aggregated by recipient — every deposit routed to the given * recipient across all of that recipient's deposit addresses and source - * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} - * is `true`. + * chains. Optional: only supported by providers that expose recipient-keyed + * history. * * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. - * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). + * @throws {UnsupportedOperationError} If this provider does not support recipient-keyed history. */ async getTransfersByRecipient (destinationChain, recipient, options) { - throw new NotImplementedError('getTransfersByRecipient(destinationChain, recipient, options)') + throw new UnsupportedOperationError('getTransfersByRecipient(destinationChain, recipient, options)') } /** - * Retrieves the status of a single transfer by its identifier. Optional: - * only supported when {@link SdaCapabilities#transferStatus} is `true`. + * Retrieves the status of a single transfer by its identifier. Optional: only + * supported by providers that expose status-by-transfer-id. * * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. - * @throws {NotImplementedError} If this provider does not support transfer-status lookup (check `getCapabilities().transferStatus`). + * @throws {UnsupportedOperationError} If this provider does not support transfer-status lookup. * @throws {Error} If no such transfer exists. */ async getTransferStatus (id) { - throw new NotImplementedError('getTransferStatus(id)') + throw new UnsupportedOperationError('getTransferStatus(id)') } /** * Recovers a deposit or address that was not picked up automatically, using - * the provider's recovery mode (see {@link SdaCapabilities#recovery}). - * Optional: only supported when `recovery` is not `'none'`. + * the provider's recovery mode ({@link SdaRecoveryMode}). Optional: only + * supported by providers whose recovery mode is not `'none'`. * * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. - * @throws {NotImplementedError} If this provider does not support recovery (check `getCapabilities().recovery`). + * @throws {UnsupportedOperationError} If this provider does not support recovery. */ async recoverDepositAddress (options) { - throw new NotImplementedError('recoverDepositAddress(options)') + throw new UnsupportedOperationError('recoverDepositAddress(options)') } /** - * Disables a deposit address so it no longer accepts deposits. Optional: - * only supported when {@link SdaCapabilities#disableAddress} is `true`. + * Disables a deposit address so it no longer accepts deposits. Optional: only + * supported by providers that allow disabling an address. * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} Resolves once the address has been disabled. - * @throws {NotImplementedError} If this provider does not support disabling addresses (check `getCapabilities().disableAddress`). + * @throws {UnsupportedOperationError} If this provider does not support disabling addresses. */ async disableDepositAddress (id) { - throw new NotImplementedError('disableDepositAddress(id)') + throw new UnsupportedOperationError('disableDepositAddress(id)') } } @@ -488,51 +461,41 @@ export default class SdaProtocol { this._account = account } - /** - * Returns which optional parts of the interface this provider implements. - * - * @abstract - * @returns {SdaCapabilities} The provider's capabilities. - */ - getCapabilities () { - throw new NotImplementedError('getCapabilities()') - } - /** * Lists the conversion routes the provider supports: source chains, accepted - * input tokens, destination assets and per-route deposit limits. When - * {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'`, `sourceChain` - * and `destinationChain` must be supplied. + * input tokens, destination assets and per-route deposit limits. A provider + * whose route discovery is `'by-chain-pair'` ({@link SdaRouteDiscoveryMode}) + * requires `sourceChain` and `destinationChain` to be supplied. * * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {Error} If {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + * @throws {Error} If the provider's route discovery is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. */ async getSupportedRoutes (options) { throw new NotImplementedError('getSupportedRoutes(options)') } /** - * Fetches a non-binding quote for a deposit. Optional: only supported when - * {@link SdaCapabilities#quoting} is `true`, and required up front when - * {@link SdaCapabilities#quoteRequired} is `true`. + * Fetches a non-binding quote for a deposit. Optional: only supported by + * providers that offer quoting. Some providers bind the deposit address to a + * quote and require it up front — see that provider's documentation. * * @abstract * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. - * @throws {NotImplementedError} If this provider does not support quoting (check `getCapabilities().quoting`). + * @throws {UnsupportedOperationError} If this provider does not support quoting. */ async quoteDeposit (options) { - throw new NotImplementedError('quoteDeposit(options)') + throw new UnsupportedOperationError('quoteDeposit(options)') } /** * Creates deposit addresses for the given route and destination, ready to - * receive per the provider's {@link SdaCapabilities#activation} model (for a - * `'required'` / `'ttl'` provider this also activates the address so it is - * monitored). Returns one entry per distinct address: a provider that issues a - * single address across a chain family returns one entry covering all of + * receive per the provider's activation model ({@link SdaActivationModel}) — + * for a `'required'` / `'ttl'` provider this also activates the address so it + * is monitored. Returns one entry per distinct address: a provider that issues + * a single address across a chain family returns one entry covering all of * `sourceChains`, while a provider that issues one address per source chain * returns one entry each. * @@ -548,116 +511,116 @@ export default class SdaProtocol { * Derives a deposit address client-side, without any provider call and * without activating or monitoring it — used to verify (derive + compare) or * recover an address for a self-custodial provider. Optional: only supported - * when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * by providers whose deposit address is client-derivable. * * @abstract * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. - * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). + * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. */ async deriveDepositAddress (options) { - throw new NotImplementedError('deriveDepositAddress(options)') + throw new UnsupportedOperationError('deriveDepositAddress(options)') } /** * Looks up an existing deposit address by its identifier — the * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, - * which round-trips any chain context the provider needs. Optional: - * only supported when {@link SdaCapabilities#getAddress} is `true`. + * which round-trips any chain context the provider needs. Optional: only + * supported by providers that expose address lookup. * * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} The deposit address descriptor. - * @throws {NotImplementedError} If this provider does not support address lookup (check `getCapabilities().getAddress`). + * @throws {UnsupportedOperationError} If this provider does not support address lookup. * @throws {Error} If no such address exists. */ async getDepositAddress (id) { - throw new NotImplementedError('getDepositAddress(id)') + throw new UnsupportedOperationError('getDepositAddress(id)') } /** * Refreshes the activation of a deposit address so the provider keeps - * monitoring it. Optional: only relevant when {@link SdaCapabilities#activation} - * is `'ttl'`. + * monitoring it. Optional: only relevant for providers whose activation model + * ({@link SdaActivationModel}) is `'ttl'`. * * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). - * @throws {NotImplementedError} If this provider does not use activation TTLs (check `getCapabilities().activation`). + * @throws {UnsupportedOperationError} If this provider does not use activation TTLs. */ async renewDepositAddress (id) { - throw new NotImplementedError('renewDepositAddress(id)') + throw new UnsupportedOperationError('renewDepositAddress(id)') } /** * Lists the deposits observed at a deposit address. Optional: only supported - * when {@link SdaCapabilities#historyByAddress} is `true`. + * by providers that expose pull-based history keyed by deposit address. * * @abstract * @param {string} address - The deposit address to list transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). * @returns {Promise} The transfers for the address. - * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). + * @throws {UnsupportedOperationError} If this provider does not support pull-based history. */ async getTransfers (address, options) { - throw new NotImplementedError('getTransfers(address, options)') + throw new UnsupportedOperationError('getTransfers(address, options)') } /** * Lists transfers aggregated by recipient — every deposit routed to the given * recipient across all of that recipient's deposit addresses and source - * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} - * is `true`. + * chains. Optional: only supported by providers that expose recipient-keyed + * history. * * @abstract * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. - * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). + * @throws {UnsupportedOperationError} If this provider does not support recipient-keyed history. */ async getTransfersByRecipient (destinationChain, recipient, options) { - throw new NotImplementedError('getTransfersByRecipient(destinationChain, recipient, options)') + throw new UnsupportedOperationError('getTransfersByRecipient(destinationChain, recipient, options)') } /** - * Retrieves the status of a single transfer by its identifier. Optional: - * only supported when {@link SdaCapabilities#transferStatus} is `true`. + * Retrieves the status of a single transfer by its identifier. Optional: only + * supported by providers that expose status-by-transfer-id. * * @abstract * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. - * @throws {NotImplementedError} If this provider does not support transfer-status lookup (check `getCapabilities().transferStatus`). + * @throws {UnsupportedOperationError} If this provider does not support transfer-status lookup. * @throws {Error} If no such transfer exists. */ async getTransferStatus (id) { - throw new NotImplementedError('getTransferStatus(id)') + throw new UnsupportedOperationError('getTransferStatus(id)') } /** * Recovers a deposit or address that was not picked up automatically, using - * the provider's recovery mode (see {@link SdaCapabilities#recovery}). - * Optional: only supported when `recovery` is not `'none'`. + * the provider's recovery mode ({@link SdaRecoveryMode}). Optional: only + * supported by providers whose recovery mode is not `'none'`. * * @abstract * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. - * @throws {NotImplementedError} If this provider does not support recovery (check `getCapabilities().recovery`). + * @throws {UnsupportedOperationError} If this provider does not support recovery. */ async recoverDepositAddress (options) { - throw new NotImplementedError('recoverDepositAddress(options)') + throw new UnsupportedOperationError('recoverDepositAddress(options)') } /** - * Disables a deposit address so it no longer accepts deposits. Optional: - * only supported when {@link SdaCapabilities#disableAddress} is `true`. + * Disables a deposit address so it no longer accepts deposits. Optional: only + * supported by providers that allow disabling an address. * * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} Resolves once the address has been disabled. - * @throws {NotImplementedError} If this provider does not support disabling addresses (check `getCapabilities().disableAddress`). + * @throws {UnsupportedOperationError} If this provider does not support disabling addresses. */ async disableDepositAddress (id) { - throw new NotImplementedError('disableDepositAddress(id)') + throw new UnsupportedOperationError('disableDepositAddress(id)') } } diff --git a/types/src/protocols/index.d.ts b/types/src/protocols/index.d.ts index c235722..8c1f7f5 100644 --- a/types/src/protocols/index.d.ts +++ b/types/src/protocols/index.d.ts @@ -44,7 +44,6 @@ export type SwidgeStatusResult = import("./swidge-protocol.js").SwidgeStatusResu export type SwidgeSupportedChain = import("./swidge-protocol.js").SwidgeSupportedChain; export type SwidgeSupportedToken = import("./swidge-protocol.js").SwidgeSupportedToken; export type SwidgeSupportedTokensOptions = import("./swidge-protocol.js").SwidgeSupportedTokensOptions; -export type SdaCapabilities = import("./sda-protocol.js").SdaCapabilities; export type SdaRecoveryMode = import("./sda-protocol.js").SdaRecoveryMode; export type SdaRouteDiscoveryMode = import("./sda-protocol.js").SdaRouteDiscoveryMode; export type SdaCustodyModel = import("./sda-protocol.js").SdaCustodyModel; diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index d11acdd..cd62e8a 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -6,36 +6,13 @@ * * @typedef {string | number} Blockchain */ -/** - * Describes which optional parts of the SDA interface a provider implements, so - * consumers can adapt their UI/flow without trial-and-error. The required core - * (`getSupportedRoutes`, `createDepositAddress`) is always available and - * therefore not listed here. - * - * @typedef {Object} SdaCapabilities - * @property {boolean} quoting - Whether {@link ISdaProtocol#quoteDeposit} is supported. - * @property {boolean} quoteRequired - Whether a quote must be fetched (via {@link ISdaProtocol#quoteDeposit}) and passed to {@link ISdaProtocol#createDepositAddress} via `options.quote` before an address can be issued (e.g., Relay, whose deposit address is bound to a quote/amount). - * @property {boolean} reusableAddresses - Whether issued addresses can receive more than one deposit. - * @property {boolean} multiChainAddress - Whether a single address is valid across several source chains of the same VM family. - * @property {SdaCustodyModel} custodyModel - Who controls deposited funds while in flight: `'trusted-operator'` (the provider holds the address and funds) or `'self-custodial'` (the address is an on-chain contract with withdrawal rights fixed in code, so funds are recoverable on-chain without the provider). - * @property {boolean} clientDerivableAddress - Whether the deposit address is deterministically derivable and verifiable client-side, before any provider call (via {@link ISdaProtocol#deriveDepositAddress}). - * @property {SdaActivationModel} activation - The address activation lifecycle: `'none'` (live as soon as it is created), `'required'` (must be activated before the provider monitors it), or `'ttl'` (activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}). - * @property {SdaRouteDiscoveryMode} routeDiscovery - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). - * @property {boolean} getAddress - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. - * @property {boolean} transferStatus - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. - * @property {boolean} historyByAddress - Whether {@link ISdaProtocol#getTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. - * @property {boolean} historyByRecipient - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. - * @property {SdaRecoveryMode} recovery - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'` or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). Keep-alive of an expired address is the activation lifecycle (`activation: 'ttl'`), not recovery. - * @property {boolean} disableAddress - Whether {@link ISdaProtocol#disableDepositAddress} is supported. - * @property {boolean} refund - Whether the provider honours a `refundAddress`. - */ /** * How a provider re-processes a deposit that was not picked up automatically. * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means * no such call is exposed. This is about provider-side reprocessing of a missed * deposit, not about fund custody — whether deposited funds are recoverable is - * governed by {@link SdaCapabilities#custodyModel}. Re-enabling an idle/expired - * address is the activation lifecycle ({@link SdaActivationModel} `'ttl'` + + * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the + * activation lifecycle ({@link SdaActivationModel} `'ttl'` + * {@link ISdaProtocol#renewDepositAddress}), not recovery. * * @typedef {'reindex' | 'none'} SdaRecoveryMode @@ -161,7 +138,8 @@ * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). * @property {boolean} [reusable] - Request a reusable address (when the provider supports both modes). + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). + * @property {boolean} [reusable] - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. */ /** @@ -180,7 +158,8 @@ * @property {SdaLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. */ + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, for a provider whose activation model ({@link SdaActivationModel}) is `'ttl'`. + */ /** * The lifecycle status of a deposit/transfer through an SDA. * @@ -241,45 +220,43 @@ * destination chain and address. * * The required core every provider implements is route discovery and address - * creation. Everything else is optional and advertised through - * {@link ISdaProtocol#getCapabilities}. + * creation. Every other operation is optional: a provider that does not support + * one leaves the base implementation in place, which throws + * {@link UnsupportedOperationError}. Descriptive traits that are not tied to a + * single method (custody, activation and route-discovery models) are documented + * on each provider — see {@link SdaCustodyModel}, {@link SdaActivationModel} and + * {@link SdaRouteDiscoveryMode}. * * @interface */ export class ISdaProtocol { - /** - * Returns which optional parts of the interface this provider implements. - * - * @returns {SdaCapabilities} The provider's capabilities. - */ - getCapabilities(): SdaCapabilities; /** * Lists the conversion routes the provider supports: source chains, accepted - * input tokens, destination assets and per-route deposit limits. When - * {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'`, `sourceChain` - * and `destinationChain` must be supplied. + * input tokens, destination assets and per-route deposit limits. A provider + * whose route discovery is `'by-chain-pair'` ({@link SdaRouteDiscoveryMode}) + * requires `sourceChain` and `destinationChain` to be supplied. * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {Error} If {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + * @throws {Error} If the provider's route discovery is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** - * Fetches a non-binding quote for a deposit. Optional: only supported when - * {@link SdaCapabilities#quoting} is `true`, and required up front when - * {@link SdaCapabilities#quoteRequired} is `true`. + * Fetches a non-binding quote for a deposit. Optional: only supported by + * providers that offer quoting. Some providers bind the deposit address to a + * quote and require it up front — see that provider's documentation. * * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. - * @throws {NotImplementedError} If this provider does not support quoting (check `getCapabilities().quoting`). + * @throws {UnsupportedOperationError} If this provider does not support quoting. */ quoteDeposit(options: SdaQuoteOptions): Promise; /** * Creates deposit addresses for the given route and destination, ready to - * receive per the provider's {@link SdaCapabilities#activation} model (for a - * `'required'` / `'ttl'` provider this also activates the address so it is - * monitored). Returns one entry per distinct address: a provider that issues a - * single address across a chain family returns one entry covering all of + * receive per the provider's activation model ({@link SdaActivationModel}) — + * for a `'required'` / `'ttl'` provider this also activates the address so it + * is monitored. Returns one entry per distinct address: a provider that issues + * a single address across a chain family returns one entry covering all of * `sourceChains`, while a provider that issues one address per source chain * returns one entry each. * @@ -291,85 +268,85 @@ export class ISdaProtocol { * Derives a deposit address client-side, without any provider call and * without activating or monitoring it — used to verify (derive + compare) or * recover an address for a self-custodial provider. Optional: only supported - * when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * by providers whose deposit address is client-derivable. * * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. - * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). + * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. */ deriveDepositAddress(options: SdaCreateOptions): Promise; /** * Looks up an existing deposit address by its identifier — the * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, - * which round-trips any chain context the provider needs. Optional: - * only supported when {@link SdaCapabilities#getAddress} is `true`. + * which round-trips any chain context the provider needs. Optional: only + * supported by providers that expose address lookup. * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} The deposit address descriptor. - * @throws {NotImplementedError} If this provider does not support address lookup (check `getCapabilities().getAddress`). + * @throws {UnsupportedOperationError} If this provider does not support address lookup. * @throws {Error} If no such address exists. */ getDepositAddress(id: string): Promise; /** * Refreshes the activation of a deposit address so the provider keeps - * monitoring it. Optional: only relevant when {@link SdaCapabilities#activation} - * is `'ttl'`. + * monitoring it. Optional: only relevant for providers whose activation model + * ({@link SdaActivationModel}) is `'ttl'`. * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). - * @throws {NotImplementedError} If this provider does not use activation TTLs (check `getCapabilities().activation`). + * @throws {UnsupportedOperationError} If this provider does not use activation TTLs. */ renewDepositAddress(id: string): Promise; /** * Lists the deposits observed at a deposit address. Optional: only supported - * when {@link SdaCapabilities#historyByAddress} is `true`. + * by providers that expose pull-based history keyed by deposit address. * * @param {string} address - The deposit address to list transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). * @returns {Promise} The transfers for the address. - * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). + * @throws {UnsupportedOperationError} If this provider does not support pull-based history. */ getTransfers(address: string, options?: SdaTransfersOptions): Promise; /** * Lists transfers aggregated by recipient — every deposit routed to the given * recipient across all of that recipient's deposit addresses and source - * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} - * is `true`. + * chains. Optional: only supported by providers that expose recipient-keyed + * history. * * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. - * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). + * @throws {UnsupportedOperationError} If this provider does not support recipient-keyed history. */ getTransfersByRecipient(destinationChain: Blockchain, recipient: string, options?: SdaTransfersOptions): Promise; /** - * Retrieves the status of a single transfer by its identifier. Optional: - * only supported when {@link SdaCapabilities#transferStatus} is `true`. + * Retrieves the status of a single transfer by its identifier. Optional: only + * supported by providers that expose status-by-transfer-id. * * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. - * @throws {NotImplementedError} If this provider does not support transfer-status lookup (check `getCapabilities().transferStatus`). + * @throws {UnsupportedOperationError} If this provider does not support transfer-status lookup. * @throws {Error} If no such transfer exists. */ getTransferStatus(id: string): Promise; /** * Recovers a deposit or address that was not picked up automatically, using - * the provider's recovery mode (see {@link SdaCapabilities#recovery}). - * Optional: only supported when `recovery` is not `'none'`. + * the provider's recovery mode ({@link SdaRecoveryMode}). Optional: only + * supported by providers whose recovery mode is not `'none'`. * * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. - * @throws {NotImplementedError} If this provider does not support recovery (check `getCapabilities().recovery`). + * @throws {UnsupportedOperationError} If this provider does not support recovery. */ recoverDepositAddress(options: SdaRecoveryOptions): Promise; /** - * Disables a deposit address so it no longer accepts deposits. Optional: - * only supported when {@link SdaCapabilities#disableAddress} is `true`. + * Disables a deposit address so it no longer accepts deposits. Optional: only + * supported by providers that allow disabling an address. * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} Resolves once the address has been disabled. - * @throws {NotImplementedError} If this provider does not support disabling addresses (check `getCapabilities().disableAddress`). + * @throws {UnsupportedOperationError} If this provider does not support disabling addresses. */ disableDepositAddress(id: string): Promise; } @@ -410,42 +387,35 @@ export default class SdaProtocol implements ISdaProtocol { * @type {IWalletAccountReadOnly | IWalletAccount | undefined} */ protected _account: IWalletAccountReadOnly | IWalletAccount | undefined; - /** - * Returns which optional parts of the interface this provider implements. - * - * @abstract - * @returns {SdaCapabilities} The provider's capabilities. - */ - getCapabilities(): SdaCapabilities; /** * Lists the conversion routes the provider supports: source chains, accepted - * input tokens, destination assets and per-route deposit limits. When - * {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'`, `sourceChain` - * and `destinationChain` must be supplied. + * input tokens, destination assets and per-route deposit limits. A provider + * whose route discovery is `'by-chain-pair'` ({@link SdaRouteDiscoveryMode}) + * requires `sourceChain` and `destinationChain` to be supplied. * * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {Error} If {@link SdaCapabilities#routeDiscovery} is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + * @throws {Error} If the provider's route discovery is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** - * Fetches a non-binding quote for a deposit. Optional: only supported when - * {@link SdaCapabilities#quoting} is `true`, and required up front when - * {@link SdaCapabilities#quoteRequired} is `true`. + * Fetches a non-binding quote for a deposit. Optional: only supported by + * providers that offer quoting. Some providers bind the deposit address to a + * quote and require it up front — see that provider's documentation. * * @abstract * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. - * @throws {NotImplementedError} If this provider does not support quoting (check `getCapabilities().quoting`). + * @throws {UnsupportedOperationError} If this provider does not support quoting. */ quoteDeposit(options: SdaQuoteOptions): Promise; /** * Creates deposit addresses for the given route and destination, ready to - * receive per the provider's {@link SdaCapabilities#activation} model (for a - * `'required'` / `'ttl'` provider this also activates the address so it is - * monitored). Returns one entry per distinct address: a provider that issues a - * single address across a chain family returns one entry covering all of + * receive per the provider's activation model ({@link SdaActivationModel}) — + * for a `'required'` / `'ttl'` provider this also activates the address so it + * is monitored. Returns one entry per distinct address: a provider that issues + * a single address across a chain family returns one entry covering all of * `sourceChains`, while a provider that issues one address per source chain * returns one entry each. * @@ -458,93 +428,93 @@ export default class SdaProtocol implements ISdaProtocol { * Derives a deposit address client-side, without any provider call and * without activating or monitoring it — used to verify (derive + compare) or * recover an address for a self-custodial provider. Optional: only supported - * when {@link SdaCapabilities#clientDerivableAddress} is `true`. + * by providers whose deposit address is client-derivable. * * @abstract * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. - * @throws {NotImplementedError} If this provider does not support client-side derivation (check `getCapabilities().clientDerivableAddress`). + * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. */ deriveDepositAddress(options: SdaCreateOptions): Promise; /** * Looks up an existing deposit address by its identifier — the * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, - * which round-trips any chain context the provider needs. Optional: - * only supported when {@link SdaCapabilities#getAddress} is `true`. + * which round-trips any chain context the provider needs. Optional: only + * supported by providers that expose address lookup. * * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} The deposit address descriptor. - * @throws {NotImplementedError} If this provider does not support address lookup (check `getCapabilities().getAddress`). + * @throws {UnsupportedOperationError} If this provider does not support address lookup. * @throws {Error} If no such address exists. */ getDepositAddress(id: string): Promise; /** * Refreshes the activation of a deposit address so the provider keeps - * monitoring it. Optional: only relevant when {@link SdaCapabilities#activation} - * is `'ttl'`. + * monitoring it. Optional: only relevant for providers whose activation model + * ({@link SdaActivationModel}) is `'ttl'`. * * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). - * @throws {NotImplementedError} If this provider does not use activation TTLs (check `getCapabilities().activation`). + * @throws {UnsupportedOperationError} If this provider does not use activation TTLs. */ renewDepositAddress(id: string): Promise; /** * Lists the deposits observed at a deposit address. Optional: only supported - * when {@link SdaCapabilities#historyByAddress} is `true`. + * by providers that expose pull-based history keyed by deposit address. * * @abstract * @param {string} address - The deposit address to list transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). * @returns {Promise} The transfers for the address. - * @throws {NotImplementedError} If this provider does not support pull-based history (check `getCapabilities().historyByAddress`). + * @throws {UnsupportedOperationError} If this provider does not support pull-based history. */ getTransfers(address: string, options?: SdaTransfersOptions): Promise; /** * Lists transfers aggregated by recipient — every deposit routed to the given * recipient across all of that recipient's deposit addresses and source - * chains. Optional: only supported when {@link SdaCapabilities#historyByRecipient} - * is `true`. + * chains. Optional: only supported by providers that expose recipient-keyed + * history. * * @abstract * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. - * @throws {NotImplementedError} If this provider does not support recipient-keyed history (check `getCapabilities().historyByRecipient`). + * @throws {UnsupportedOperationError} If this provider does not support recipient-keyed history. */ getTransfersByRecipient(destinationChain: Blockchain, recipient: string, options?: SdaTransfersOptions): Promise; /** - * Retrieves the status of a single transfer by its identifier. Optional: - * only supported when {@link SdaCapabilities#transferStatus} is `true`. + * Retrieves the status of a single transfer by its identifier. Optional: only + * supported by providers that expose status-by-transfer-id. * * @abstract * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. - * @throws {NotImplementedError} If this provider does not support transfer-status lookup (check `getCapabilities().transferStatus`). + * @throws {UnsupportedOperationError} If this provider does not support transfer-status lookup. * @throws {Error} If no such transfer exists. */ getTransferStatus(id: string): Promise; /** * Recovers a deposit or address that was not picked up automatically, using - * the provider's recovery mode (see {@link SdaCapabilities#recovery}). - * Optional: only supported when `recovery` is not `'none'`. + * the provider's recovery mode ({@link SdaRecoveryMode}). Optional: only + * supported by providers whose recovery mode is not `'none'`. * * @abstract * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. - * @throws {NotImplementedError} If this provider does not support recovery (check `getCapabilities().recovery`). + * @throws {UnsupportedOperationError} If this provider does not support recovery. */ recoverDepositAddress(options: SdaRecoveryOptions): Promise; /** - * Disables a deposit address so it no longer accepts deposits. Optional: - * only supported when {@link SdaCapabilities#disableAddress} is `true`. + * Disables a deposit address so it no longer accepts deposits. Optional: only + * supported by providers that allow disabling an address. * * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} Resolves once the address has been disabled. - * @throws {NotImplementedError} If this provider does not support disabling addresses (check `getCapabilities().disableAddress`). + * @throws {UnsupportedOperationError} If this provider does not support disabling addresses. */ disableDepositAddress(id: string): Promise; } @@ -555,81 +525,13 @@ export type IWalletAccount = import("../wallet-account.js").IWalletAccount; * chain name (e.g. `'ethereum'`). */ export type Blockchain = string | number; -/** - * Describes which optional parts of the SDA interface a provider implements, so - * consumers can adapt their UI/flow without trial-and-error. The required core - * (`getSupportedRoutes`, `createDepositAddress`) is always available and - * therefore not listed here. - */ -export type SdaCapabilities = { - /** - * - Whether {@link ISdaProtocol#quoteDeposit} is supported. - */ - quoting: boolean; - /** - * - Whether a quote must be fetched (via {@link ISdaProtocol#quoteDeposit}) and passed to {@link ISdaProtocol#createDepositAddress} via `options.quote` before an address can be issued (e.g., Relay, whose deposit address is bound to a quote/amount). - */ - quoteRequired: boolean; - /** - * - Whether issued addresses can receive more than one deposit. - */ - reusableAddresses: boolean; - /** - * - Whether a single address is valid across several source chains of the same VM family. - */ - multiChainAddress: boolean; - /** - * - Who controls deposited funds while in flight: `'trusted-operator'` (the provider holds the address and funds) or `'self-custodial'` (the address is an on-chain contract with withdrawal rights fixed in code, so funds are recoverable on-chain without the provider). - */ - custodyModel: SdaCustodyModel; - /** - * - Whether the deposit address is deterministically derivable and verifiable client-side, before any provider call (via {@link ISdaProtocol#deriveDepositAddress}). - */ - clientDerivableAddress: boolean; - /** - * - The address activation lifecycle: `'none'` (live as soon as it is created), `'required'` (must be activated before the provider monitors it), or `'ttl'` (activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}). - */ - activation: SdaActivationModel; - /** - * - How routes can be listed: `'full'` (all routes returnable with no filters) or `'by-chain-pair'` (a source and destination chain must be supplied to {@link ISdaProtocol#getSupportedRoutes}). - */ - routeDiscovery: SdaRouteDiscoveryMode; - /** - * - Whether {@link ISdaProtocol#getDepositAddress} (look up an existing SDA) is supported. - */ - getAddress: boolean; - /** - * - Whether {@link ISdaProtocol#getTransferStatus} (status by transfer id) is supported. - */ - transferStatus: boolean; - /** - * - Whether {@link ISdaProtocol#getTransfers} (pull-based deposit history, keyed by deposit address) is supported. Push-only providers (webhook-based) report `false`. - */ - historyByAddress: boolean; - /** - * - Whether {@link ISdaProtocol#getTransfersByRecipient} (history aggregated by recipient across all of that user's deposit addresses and source chains) is supported. - */ - historyByRecipient: boolean; - /** - * - How the provider re-processes a missed/undetected deposit (see {@link ISdaProtocol#recoverDepositAddress}): `'reindex'` or `'none'`. This describes provider-side reprocessing only — it does NOT say whether deposited funds can be recovered; that is governed by {@link SdaCapabilities#custodyModel} (a `'self-custodial'` provider's funds are recoverable on-chain even when `recovery` is `'none'`). Keep-alive of an expired address is the activation lifecycle (`activation: 'ttl'`), not recovery. - */ - recovery: SdaRecoveryMode; - /** - * - Whether {@link ISdaProtocol#disableDepositAddress} is supported. - */ - disableAddress: boolean; - /** - * - Whether the provider honours a `refundAddress`. - */ - refund: boolean; -}; /** * How a provider re-processes a deposit that was not picked up automatically. * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means * no such call is exposed. This is about provider-side reprocessing of a missed * deposit, not about fund custody — whether deposited funds are recoverable is - * governed by {@link SdaCapabilities#custodyModel}. Re-enabling an idle/expired - * address is the activation lifecycle ({@link SdaActivationModel} `'ttl'` + + * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the + * activation lifecycle ({@link SdaActivationModel} `'ttl'` + * {@link ISdaProtocol#renewDepositAddress}), not recovery. */ export type SdaRecoveryMode = "reindex" | "none"; @@ -886,11 +788,11 @@ export type SdaCreateOptions = { */ inputToken?: string; /** - * - The address that receives refunds if a deposit cannot be processed (push-refund style). * + * - The address that receives refunds if a deposit cannot be processed (push-refund style). */ refundAddress?: string; /** - * - Request a reusable address (when the provider supports both modes). + * - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. */ reusable?: boolean; /** @@ -948,7 +850,7 @@ export type SdaDepositAddress = { */ refundAddress?: string; /** - * - Unix timestamp (seconds) at which the address's activation expires, when {@link SdaCapabilities#activation} is `'ttl'`. + * - Unix timestamp (seconds) at which the address's activation expires, for a provider whose activation model ({@link SdaActivationModel}) is `'ttl'`. */ expiry?: number; }; From 8907ba8b80bd6e38afae245f8b66698ef86a4661 Mon Sep 17 00:00:00 2001 From: rickalon Date: Fri, 3 Jul 2026 15:06:25 -0400 Subject: [PATCH 14/27] docs(protocols): document AccountRequiredError on create/derive createDepositAddress and deriveDepositAddress resolve the delivery destination from the bound account when `destinationAddress` is omitted; document that they throw AccountRequiredError when neither is available, so a missing account surfaces a clear error instead of a downstream TypeError (PR #46 review, #15). --- src/protocols/sda-protocol.js | 6 ++++++ types/src/protocols/sda-protocol.d.ts | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 1b5c2d7..4942149 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -19,6 +19,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' /** @typedef {import('../wallet-account.js').IWalletAccount} IWalletAccount */ +/** @typedef {import('../errors.js').AccountRequiredError} AccountRequiredError */ + /** * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific * chain name (e.g. `'ethereum'`). @@ -307,6 +309,7 @@ export class ISdaProtocol { * * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. + * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. */ async createDepositAddress (options) { throw new NotImplementedError('createDepositAddress(options)') @@ -321,6 +324,7 @@ export class ISdaProtocol { * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. + * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. */ async deriveDepositAddress (options) { throw new UnsupportedOperationError('deriveDepositAddress(options)') @@ -502,6 +506,7 @@ export default class SdaProtocol { * @abstract * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. + * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. */ async createDepositAddress (options) { throw new NotImplementedError('createDepositAddress(options)') @@ -517,6 +522,7 @@ export default class SdaProtocol { * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. + * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. */ async deriveDepositAddress (options) { throw new UnsupportedOperationError('deriveDepositAddress(options)') diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index cd62e8a..4adbe90 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -1,5 +1,6 @@ /** @typedef {import('../wallet-account-read-only.js').IWalletAccountReadOnly} IWalletAccountReadOnly */ /** @typedef {import('../wallet-account.js').IWalletAccount} IWalletAccount */ +/** @typedef {import('../errors.js').AccountRequiredError} AccountRequiredError */ /** * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific * chain name (e.g. `'ethereum'`). @@ -262,6 +263,7 @@ export class ISdaProtocol { * * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. + * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. */ createDepositAddress(options: SdaCreateOptions): Promise; /** @@ -273,6 +275,7 @@ export class ISdaProtocol { * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. + * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. */ deriveDepositAddress(options: SdaCreateOptions): Promise; /** @@ -422,6 +425,7 @@ export default class SdaProtocol implements ISdaProtocol { * @abstract * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. + * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. */ createDepositAddress(options: SdaCreateOptions): Promise; /** @@ -434,6 +438,7 @@ export default class SdaProtocol implements ISdaProtocol { * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. + * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. */ deriveDepositAddress(options: SdaCreateOptions): Promise; /** @@ -520,6 +525,7 @@ export default class SdaProtocol implements ISdaProtocol { } export type IWalletAccountReadOnly = import("../wallet-account-read-only.js").IWalletAccountReadOnly; export type IWalletAccount = import("../wallet-account.js").IWalletAccount; +export type AccountRequiredError = import("../errors.js").AccountRequiredError; /** * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific * chain name (e.g. `'ethereum'`). From 6bc203fb03e5840bedf321cf1bbf018ec95e1312 Mon Sep 17 00:00:00 2001 From: rickalon Date: Mon, 6 Jul 2026 07:23:45 -0400 Subject: [PATCH 15/27] refactor(protocols): make SdaRecoveryOptions a union of identifying shapes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recovery previously took an all-optional bag, so recoverDepositAddress({}) type-checked. Model it as SdaRecoverById | SdaRecoverByAddress instead — a caller must identify the deposit by SDA id or by deposit address, and the empty call is now a type error. Drops the unused sourceTxHash field; a provider needing other inputs extends the relevant member (PR #46 review, #13; Davi0k approved). --- src/protocols/index.js | 2 + src/protocols/sda-protocol.js | 27 +++++++++---- types/src/protocols/index.d.ts | 2 + types/src/protocols/sda-protocol.d.ts | 58 ++++++++++++++++++--------- 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/protocols/index.js b/src/protocols/index.js index 0505cd0..e28bac3 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -86,6 +86,8 @@ /** @typedef {import('./sda-protocol.js').SdaTransferStatus} SdaTransferStatus */ /** @typedef {import('./sda-protocol.js').SdaTransfer} SdaTransfer */ /** @typedef {import('./sda-protocol.js').SdaTransfersOptions} SdaTransfersOptions */ +/** @typedef {import('./sda-protocol.js').SdaRecoverById} SdaRecoverById */ +/** @typedef {import('./sda-protocol.js').SdaRecoverByAddress} SdaRecoverByAddress */ /** @typedef {import('./sda-protocol.js').SdaRecoveryOptions} SdaRecoveryOptions */ /** @typedef {import('./sda-protocol.js').SdaRecoveryResult} SdaRecoveryResult */ diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 4942149..6c1bce1 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -231,16 +231,29 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. */ +/** + * Recover a deposit by the SDA identifier. + * + * @typedef {Object} SdaRecoverById + * @property {string} id - The provider SDA identifier (the `SdaDepositAddress.id`). + */ + +/** + * Recover a deposit by its deposit address. + * + * @typedef {Object} SdaRecoverByAddress + * @property {string} address - The deposit address to reindex. + * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by providers that key addresses by (address, chain). + */ + /** * Options for re-processing a deposit that was not picked up automatically - * (`reindex`). Callers supply whatever the provider needs — typically the source - * transaction, or the deposit address / SDA id. + * (`reindex`). A caller identifies the deposit either by SDA id or by its deposit + * address; the union has no empty member, so `recoverDepositAddress({})` is a + * type error. A provider needing extra inputs extends the relevant member on its + * own options type. * - * @typedef {Object} SdaRecoveryOptions - * @property {string} [address] - The deposit address to reindex. - * @property {string} [id] - The provider SDA identifier. - * @property {string} [sourceTxHash] - The deposit transaction to re-index. - * @property {Blockchain} [sourceChain] - The chain of the deposit transaction. + * @typedef {SdaRecoverById | SdaRecoverByAddress} SdaRecoveryOptions */ /** diff --git a/types/src/protocols/index.d.ts b/types/src/protocols/index.d.ts index 8c1f7f5..6cf430d 100644 --- a/types/src/protocols/index.d.ts +++ b/types/src/protocols/index.d.ts @@ -62,6 +62,8 @@ export type SdaDepositAddress = import("./sda-protocol.js").SdaDepositAddress; export type SdaTransferStatus = import("./sda-protocol.js").SdaTransferStatus; export type SdaTransfer = import("./sda-protocol.js").SdaTransfer; export type SdaTransfersOptions = import("./sda-protocol.js").SdaTransfersOptions; +export type SdaRecoverById = import("./sda-protocol.js").SdaRecoverById; +export type SdaRecoverByAddress = import("./sda-protocol.js").SdaRecoverByAddress; export type SdaRecoveryOptions = import("./sda-protocol.js").SdaRecoveryOptions; export type SdaRecoveryResult = import("./sda-protocol.js").SdaRecoveryResult; export { default as SwapProtocol, ISwapProtocol } from "./swap-protocol.js"; diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 4adbe90..38fa806 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -193,16 +193,27 @@ * @property {number} [skip] - The number of transfers to skip, for offset-based pagination. * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. */ +/** + * Recover a deposit by the SDA identifier. + * + * @typedef {Object} SdaRecoverById + * @property {string} id - The provider SDA identifier (the `SdaDepositAddress.id`). + */ +/** + * Recover a deposit by its deposit address. + * + * @typedef {Object} SdaRecoverByAddress + * @property {string} address - The deposit address to reindex. + * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by providers that key addresses by (address, chain). + */ /** * Options for re-processing a deposit that was not picked up automatically - * (`reindex`). Callers supply whatever the provider needs — typically the source - * transaction, or the deposit address / SDA id. + * (`reindex`). A caller identifies the deposit either by SDA id or by its deposit + * address; the union has no empty member, so `recoverDepositAddress({})` is a + * type error. A provider needing extra inputs extends the relevant member on its + * own options type. * - * @typedef {Object} SdaRecoveryOptions - * @property {string} [address] - The deposit address to reindex. - * @property {string} [id] - The provider SDA identifier. - * @property {string} [sourceTxHash] - The deposit transaction to re-index. - * @property {Blockchain} [sourceChain] - The chain of the deposit transaction. + * @typedef {SdaRecoverById | SdaRecoverByAddress} SdaRecoveryOptions */ /** * The outcome of a recovery attempt. @@ -939,28 +950,35 @@ export type SdaTransfersOptions = { status?: SdaTransferStatus; }; /** - * Options for re-processing a deposit that was not picked up automatically - * (`reindex`). Callers supply whatever the provider needs — typically the source - * transaction, or the deposit address / SDA id. + * Recover a deposit by the SDA identifier. */ -export type SdaRecoveryOptions = { - /** - * - The deposit address to reindex. - */ - address?: string; +export type SdaRecoverById = { /** - * - The provider SDA identifier. + * - The provider SDA identifier (the `SdaDepositAddress.id`). */ - id?: string; + id: string; +}; +/** + * Recover a deposit by its deposit address. + */ +export type SdaRecoverByAddress = { /** - * - The deposit transaction to re-index. + * - The deposit address to reindex. */ - sourceTxHash?: string; + address: string; /** - * - The chain of the deposit transaction. + * - The chain of the deposit address, required by providers that key addresses by (address, chain). */ sourceChain?: Blockchain; }; +/** + * Options for re-processing a deposit that was not picked up automatically + * (`reindex`). A caller identifies the deposit either by SDA id or by its deposit + * address; the union has no empty member, so `recoverDepositAddress({})` is a + * type error. A provider needing extra inputs extends the relevant member on its + * own options type. + */ +export type SdaRecoveryOptions = SdaRecoverById | SdaRecoverByAddress; /** * The outcome of a recovery attempt. */ From 83303e1c6b4d1f485d9ff5e11517d1a7e597036d Mon Sep 17 00:00:00 2001 From: rickalon Date: Mon, 6 Jul 2026 22:25:57 -0400 Subject: [PATCH 16/27] docs(protocols): remove provider names from SDA interface JSDoc The shared interface should describe behavior in provider-neutral terms; the descriptions survive a provider swap untouched. Dropped the "(Orchestra, Relay)" and "(e.g., Rhino)" asides from SdaRecoveryMode and SdaQuoteOptions (AD11). --- src/protocols/sda-protocol.js | 4 ++-- types/src/protocols/sda-protocol.d.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 6c1bce1..6e92af2 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -30,7 +30,7 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' /** * How a provider re-processes a deposit that was not picked up automatically. - * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means + * `reindex` re-scans a known source transaction; `none` means * no such call is exposed. This is about provider-side reprocessing of a missed * deposit, not about fund custody — whether deposited funds are recoverable is * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the @@ -117,7 +117,7 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' /** * Options for fetching a deposit quote. Required up front by providers whose - * addresses are bound to a quote (e.g., Rhino); optional otherwise. + * addresses are bound to a quote; optional otherwise. * * @typedef {Object} SdaQuoteOptions * @property {Blockchain} sourceChain - The chain the deposit originates from. diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 38fa806..2bc0d2d 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -9,7 +9,7 @@ */ /** * How a provider re-processes a deposit that was not picked up automatically. - * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means + * `reindex` re-scans a known source transaction; `none` means * no such call is exposed. This is about provider-side reprocessing of a missed * deposit, not about fund custody — whether deposited funds are recoverable is * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the @@ -88,7 +88,7 @@ */ /** * Options for fetching a deposit quote. Required up front by providers whose - * addresses are bound to a quote (e.g., Rhino); optional otherwise. + * addresses are bound to a quote; optional otherwise. * * @typedef {Object} SdaQuoteOptions * @property {Blockchain} sourceChain - The chain the deposit originates from. @@ -544,7 +544,7 @@ export type AccountRequiredError = import("../errors.js").AccountRequiredError; export type Blockchain = string | number; /** * How a provider re-processes a deposit that was not picked up automatically. - * `reindex` re-scans a known source transaction (Orchestra, Relay); `none` means + * `reindex` re-scans a known source transaction; `none` means * no such call is exposed. This is about provider-side reprocessing of a missed * deposit, not about fund custody — whether deposited funds are recoverable is * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the @@ -676,7 +676,7 @@ export type SdaRoute = { }; /** * Options for fetching a deposit quote. Required up front by providers whose - * addresses are bound to a quote (e.g., Rhino); optional otherwise. + * addresses are bound to a quote; optional otherwise. */ export type SdaQuoteOptions = { /** From 42097de2f0b1904edc5d0cd9b9638697f4c8e837 Mon Sep 17 00:00:00 2001 From: rickalon Date: Tue, 7 Jul 2026 09:56:01 -0400 Subject: [PATCH 17/27] fix(types): emit ISdaProtocol as interface and SdaProtocol as abstract class tsc's JS->.d.ts declaration emit flattens JSDoc @interface and @abstract to plain `class` (verified: regenerating swap-protocol.d.ts flips its hand-maintained `interface` / `abstract class` back to `class`). Hand-edit the SDA declarations to match the sibling protocols and the .d.ts maintenance convention (TD7/TD8). --- types/src/protocols/sda-protocol.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 2bc0d2d..c8f4174 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -241,7 +241,7 @@ * * @interface */ -export class ISdaProtocol { +export interface ISdaProtocol { /** * Lists the conversion routes the provider supports: source chains, accepted * input tokens, destination assets and per-route deposit limits. A provider @@ -371,7 +371,7 @@ export class ISdaProtocol { * @abstract * @implements {ISdaProtocol} */ -export default class SdaProtocol implements ISdaProtocol { +export default abstract class SdaProtocol implements ISdaProtocol { /** * Creates a new SDA protocol without binding it to a wallet account. * From e639e62e45f0d66042cd6029fa95de81c0200991 Mon Sep 17 00:00:00 2001 From: rickalon Date: Tue, 7 Jul 2026 11:21:40 -0400 Subject: [PATCH 18/27] refactor(protocols): drop options.quote; quoteDeposit is a standalone estimate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createDepositAddress no longer accepts a pre-fetched quote — a provider whose address binds to a quote fetches one internally with a cache (the #7 conclusion: never require callers to quote up front). quoteDeposit / SdaQuote / SdaQuoteOptions stay as an optional standalone estimate (real providers like Rhino and Relay expose quote endpoints); only the quote-first coupling and the now-stale "required up front" docs are removed. .d.ts hand-edited, no build:types. --- src/protocols/sda-protocol.js | 25 +++++++++-------- types/src/protocols/sda-protocol.d.ts | 39 +++++++++++---------------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 6e92af2..9682588 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -116,8 +116,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' */ /** - * Options for fetching a deposit quote. Required up front by providers whose - * addresses are bound to a quote; optional otherwise. + * Options for fetching a deposit quote — a non-binding estimate of what a given + * deposit would deliver. * * @typedef {Object} SdaQuoteOptions * @property {Blockchain} sourceChain - The chain the deposit originates from. @@ -146,9 +146,7 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' */ /** - * A non-binding estimate of the asset delivered for a given deposit. Some - * providers return an `id` that must be passed to {@link ISdaProtocol#createDepositAddress} - * to bind the address to this quote. + * A non-binding estimate of the asset delivered for a given deposit. * * @typedef {Object} SdaQuote * @property {Blockchain} inputChain - The chain the deposit originates from. @@ -160,7 +158,7 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {SdaFee[]} fees - Itemised fee breakdown. * @property {string} [rate] - The effective conversion rate as a string, to avoid precision loss. * @property {number} [expiry] - Unix timestamp (seconds) at which the quote expires. - * @property {string} [id] - The provider quote identifier, when an address must be bound to this quote. + * @property {string} [id] - The provider quote identifier, if the provider issues one. */ /** @@ -174,7 +172,6 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). * @property {boolean} [reusable] - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. - * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. */ /** @@ -299,9 +296,10 @@ export class ISdaProtocol { } /** - * Fetches a non-binding quote for a deposit. Optional: only supported by - * providers that offer quoting. Some providers bind the deposit address to a - * quote and require it up front — see that provider's documentation. + * Fetches a non-binding quote (estimate) for a deposit — what a given deposit + * would deliver. Optional: only supported by providers that offer quoting. A + * standalone estimate; {@link ISdaProtocol#createDepositAddress} never requires + * a quote (a provider that binds an address to a quote fetches one internally). * * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. @@ -494,9 +492,10 @@ export default class SdaProtocol { } /** - * Fetches a non-binding quote for a deposit. Optional: only supported by - * providers that offer quoting. Some providers bind the deposit address to a - * quote and require it up front — see that provider's documentation. + * Fetches a non-binding quote (estimate) for a deposit — what a given deposit + * would deliver. Optional: only supported by providers that offer quoting. A + * standalone estimate; {@link ISdaProtocol#createDepositAddress} never requires + * a quote (a provider that binds an address to a quote fetches one internally). * * @abstract * @param {SdaQuoteOptions} options - The quote options. diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index c8f4174..9cddf05 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -87,8 +87,8 @@ * @property {number} [estimatedDuration] - Typical end-to-end duration in seconds. */ /** - * Options for fetching a deposit quote. Required up front by providers whose - * addresses are bound to a quote; optional otherwise. + * Options for fetching a deposit quote — a non-binding estimate of what a given + * deposit would deliver. * * @typedef {Object} SdaQuoteOptions * @property {Blockchain} sourceChain - The chain the deposit originates from. @@ -114,9 +114,7 @@ * @property {string} [description] - A human-readable description of the fee. */ /** - * A non-binding estimate of the asset delivered for a given deposit. Some - * providers return an `id` that must be passed to {@link ISdaProtocol#createDepositAddress} - * to bind the address to this quote. + * A non-binding estimate of the asset delivered for a given deposit. * * @typedef {Object} SdaQuote * @property {Blockchain} inputChain - The chain the deposit originates from. @@ -128,7 +126,7 @@ * @property {SdaFee[]} fees - Itemised fee breakdown. * @property {string} [rate] - The effective conversion rate as a string, to avoid precision loss. * @property {number} [expiry] - Unix timestamp (seconds) at which the quote expires. - * @property {string} [id] - The provider quote identifier, when an address must be bound to this quote. + * @property {string} [id] - The provider quote identifier, if the provider issues one. */ /** * Options for creating a deposit address. @@ -141,7 +139,6 @@ * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). * @property {boolean} [reusable] - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. - * @property {SdaQuote | string} [quote] - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. */ /** * A deposit address plus its normalized descriptor: where it accepts deposits @@ -254,9 +251,10 @@ export interface ISdaProtocol { */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** - * Fetches a non-binding quote for a deposit. Optional: only supported by - * providers that offer quoting. Some providers bind the deposit address to a - * quote and require it up front — see that provider's documentation. + * Fetches a non-binding quote (estimate) for a deposit — what a given deposit + * would deliver. Optional: only supported by providers that offer quoting. A + * standalone estimate; {@link ISdaProtocol#createDepositAddress} never requires + * a quote (a provider that binds an address to a quote fetches one internally). * * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. @@ -414,9 +412,10 @@ export default abstract class SdaProtocol implements ISdaProtocol { */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** - * Fetches a non-binding quote for a deposit. Optional: only supported by - * providers that offer quoting. Some providers bind the deposit address to a - * quote and require it up front — see that provider's documentation. + * Fetches a non-binding quote (estimate) for a deposit — what a given deposit + * would deliver. Optional: only supported by providers that offer quoting. A + * standalone estimate; {@link ISdaProtocol#createDepositAddress} never requires + * a quote (a provider that binds an address to a quote fetches one internally). * * @abstract * @param {SdaQuoteOptions} options - The quote options. @@ -675,8 +674,8 @@ export type SdaRoute = { estimatedDuration?: number; }; /** - * Options for fetching a deposit quote. Required up front by providers whose - * addresses are bound to a quote; optional otherwise. + * Options for fetching a deposit quote — a non-binding estimate of what a given + * deposit would deliver. */ export type SdaQuoteOptions = { /** @@ -734,9 +733,7 @@ export type SdaFee = { description?: string; }; /** - * A non-binding estimate of the asset delivered for a given deposit. Some - * providers return an `id` that must be passed to {@link ISdaProtocol#createDepositAddress} - * to bind the address to this quote. + * A non-binding estimate of the asset delivered for a given deposit. */ export type SdaQuote = { /** @@ -776,7 +773,7 @@ export type SdaQuote = { */ expiry?: number; /** - * - The provider quote identifier, when an address must be bound to this quote. + * - The provider quote identifier, if the provider issues one. */ id?: string; }; @@ -812,10 +809,6 @@ export type SdaCreateOptions = { * - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. */ reusable?: boolean; - /** - * - A pre-fetched quote (or quote id) to bind the address to, for providers that require it. - */ - quote?: SdaQuote | string; }; /** * A deposit address plus its normalized descriptor: where it accepts deposits From 511277834689cb98815b0f02ea169506afc64c73 Mon Sep 17 00:00:00 2001 From: rickalon Date: Tue, 7 Jul 2026 13:09:25 -0400 Subject: [PATCH 19/27] docs(protocols): SdaLimits is base-unit only; omit if denominated otherwise Clarify that a provider whose deposit limits are enforced in another denomination (e.g. USD) omits `limits` rather than converting into the input token's base unit, so the field never carries a mismatched unit. .d.ts hand-edited, no build:types. --- src/protocols/sda-protocol.js | 4 +++- types/src/protocols/sda-protocol.d.ts | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 9682588..7db67d9 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -84,7 +84,9 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' /** * Per-route deposit limits, denominated in the base unit of the route's input - * token. Either bound may be absent when the provider does not enforce it. + * token. Either bound may be absent when the provider does not enforce it; a + * provider that only enforces limits in another denomination (e.g. USD) omits + * `limits` rather than converting. * * @typedef {Object} SdaLimits * @property {number | bigint} [min] - Minimum deposit amount, in the input token's base unit. diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 9cddf05..e147486 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -58,7 +58,9 @@ */ /** * Per-route deposit limits, denominated in the base unit of the route's input - * token. Either bound may be absent when the provider does not enforce it. + * token. Either bound may be absent when the provider does not enforce it; a + * provider that only enforces limits in another denomination (e.g. USD) omits + * `limits` rather than converting. * * @typedef {Object} SdaLimits * @property {number | bigint} [min] - Minimum deposit amount, in the input token's base unit. @@ -606,7 +608,9 @@ export type SdaToken = { }; /** * Per-route deposit limits, denominated in the base unit of the route's input - * token. Either bound may be absent when the provider does not enforce it. + * token. Either bound may be absent when the provider does not enforce it; a + * provider that only enforces limits in another denomination (e.g. USD) omits + * `limits` rather than converting. */ export type SdaLimits = { /** From eda7dcb8daef356f107261b9c98d7cf5f3ff9994 Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 9 Jul 2026 15:11:43 -0400 Subject: [PATCH 20/27] refactor(protocols): rework SDA error taxonomy (review round 2) - errors: add ValueError (correct type, invalid value) and NoSuchElementError (lookup miss); remove the now-unused AccountRequiredError; trim the UnsupportedOperationError description. - ISdaProtocol methods now all throw NotImplementedError; the abstract SdaProtocol keeps throwing UnsupportedOperationError for optional operations (the base default a non-supporting provider inherits). - @throws: route-discovery validation -> ValueError; create/derive missing destination -> ValueError; not-found lookups -> NoSuchElementError; standardize the UnsupportedOperationError condition text. .d.ts hand-edited, no build:types. --- index.js | 3 +- src/errors.js | 31 +++++++---- src/protocols/sda-protocol.js | 78 ++++++++++++++------------- types/index.d.ts | 2 +- types/src/errors.d.ts | 22 +++++--- types/src/protocols/sda-protocol.d.ts | 62 ++++++++++----------- 6 files changed, 112 insertions(+), 86 deletions(-) diff --git a/index.js b/index.js index d69dc2c..eb7d94e 100644 --- a/index.js +++ b/index.js @@ -36,7 +36,8 @@ export { NotImplementedError, SignerError, UnsupportedOperationError, - AccountRequiredError + ValueError, + NoSuchElementError } from './src/errors.js' export { ISigner } from './src/signer.js' diff --git a/src/errors.js b/src/errors.js index 83b09ff..1374763 100644 --- a/src/errors.js +++ b/src/errors.js @@ -42,8 +42,7 @@ export class SignerError extends Error { export class UnsupportedOperationError extends Error { /** * Create a new unsupported operation error. Thrown by an optional operation - * that the concrete implementation deliberately does not support, so consumers - * can distinguish "not supported here" from an abstract method left unimplemented. + * that the concrete implementation deliberately does not support. * * @param {string} operation - The name of the operation that is not supported. */ @@ -54,16 +53,30 @@ export class UnsupportedOperationError extends Error { } } -export class AccountRequiredError extends Error { +export class ValueError extends Error { /** - * Create a new account required error. Thrown when an operation needs a wallet - * account to run but none was bound at construction. + * Create a new value error. Thrown when an argument has the correct type but + * violates a validation rule. * - * @param {string} operation - The name of the operation that requires a wallet account. + * @param {string} message - The error's message. */ - constructor (operation) { - super(`Operation '${operation}' requires a wallet account.`) + constructor (message) { + super(message) + + this.name = 'ValueError' + } +} + +export class NoSuchElementError extends Error { + /** + * Create a new no such element error. Thrown when a lookup finds no element for + * the given identifier. + * + * @param {string} message - The error's message. + */ + constructor (message) { + super(message) - this.name = 'AccountRequiredError' + this.name = 'NoSuchElementError' } } diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 7db67d9..a9f9ce6 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -19,7 +19,9 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' /** @typedef {import('../wallet-account.js').IWalletAccount} IWalletAccount */ -/** @typedef {import('../errors.js').AccountRequiredError} AccountRequiredError */ +/** @typedef {import('../errors.js').ValueError} ValueError */ + +/** @typedef {import('../errors.js').NoSuchElementError} NoSuchElementError */ /** * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific @@ -291,7 +293,7 @@ export class ISdaProtocol { * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {Error} If the provider's route discovery is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain is not set. */ async getSupportedRoutes (options) { throw new NotImplementedError('getSupportedRoutes(options)') @@ -305,10 +307,10 @@ export class ISdaProtocol { * * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. - * @throws {UnsupportedOperationError} If this provider does not support quoting. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async quoteDeposit (options) { - throw new UnsupportedOperationError('quoteDeposit(options)') + throw new NotImplementedError('quoteDeposit(options)') } /** @@ -322,7 +324,7 @@ export class ISdaProtocol { * * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. - * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ async createDepositAddress (options) { throw new NotImplementedError('createDepositAddress(options)') @@ -336,11 +338,11 @@ export class ISdaProtocol { * * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. - * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. - * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ async deriveDepositAddress (options) { - throw new UnsupportedOperationError('deriveDepositAddress(options)') + throw new NotImplementedError('deriveDepositAddress(options)') } /** @@ -351,11 +353,11 @@ export class ISdaProtocol { * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} The deposit address descriptor. - * @throws {UnsupportedOperationError} If this provider does not support address lookup. - * @throws {Error} If no such address exists. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {NoSuchElementError} If no such address exists. */ async getDepositAddress (id) { - throw new UnsupportedOperationError('getDepositAddress(id)') + throw new NotImplementedError('getDepositAddress(id)') } /** @@ -365,10 +367,10 @@ export class ISdaProtocol { * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). - * @throws {UnsupportedOperationError} If this provider does not use activation TTLs. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async renewDepositAddress (id) { - throw new UnsupportedOperationError('renewDepositAddress(id)') + throw new NotImplementedError('renewDepositAddress(id)') } /** @@ -378,10 +380,10 @@ export class ISdaProtocol { * @param {string} address - The deposit address to list transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). * @returns {Promise} The transfers for the address. - * @throws {UnsupportedOperationError} If this provider does not support pull-based history. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async getTransfers (address, options) { - throw new UnsupportedOperationError('getTransfers(address, options)') + throw new NotImplementedError('getTransfers(address, options)') } /** @@ -394,10 +396,10 @@ export class ISdaProtocol { * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. - * @throws {UnsupportedOperationError} If this provider does not support recipient-keyed history. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async getTransfersByRecipient (destinationChain, recipient, options) { - throw new UnsupportedOperationError('getTransfersByRecipient(destinationChain, recipient, options)') + throw new NotImplementedError('getTransfersByRecipient(destinationChain, recipient, options)') } /** @@ -406,11 +408,11 @@ export class ISdaProtocol { * * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. - * @throws {UnsupportedOperationError} If this provider does not support transfer-status lookup. - * @throws {Error} If no such transfer exists. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {NoSuchElementError} If no such transfer exists. */ async getTransferStatus (id) { - throw new UnsupportedOperationError('getTransferStatus(id)') + throw new NotImplementedError('getTransferStatus(id)') } /** @@ -420,10 +422,10 @@ export class ISdaProtocol { * * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. - * @throws {UnsupportedOperationError} If this provider does not support recovery. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async recoverDepositAddress (options) { - throw new UnsupportedOperationError('recoverDepositAddress(options)') + throw new NotImplementedError('recoverDepositAddress(options)') } /** @@ -432,10 +434,10 @@ export class ISdaProtocol { * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} Resolves once the address has been disabled. - * @throws {UnsupportedOperationError} If this provider does not support disabling addresses. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async disableDepositAddress (id) { - throw new UnsupportedOperationError('disableDepositAddress(id)') + throw new NotImplementedError('disableDepositAddress(id)') } } @@ -487,7 +489,7 @@ export default class SdaProtocol { * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {Error} If the provider's route discovery is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain is not set. */ async getSupportedRoutes (options) { throw new NotImplementedError('getSupportedRoutes(options)') @@ -502,7 +504,7 @@ export default class SdaProtocol { * @abstract * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. - * @throws {UnsupportedOperationError} If this provider does not support quoting. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async quoteDeposit (options) { throw new UnsupportedOperationError('quoteDeposit(options)') @@ -520,7 +522,7 @@ export default class SdaProtocol { * @abstract * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. - * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ async createDepositAddress (options) { throw new NotImplementedError('createDepositAddress(options)') @@ -535,8 +537,8 @@ export default class SdaProtocol { * @abstract * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. - * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. - * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ async deriveDepositAddress (options) { throw new UnsupportedOperationError('deriveDepositAddress(options)') @@ -551,8 +553,8 @@ export default class SdaProtocol { * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} The deposit address descriptor. - * @throws {UnsupportedOperationError} If this provider does not support address lookup. - * @throws {Error} If no such address exists. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {NoSuchElementError} If no such address exists. */ async getDepositAddress (id) { throw new UnsupportedOperationError('getDepositAddress(id)') @@ -566,7 +568,7 @@ export default class SdaProtocol { * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). - * @throws {UnsupportedOperationError} If this provider does not use activation TTLs. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async renewDepositAddress (id) { throw new UnsupportedOperationError('renewDepositAddress(id)') @@ -580,7 +582,7 @@ export default class SdaProtocol { * @param {string} address - The deposit address to list transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). * @returns {Promise} The transfers for the address. - * @throws {UnsupportedOperationError} If this provider does not support pull-based history. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async getTransfers (address, options) { throw new UnsupportedOperationError('getTransfers(address, options)') @@ -597,7 +599,7 @@ export default class SdaProtocol { * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. - * @throws {UnsupportedOperationError} If this provider does not support recipient-keyed history. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async getTransfersByRecipient (destinationChain, recipient, options) { throw new UnsupportedOperationError('getTransfersByRecipient(destinationChain, recipient, options)') @@ -610,8 +612,8 @@ export default class SdaProtocol { * @abstract * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. - * @throws {UnsupportedOperationError} If this provider does not support transfer-status lookup. - * @throws {Error} If no such transfer exists. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {NoSuchElementError} If no such transfer exists. */ async getTransferStatus (id) { throw new UnsupportedOperationError('getTransferStatus(id)') @@ -625,7 +627,7 @@ export default class SdaProtocol { * @abstract * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. - * @throws {UnsupportedOperationError} If this provider does not support recovery. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async recoverDepositAddress (options) { throw new UnsupportedOperationError('recoverDepositAddress(options)') @@ -638,7 +640,7 @@ export default class SdaProtocol { * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} Resolves once the address has been disabled. - * @throws {UnsupportedOperationError} If this provider does not support disabling addresses. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async disableDepositAddress (id) { throw new UnsupportedOperationError('disableDepositAddress(id)') diff --git a/types/index.d.ts b/types/index.d.ts index ebf5a9e..6e67e92 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -9,4 +9,4 @@ export type TransferOptions = import("./src/wallet-account-read-only.js").Transf export type TransferResult = import("./src/wallet-account-read-only.js").TransferResult; export type KeyPair = import("./src/wallet-account.js").KeyPair; export { default as WalletAccountReadOnly, IWalletAccountReadOnly } from "./src/wallet-account-read-only.js"; -export { NotImplementedError, SignerError, UnsupportedOperationError, AccountRequiredError } from "./src/errors.js"; +export { NotImplementedError, SignerError, UnsupportedOperationError, ValueError, NoSuchElementError } from "./src/errors.js"; diff --git a/types/src/errors.d.ts b/types/src/errors.d.ts index 4347e34..42caa20 100644 --- a/types/src/errors.d.ts +++ b/types/src/errors.d.ts @@ -17,19 +17,27 @@ export class SignerError extends Error { export class UnsupportedOperationError extends Error { /** * Create a new unsupported operation error. Thrown by an optional operation - * that the concrete implementation deliberately does not support, so consumers - * can distinguish "not supported here" from an abstract method left unimplemented. + * that the concrete implementation deliberately does not support. * * @param {string} operation - The name of the operation that is not supported. */ constructor(operation: string); } -export class AccountRequiredError extends Error { +export class ValueError extends Error { /** - * Create a new account required error. Thrown when an operation needs a wallet - * account to run but none was bound at construction. + * Create a new value error. Thrown when an argument has the correct type but + * violates a validation rule. * - * @param {string} operation - The name of the operation that requires a wallet account. + * @param {string} message - The error's message. */ - constructor(operation: string); + constructor(message: string); +} +export class NoSuchElementError extends Error { + /** + * Create a new no such element error. Thrown when a lookup finds no element for + * the given identifier. + * + * @param {string} message - The error's message. + */ + constructor(message: string); } diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index e147486..ebcb6fd 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -1,6 +1,7 @@ /** @typedef {import('../wallet-account-read-only.js').IWalletAccountReadOnly} IWalletAccountReadOnly */ /** @typedef {import('../wallet-account.js').IWalletAccount} IWalletAccount */ -/** @typedef {import('../errors.js').AccountRequiredError} AccountRequiredError */ +/** @typedef {import('../errors.js').ValueError} ValueError */ +/** @typedef {import('../errors.js').NoSuchElementError} NoSuchElementError */ /** * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific * chain name (e.g. `'ethereum'`). @@ -249,7 +250,7 @@ export interface ISdaProtocol { * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {Error} If the provider's route discovery is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain is not set. */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** @@ -260,7 +261,7 @@ export interface ISdaProtocol { * * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. - * @throws {UnsupportedOperationError} If this provider does not support quoting. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ quoteDeposit(options: SdaQuoteOptions): Promise; /** @@ -274,7 +275,7 @@ export interface ISdaProtocol { * * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. - * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ createDepositAddress(options: SdaCreateOptions): Promise; /** @@ -285,8 +286,8 @@ export interface ISdaProtocol { * * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. - * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. - * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ deriveDepositAddress(options: SdaCreateOptions): Promise; /** @@ -297,8 +298,8 @@ export interface ISdaProtocol { * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} The deposit address descriptor. - * @throws {UnsupportedOperationError} If this provider does not support address lookup. - * @throws {Error} If no such address exists. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {NoSuchElementError} If no such address exists. */ getDepositAddress(id: string): Promise; /** @@ -308,7 +309,7 @@ export interface ISdaProtocol { * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). - * @throws {UnsupportedOperationError} If this provider does not use activation TTLs. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ renewDepositAddress(id: string): Promise; /** @@ -318,7 +319,7 @@ export interface ISdaProtocol { * @param {string} address - The deposit address to list transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). * @returns {Promise} The transfers for the address. - * @throws {UnsupportedOperationError} If this provider does not support pull-based history. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ getTransfers(address: string, options?: SdaTransfersOptions): Promise; /** @@ -331,7 +332,7 @@ export interface ISdaProtocol { * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. - * @throws {UnsupportedOperationError} If this provider does not support recipient-keyed history. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ getTransfersByRecipient(destinationChain: Blockchain, recipient: string, options?: SdaTransfersOptions): Promise; /** @@ -340,8 +341,8 @@ export interface ISdaProtocol { * * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. - * @throws {UnsupportedOperationError} If this provider does not support transfer-status lookup. - * @throws {Error} If no such transfer exists. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {NoSuchElementError} If no such transfer exists. */ getTransferStatus(id: string): Promise; /** @@ -351,7 +352,7 @@ export interface ISdaProtocol { * * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. - * @throws {UnsupportedOperationError} If this provider does not support recovery. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ recoverDepositAddress(options: SdaRecoveryOptions): Promise; /** @@ -360,7 +361,7 @@ export interface ISdaProtocol { * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} Resolves once the address has been disabled. - * @throws {UnsupportedOperationError} If this provider does not support disabling addresses. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ disableDepositAddress(id: string): Promise; } @@ -410,7 +411,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {Error} If the provider's route discovery is `'by-chain-pair'` and `sourceChain` or `destinationChain` is missing. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain is not set. */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** @@ -422,7 +423,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {SdaQuoteOptions} options - The quote options. * @returns {Promise} The quoted deposit details. - * @throws {UnsupportedOperationError} If this provider does not support quoting. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ quoteDeposit(options: SdaQuoteOptions): Promise; /** @@ -437,7 +438,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {SdaCreateOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. - * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ createDepositAddress(options: SdaCreateOptions): Promise; /** @@ -449,8 +450,8 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). * @returns {Promise} The derived deposit address. - * @throws {UnsupportedOperationError} If this provider does not support client-side derivation. - * @throws {AccountRequiredError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ deriveDepositAddress(options: SdaCreateOptions): Promise; /** @@ -462,8 +463,8 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} The deposit address descriptor. - * @throws {UnsupportedOperationError} If this provider does not support address lookup. - * @throws {Error} If no such address exists. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {NoSuchElementError} If no such address exists. */ getDepositAddress(id: string): Promise; /** @@ -474,7 +475,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). - * @throws {UnsupportedOperationError} If this provider does not use activation TTLs. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ renewDepositAddress(id: string): Promise; /** @@ -485,7 +486,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @param {string} address - The deposit address to list transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). * @returns {Promise} The transfers for the address. - * @throws {UnsupportedOperationError} If this provider does not support pull-based history. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ getTransfers(address: string, options?: SdaTransfersOptions): Promise; /** @@ -499,7 +500,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. * @returns {Promise} The transfers routed to the recipient. - * @throws {UnsupportedOperationError} If this provider does not support recipient-keyed history. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ getTransfersByRecipient(destinationChain: Blockchain, recipient: string, options?: SdaTransfersOptions): Promise; /** @@ -509,8 +510,8 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. - * @throws {UnsupportedOperationError} If this provider does not support transfer-status lookup. - * @throws {Error} If no such transfer exists. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {NoSuchElementError} If no such transfer exists. */ getTransferStatus(id: string): Promise; /** @@ -521,7 +522,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. - * @throws {UnsupportedOperationError} If this provider does not support recovery. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ recoverDepositAddress(options: SdaRecoveryOptions): Promise; /** @@ -531,13 +532,14 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). * @returns {Promise} Resolves once the address has been disabled. - * @throws {UnsupportedOperationError} If this provider does not support disabling addresses. + * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ disableDepositAddress(id: string): Promise; } export type IWalletAccountReadOnly = import("../wallet-account-read-only.js").IWalletAccountReadOnly; export type IWalletAccount = import("../wallet-account.js").IWalletAccount; -export type AccountRequiredError = import("../errors.js").AccountRequiredError; +export type ValueError = import("../errors.js").ValueError; +export type NoSuchElementError = import("../errors.js").NoSuchElementError; /** * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific * chain name (e.g. `'ethereum'`). From 6ba18eec5cf526671991fd2e7e0376ea9deb0dd6 Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 9 Jul 2026 15:39:10 -0400 Subject: [PATCH 21/27] refactor(protocols): apply review round 2 docs + renames to SDA interface - rename types: SdaLimits->SdaDepositAddressLimits, SdaQuoteOptions->SdaDepositOptions, SdaQuote->SdaDepositQuote, SdaCreateOptions->SdaCreateDepositAddressOptions, destinationAsset->outputAsset; method getTransferStatus->getTransfer. - delete the docs-only enum typedefs (SdaRouteDiscoveryMode/SdaActivationModel/ SdaCustodyModel/SdaRecoveryMode); inline as plain text. - KISS docs: drop the "Optional:" phrasing, the descriptive-traits and abstract-class boilerplate, and the SdaRecoveryOptions "type error" tail; provider -> protocol. - abstract SdaProtocol: optional methods are concrete defaults (drop @abstract). - .d.ts regenerated to match the doc rewrite; interface/abstract keywords re-applied. --- src/protocols/index.js | 12 +- src/protocols/sda-protocol.js | 317 ++++++------------ types/src/protocols/index.d.ts | 12 +- types/src/protocols/sda-protocol.d.ts | 452 +++++++++----------------- 4 files changed, 272 insertions(+), 521 deletions(-) diff --git a/src/protocols/index.js b/src/protocols/index.js index e28bac3..937a9d3 100644 --- a/src/protocols/index.js +++ b/src/protocols/index.js @@ -68,20 +68,16 @@ /** @typedef {import('./swidge-protocol.js').SwidgeSupportedToken} SwidgeSupportedToken */ /** @typedef {import('./swidge-protocol.js').SwidgeSupportedTokensOptions} SwidgeSupportedTokensOptions */ -/** @typedef {import('./sda-protocol.js').SdaRecoveryMode} SdaRecoveryMode */ -/** @typedef {import('./sda-protocol.js').SdaRouteDiscoveryMode} SdaRouteDiscoveryMode */ -/** @typedef {import('./sda-protocol.js').SdaCustodyModel} SdaCustodyModel */ -/** @typedef {import('./sda-protocol.js').SdaActivationModel} SdaActivationModel */ /** @typedef {import('./sda-protocol.js').Blockchain} Blockchain */ /** @typedef {import('./sda-protocol.js').SdaToken} SdaToken */ -/** @typedef {import('./sda-protocol.js').SdaLimits} SdaLimits */ +/** @typedef {import('./sda-protocol.js').SdaDepositAddressLimits} SdaDepositAddressLimits */ /** @typedef {import('./sda-protocol.js').SdaRoutesOptions} SdaRoutesOptions */ /** @typedef {import('./sda-protocol.js').SdaRoute} SdaRoute */ -/** @typedef {import('./sda-protocol.js').SdaQuoteOptions} SdaQuoteOptions */ +/** @typedef {import('./sda-protocol.js').SdaDepositOptions} SdaDepositOptions */ /** @typedef {import('./sda-protocol.js').SdaFeeType} SdaFeeType */ /** @typedef {import('./sda-protocol.js').SdaFee} SdaFee */ -/** @typedef {import('./sda-protocol.js').SdaQuote} SdaQuote */ -/** @typedef {import('./sda-protocol.js').SdaCreateOptions} SdaCreateOptions */ +/** @typedef {import('./sda-protocol.js').SdaDepositQuote} SdaDepositQuote */ +/** @typedef {import('./sda-protocol.js').SdaCreateDepositAddressOptions} SdaCreateDepositAddressOptions */ /** @typedef {import('./sda-protocol.js').SdaDepositAddress} SdaDepositAddress */ /** @typedef {import('./sda-protocol.js').SdaTransferStatus} SdaTransferStatus */ /** @typedef {import('./sda-protocol.js').SdaTransfer} SdaTransfer */ diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index a9f9ce6..0385c35 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -24,59 +24,17 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' /** @typedef {import('../errors.js').NoSuchElementError} NoSuchElementError */ /** - * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific - * chain name (e.g. `'ethereum'`). + * A blockchain identifier: a numeric chain id (e.g. `1`) or a protocol-specific chain name (e.g. `'ethereum'`). * * @typedef {string | number} Blockchain */ /** - * How a provider re-processes a deposit that was not picked up automatically. - * `reindex` re-scans a known source transaction; `none` means - * no such call is exposed. This is about provider-side reprocessing of a missed - * deposit, not about fund custody — whether deposited funds are recoverable is - * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the - * activation lifecycle ({@link SdaActivationModel} `'ttl'` + - * {@link ISdaProtocol#renewDepositAddress}), not recovery. - * - * @typedef {'reindex' | 'none'} SdaRecoveryMode - */ - -/** - * The activation lifecycle of a deposit address. `'none'` — the address is live - * as soon as it is created. `'required'` — the address must be activated (so the - * provider starts monitoring it) before it can receive deposits. `'ttl'` — - * activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}. - * - * @typedef {'none' | 'required' | 'ttl'} SdaActivationModel - */ - -/** - * Who controls deposited funds while a deposit is in flight. `'trusted-operator'` - * — the provider holds the deposit address and the funds (recovery means asking - * the provider to reprocess). `'self-custodial'` — the address is an on-chain - * contract whose withdrawal rights are fixed in code (e.g. the recipient can - * withdraw immediately, an optional custodial withdrawer only after a timelock), - * so funds are recoverable on-chain without the provider. - * - * @typedef {'self-custodial' | 'trusted-operator'} SdaCustodyModel - */ - -/** - * How a provider lets routes be discovered. `'full'` means - * {@link ISdaProtocol#getSupportedRoutes} returns every route with no filters; - * `'by-chain-pair'` means a source and destination chain must be supplied. - * - * @typedef {'full' | 'by-chain-pair'} SdaRouteDiscoveryMode - */ - -/** - * A normalized, protocol-agnostic token reference. `token` is the identifier - * the provider expects in SDA calls; `address` is the on-chain contract address - * when applicable (absent for native gas tokens). + * A normalized token reference. `token` is the identifier the protocol expects in SDA calls; `address` is the + * on-chain contract address when applicable (absent for native gas tokens). * * @typedef {Object} SdaToken - * @property {string} token - The provider-specific token identifier to use in SDA calls. + * @property {string} token - The protocol-specific token identifier to use in SDA calls. * @property {Blockchain} chain - The chain on which the token lives. * @property {string} symbol - The token symbol (e.g., 'USDC', 'USDT'). * @property {number} decimals - The number of decimal places for the token's base unit. @@ -85,12 +43,11 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' */ /** - * Per-route deposit limits, denominated in the base unit of the route's input - * token. Either bound may be absent when the provider does not enforce it; a - * provider that only enforces limits in another denomination (e.g. USD) omits - * `limits` rather than converting. + * Per-route deposit limits, denominated in the base unit of the route's input token. Either bound may be absent + * when the protocol does not enforce it; a protocol that only enforces limits in another denomination (e.g. USD) + * omits `limits` rather than converting. * - * @typedef {Object} SdaLimits + * @typedef {Object} SdaDepositAddressLimits * @property {number | bigint} [min] - Minimum deposit amount, in the input token's base unit. * @property {number | bigint} [max] - Maximum deposit amount, in the input token's base unit. */ @@ -102,37 +59,36 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {Blockchain} [sourceChain] - Restrict to routes that accept deposits from this chain. * @property {string} [sourceToken] - Restrict to routes that accept this input token. * @property {Blockchain} [destinationChain] - Restrict to routes that deliver to this chain. - * @property {string} [destinationAsset] - Restrict to routes that deliver this asset. + * @property {string} [outputAsset] - Restrict to routes that deliver this asset. */ /** - * A supported conversion route: one or more source chains and their accepted - * input tokens, the destination chain, and the asset delivered there. + * A supported conversion route: one or more source chains and their accepted input tokens, the destination chain, + * and the asset delivered there. * * @typedef {Object} SdaRoute - * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some providers issue one address valid across a VM family. + * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some protocols issue one address valid across a VM family. * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {SdaToken} destinationAsset - The asset delivered to the destination (e.g., USDT). - * @property {SdaLimits} [limits] - Deposit limits for this route. + * @property {SdaToken} outputAsset - The asset delivered to the destination (e.g., USDT). + * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this route. * @property {boolean} [reusable] - Whether addresses issued for this route can receive more than one deposit. * @property {number} [estimatedDuration] - Typical end-to-end duration in seconds. */ /** - * Options for fetching a deposit quote — a non-binding estimate of what a given - * deposit would deliver. + * Options for fetching a deposit quote — a non-binding estimate of what a given deposit would deliver. * - * @typedef {Object} SdaQuoteOptions + * @typedef {Object} SdaDepositOptions * @property {Blockchain} sourceChain - The chain the deposit originates from. - * @property {string} inputToken - The provider identifier of the token being deposited. + * @property {string} inputToken - The protocol identifier of the token being deposited. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} destinationAsset - The provider identifier of the asset to deliver. + * @property {string} outputAsset - The protocol identifier of the asset to deliver. * @property {number | bigint} inputAmount - The amount to deposit, in the input token's base unit. */ /** - * The category of a fee charged by the provider. + * The category of a fee charged by the protocol. * * @typedef {'network' | 'protocol' | 'affiliate' | 'other'} SdaFeeType */ @@ -152,49 +108,49 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' /** * A non-binding estimate of the asset delivered for a given deposit. * - * @typedef {Object} SdaQuote + * @typedef {Object} SdaDepositQuote * @property {Blockchain} inputChain - The chain the deposit originates from. - * @property {string} inputToken - The provider identifier of the deposited token. + * @property {string} inputToken - The protocol identifier of the deposited token. * @property {bigint} inputAmount - The amount deposited, in the input token's base unit. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} destinationAsset - The provider identifier of the delivered asset. + * @property {string} outputAsset - The protocol identifier of the delivered asset. * @property {bigint} outputAmount - The estimated amount delivered, in the destination asset's base unit. * @property {SdaFee[]} fees - Itemised fee breakdown. * @property {string} [rate] - The effective conversion rate as a string, to avoid precision loss. * @property {number} [expiry] - Unix timestamp (seconds) at which the quote expires. - * @property {string} [id] - The provider quote identifier, if the provider issues one. + * @property {string} [id] - The protocol quote identifier, if the protocol issues one. */ /** * Options for creating a deposit address. * - * @typedef {Object} SdaCreateOptions - * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Providers that issue one address per VM family use the full list; single-chain providers use a one-element list. + * @typedef {Object} SdaCreateDepositAddressOptions + * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Protocols that issue one address per VM family use the full list; single-chain protocols use a one-element list. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). + * @property {string} outputAsset - The protocol identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. - * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. + * @property {string} [inputToken] - The expected input token, when the protocol needs it declared up front. * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). - * @property {boolean} [reusable] - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. + * @property {boolean} [reusable] - Request a reusable address, for protocols that let the caller pick reusable vs single-use per request. */ /** - * A deposit address plus its normalized descriptor: where it accepts deposits - * from, what it accepts, where it delivers, and its lifecycle metadata. + * A deposit address plus its normalized descriptor: where it accepts deposits from, what it accepts, where it + * delivers, and its lifecycle metadata. * * @typedef {Object} SdaDepositAddress * @property {string} address - The deposit address the user sends funds to. - * @property {string} id - The provider identifier for this SDA, used for status, recovery and disabling. + * @property {string} id - The protocol identifier for this SDA, used for status, recovery and disabling. * @property {Blockchain[]} sourceChains - The chains this address accepts deposits from. * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {SdaToken} destinationAsset - The asset delivered to the destination. + * @property {SdaToken} outputAsset - The asset delivered to the destination. * @property {string} destinationAddress - The resolved address that receives the delivered asset. - * @property {SdaQuote} [quote] - The quote bound to this address, if any. - * @property {SdaLimits} [limits] - Deposit limits for this address. + * @property {SdaDepositQuote} [quote] - The quote bound to this address, if any. + * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, for a provider whose activation model ({@link SdaActivationModel}) is `'ttl'`. + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when the protocol's address activation is time-limited. */ /** @@ -208,12 +164,12 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * A single deposit observed at, and processed through, an SDA. * * @typedef {Object} SdaTransfer - * @property {string} id - The provider identifier for this transfer. + * @property {string} id - The protocol identifier for this transfer. * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). * @property {SdaTransferStatus} status - The current status of the transfer. * @property {SdaToken} [inputToken] - The token that was deposited. * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. - * @property {SdaToken} [destinationAsset] - The asset delivered to the destination. + * @property {SdaToken} [outputAsset] - The asset delivered to the destination. * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. * @property {string} [sourceTxHash] - The hash of the deposit transaction on the source chain. * @property {string} [destinationTxHash] - The hash of the delivery transaction on the destination chain. @@ -226,7 +182,7 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * Optional pagination/filtering for transfer history. * * @typedef {Object} SdaTransfersOptions - * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by providers that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by protocols that key addresses by (address, chain). * @property {number} [limit] - The maximum number of transfers to return. * @property {number} [skip] - The number of transfers to skip, for offset-based pagination. * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. @@ -236,7 +192,7 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * Recover a deposit by the SDA identifier. * * @typedef {Object} SdaRecoverById - * @property {string} id - The provider SDA identifier (the `SdaDepositAddress.id`). + * @property {string} id - The protocol SDA identifier (the `SdaDepositAddress.id`). */ /** @@ -244,15 +200,12 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * * @typedef {Object} SdaRecoverByAddress * @property {string} address - The deposit address to reindex. - * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by providers that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by protocols that key addresses by (address, chain). */ /** - * Options for re-processing a deposit that was not picked up automatically - * (`reindex`). A caller identifies the deposit either by SDA id or by its deposit - * address; the union has no empty member, so `recoverDepositAddress({})` is a - * type error. A provider needing extra inputs extends the relevant member on its - * own options type. + * Options for re-processing a deposit that was not picked up automatically (`reindex`). A caller identifies the + * deposit either by SDA id or by its deposit address. * * @typedef {SdaRecoverById | SdaRecoverByAddress} SdaRecoveryOptions */ @@ -263,33 +216,26 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @typedef {Object} SdaRecoveryResult * @property {'reindexed' | 'pending' | 'failed'} status - The result of the reindex attempt. * @property {string} [address] - The address that was reindexed. - * @property {string} [id] - The provider SDA identifier. + * @property {string} [id] - The protocol SDA identifier. * @property {SdaTransfer} [transfer] - The transfer that was recovered, if one resulted. * @property {string} [message] - A human-readable description of the outcome. */ /** - * Interface for "Smart Deposit Address" (SDA) providers: services that issue a - * deposit address, accept a stablecoin (or native token) from a supported - * source chain, convert it, and deliver a chosen asset (e.g., USDT) to a chosen - * destination chain and address. + * Interface for "Smart Deposit Address" (SDA) protocols: services that issue a deposit address, accept a + * stablecoin (or native token) from a supported source chain, convert it, and deliver a chosen asset (e.g., USDT) + * to a chosen destination chain and address. * - * The required core every provider implements is route discovery and address - * creation. Every other operation is optional: a provider that does not support - * one leaves the base implementation in place, which throws - * {@link UnsupportedOperationError}. Descriptive traits that are not tied to a - * single method (custody, activation and route-discovery models) are documented - * on each provider — see {@link SdaCustodyModel}, {@link SdaActivationModel} and - * {@link SdaRouteDiscoveryMode}. + * The required core every protocol implements is route discovery and address creation; every other operation is + * optional. * * @interface */ export class ISdaProtocol { /** - * Lists the conversion routes the provider supports: source chains, accepted - * input tokens, destination assets and per-route deposit limits. A provider - * whose route discovery is `'by-chain-pair'` ({@link SdaRouteDiscoveryMode}) - * requires `sourceChain` and `destinationChain` to be supplied. + * Lists the conversion routes the protocol supports: source chains, accepted input tokens, output assets and + * per-route deposit limits. A protocol that discovers routes by blockchain pairs might require the `sourceChain` + * and `destinationChain` options to be set. * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. @@ -300,13 +246,10 @@ export class ISdaProtocol { } /** - * Fetches a non-binding quote (estimate) for a deposit — what a given deposit - * would deliver. Optional: only supported by providers that offer quoting. A - * standalone estimate; {@link ISdaProtocol#createDepositAddress} never requires - * a quote (a provider that binds an address to a quote fetches one internally). + * Fetches a non-binding quote (estimate) for a deposit — what a given deposit would deliver. * - * @param {SdaQuoteOptions} options - The quote options. - * @returns {Promise} The quoted deposit details. + * @param {SdaDepositOptions} options - The quote options. + * @returns {Promise} The quoted deposit details. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async quoteDeposit (options) { @@ -314,15 +257,12 @@ export class ISdaProtocol { } /** - * Creates deposit addresses for the given route and destination, ready to - * receive per the provider's activation model ({@link SdaActivationModel}) — - * for a `'required'` / `'ttl'` provider this also activates the address so it - * is monitored. Returns one entry per distinct address: a provider that issues - * a single address across a chain family returns one entry covering all of - * `sourceChains`, while a provider that issues one address per source chain - * returns one entry each. + * Creates deposit addresses for the given route and destination, ready to receive per the protocol's activation + * lifecycle — a protocol that activates addresses also activates the created address so it is monitored. Returns + * one entry per distinct address: a protocol that issues a single address across a chain family returns one entry + * covering all of `sourceChains`, while a protocol that issues one address per source chain returns one entry each. * - * @param {SdaCreateOptions} options - The address creation options. + * @param {SdaCreateDepositAddressOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ @@ -331,12 +271,10 @@ export class ISdaProtocol { } /** - * Derives a deposit address client-side, without any provider call and - * without activating or monitoring it — used to verify (derive + compare) or - * recover an address for a self-custodial provider. Optional: only supported - * by providers whose deposit address is client-derivable. + * Derives a deposit address client-side, without any protocol call and without activating or monitoring it — + * used to verify (derive + compare) or recover an address for a self-custodial protocol. * - * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). + * @param {SdaCreateDepositAddressOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own options type (which extends `SdaCreateDepositAddressOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. @@ -346,12 +284,10 @@ export class ISdaProtocol { } /** - * Looks up an existing deposit address by its identifier — the - * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, - * which round-trips any chain context the provider needs. Optional: only - * supported by providers that expose address lookup. + * Looks up an existing deposit address by its identifier — the `SdaDepositAddress.id` returned by + * {@link ISdaProtocol#createDepositAddress}, which round-trips any chain context the protocol needs. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). * @returns {Promise} The deposit address descriptor. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such address exists. @@ -361,9 +297,7 @@ export class ISdaProtocol { } /** - * Refreshes the activation of a deposit address so the provider keeps - * monitoring it. Optional: only relevant for providers whose activation model - * ({@link SdaActivationModel}) is `'ttl'`. + * Refreshes the activation of a deposit address so the protocol keeps monitoring it. * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). @@ -374,11 +308,10 @@ export class ISdaProtocol { } /** - * Lists the deposits observed at a deposit address. Optional: only supported - * by providers that expose pull-based history keyed by deposit address. + * Lists the deposits observed at a deposit address. * * @param {string} address - The deposit address to list transfers for. - * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key addresses by (address, chain). * @returns {Promise} The transfers for the address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -387,10 +320,8 @@ export class ISdaProtocol { } /** - * Lists transfers aggregated by recipient — every deposit routed to the given - * recipient across all of that recipient's deposit addresses and source - * chains. Optional: only supported by providers that expose recipient-keyed - * history. + * Lists transfers aggregated by recipient — every deposit routed to the given recipient across all of that + * recipient's deposit addresses and source chains. * * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {string} recipient - The recipient (destination) address to aggregate transfers for. @@ -403,22 +334,19 @@ export class ISdaProtocol { } /** - * Retrieves the status of a single transfer by its identifier. Optional: only - * supported by providers that expose status-by-transfer-id. + * Retrieves a single transfer by its identifier. * * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such transfer exists. */ - async getTransferStatus (id) { - throw new NotImplementedError('getTransferStatus(id)') + async getTransfer (id) { + throw new NotImplementedError('getTransfer(id)') } /** - * Recovers a deposit or address that was not picked up automatically, using - * the provider's recovery mode ({@link SdaRecoveryMode}). Optional: only - * supported by providers whose recovery mode is not `'none'`. + * Recovers a deposit or address that was not picked up automatically, using the protocol's recovery mode. * * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. @@ -429,10 +357,9 @@ export class ISdaProtocol { } /** - * Disables a deposit address so it no longer accepts deposits. Optional: only - * supported by providers that allow disabling an address. + * Disables a deposit address so it no longer accepts deposits. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). * @returns {Promise} Resolves once the address has been disabled. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -442,8 +369,7 @@ export class ISdaProtocol { } /** - * Abstract base class for "Smart Deposit Address" (SDA) providers. Concrete - * providers extend this and implement the provider-specific calls. + * Abstract base class for "Smart Deposit Address" (SDA) protocols. * * @abstract * @implements {ISdaProtocol} @@ -471,8 +397,8 @@ export default class SdaProtocol { */ constructor (account) { /** - * The wallet account to use to interact with the protocol. The account's - * address is the default delivery destination for created addresses. + * The wallet account to use to interact with the protocol. The account's address is the default delivery + * destination for created addresses. * * @protected * @type {IWalletAccountReadOnly | IWalletAccount | undefined} @@ -481,10 +407,9 @@ export default class SdaProtocol { } /** - * Lists the conversion routes the provider supports: source chains, accepted - * input tokens, destination assets and per-route deposit limits. A provider - * whose route discovery is `'by-chain-pair'` ({@link SdaRouteDiscoveryMode}) - * requires `sourceChain` and `destinationChain` to be supplied. + * Lists the conversion routes the protocol supports: source chains, accepted input tokens, output assets and + * per-route deposit limits. A protocol that discovers routes by blockchain pairs might require the `sourceChain` + * and `destinationChain` options to be set. * * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. @@ -496,14 +421,10 @@ export default class SdaProtocol { } /** - * Fetches a non-binding quote (estimate) for a deposit — what a given deposit - * would deliver. Optional: only supported by providers that offer quoting. A - * standalone estimate; {@link ISdaProtocol#createDepositAddress} never requires - * a quote (a provider that binds an address to a quote fetches one internally). + * Fetches a non-binding quote (estimate) for a deposit — what a given deposit would deliver. * - * @abstract - * @param {SdaQuoteOptions} options - The quote options. - * @returns {Promise} The quoted deposit details. + * @param {SdaDepositOptions} options - The quote options. + * @returns {Promise} The quoted deposit details. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ async quoteDeposit (options) { @@ -511,16 +432,13 @@ export default class SdaProtocol { } /** - * Creates deposit addresses for the given route and destination, ready to - * receive per the provider's activation model ({@link SdaActivationModel}) — - * for a `'required'` / `'ttl'` provider this also activates the address so it - * is monitored. Returns one entry per distinct address: a provider that issues - * a single address across a chain family returns one entry covering all of - * `sourceChains`, while a provider that issues one address per source chain - * returns one entry each. + * Creates deposit addresses for the given route and destination, ready to receive per the protocol's activation + * lifecycle — a protocol that activates addresses also activates the created address so it is monitored. Returns + * one entry per distinct address: a protocol that issues a single address across a chain family returns one entry + * covering all of `sourceChains`, while a protocol that issues one address per source chain returns one entry each. * * @abstract - * @param {SdaCreateOptions} options - The address creation options. + * @param {SdaCreateDepositAddressOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ @@ -529,13 +447,10 @@ export default class SdaProtocol { } /** - * Derives a deposit address client-side, without any provider call and - * without activating or monitoring it — used to verify (derive + compare) or - * recover an address for a self-custodial provider. Optional: only supported - * by providers whose deposit address is client-derivable. + * Derives a deposit address client-side, without any protocol call and without activating or monitoring it — + * used to verify (derive + compare) or recover an address for a self-custodial protocol. * - * @abstract - * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). + * @param {SdaCreateDepositAddressOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own options type (which extends `SdaCreateDepositAddressOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. @@ -545,13 +460,10 @@ export default class SdaProtocol { } /** - * Looks up an existing deposit address by its identifier — the - * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, - * which round-trips any chain context the provider needs. Optional: only - * supported by providers that expose address lookup. + * Looks up an existing deposit address by its identifier — the `SdaDepositAddress.id` returned by + * {@link ISdaProtocol#createDepositAddress}, which round-trips any chain context the protocol needs. * - * @abstract - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). * @returns {Promise} The deposit address descriptor. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such address exists. @@ -561,11 +473,8 @@ export default class SdaProtocol { } /** - * Refreshes the activation of a deposit address so the provider keeps - * monitoring it. Optional: only relevant for providers whose activation model - * ({@link SdaActivationModel}) is `'ttl'`. + * Refreshes the activation of a deposit address so the protocol keeps monitoring it. * - * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). * @throws {UnsupportedOperationError} If the protocol does not support this operation. @@ -575,12 +484,10 @@ export default class SdaProtocol { } /** - * Lists the deposits observed at a deposit address. Optional: only supported - * by providers that expose pull-based history keyed by deposit address. + * Lists the deposits observed at a deposit address. * - * @abstract * @param {string} address - The deposit address to list transfers for. - * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key addresses by (address, chain). * @returns {Promise} The transfers for the address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -589,12 +496,9 @@ export default class SdaProtocol { } /** - * Lists transfers aggregated by recipient — every deposit routed to the given - * recipient across all of that recipient's deposit addresses and source - * chains. Optional: only supported by providers that expose recipient-keyed - * history. + * Lists transfers aggregated by recipient — every deposit routed to the given recipient across all of that + * recipient's deposit addresses and source chains. * - * @abstract * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. @@ -606,25 +510,20 @@ export default class SdaProtocol { } /** - * Retrieves the status of a single transfer by its identifier. Optional: only - * supported by providers that expose status-by-transfer-id. + * Retrieves a single transfer by its identifier. * - * @abstract * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such transfer exists. */ - async getTransferStatus (id) { - throw new UnsupportedOperationError('getTransferStatus(id)') + async getTransfer (id) { + throw new UnsupportedOperationError('getTransfer(id)') } /** - * Recovers a deposit or address that was not picked up automatically, using - * the provider's recovery mode ({@link SdaRecoveryMode}). Optional: only - * supported by providers whose recovery mode is not `'none'`. + * Recovers a deposit or address that was not picked up automatically, using the protocol's recovery mode. * - * @abstract * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. * @throws {UnsupportedOperationError} If the protocol does not support this operation. @@ -634,11 +533,9 @@ export default class SdaProtocol { } /** - * Disables a deposit address so it no longer accepts deposits. Optional: only - * supported by providers that allow disabling an address. + * Disables a deposit address so it no longer accepts deposits. * - * @abstract - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). * @returns {Promise} Resolves once the address has been disabled. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ diff --git a/types/src/protocols/index.d.ts b/types/src/protocols/index.d.ts index 6cf430d..bd726a4 100644 --- a/types/src/protocols/index.d.ts +++ b/types/src/protocols/index.d.ts @@ -44,20 +44,16 @@ export type SwidgeStatusResult = import("./swidge-protocol.js").SwidgeStatusResu export type SwidgeSupportedChain = import("./swidge-protocol.js").SwidgeSupportedChain; export type SwidgeSupportedToken = import("./swidge-protocol.js").SwidgeSupportedToken; export type SwidgeSupportedTokensOptions = import("./swidge-protocol.js").SwidgeSupportedTokensOptions; -export type SdaRecoveryMode = import("./sda-protocol.js").SdaRecoveryMode; -export type SdaRouteDiscoveryMode = import("./sda-protocol.js").SdaRouteDiscoveryMode; -export type SdaCustodyModel = import("./sda-protocol.js").SdaCustodyModel; -export type SdaActivationModel = import("./sda-protocol.js").SdaActivationModel; export type Blockchain = import("./sda-protocol.js").Blockchain; export type SdaToken = import("./sda-protocol.js").SdaToken; -export type SdaLimits = import("./sda-protocol.js").SdaLimits; +export type SdaDepositAddressLimits = import("./sda-protocol.js").SdaDepositAddressLimits; export type SdaRoutesOptions = import("./sda-protocol.js").SdaRoutesOptions; export type SdaRoute = import("./sda-protocol.js").SdaRoute; -export type SdaQuoteOptions = import("./sda-protocol.js").SdaQuoteOptions; +export type SdaDepositOptions = import("./sda-protocol.js").SdaDepositOptions; export type SdaFeeType = import("./sda-protocol.js").SdaFeeType; export type SdaFee = import("./sda-protocol.js").SdaFee; -export type SdaQuote = import("./sda-protocol.js").SdaQuote; -export type SdaCreateOptions = import("./sda-protocol.js").SdaCreateOptions; +export type SdaDepositQuote = import("./sda-protocol.js").SdaDepositQuote; +export type SdaCreateDepositAddressOptions = import("./sda-protocol.js").SdaCreateDepositAddressOptions; export type SdaDepositAddress = import("./sda-protocol.js").SdaDepositAddress; export type SdaTransferStatus = import("./sda-protocol.js").SdaTransferStatus; export type SdaTransfer = import("./sda-protocol.js").SdaTransfer; diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index ebcb6fd..55060a9 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -3,54 +3,16 @@ /** @typedef {import('../errors.js').ValueError} ValueError */ /** @typedef {import('../errors.js').NoSuchElementError} NoSuchElementError */ /** - * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific - * chain name (e.g. `'ethereum'`). + * A blockchain identifier: a numeric chain id (e.g. `1`) or a protocol-specific chain name (e.g. `'ethereum'`). * * @typedef {string | number} Blockchain */ /** - * How a provider re-processes a deposit that was not picked up automatically. - * `reindex` re-scans a known source transaction; `none` means - * no such call is exposed. This is about provider-side reprocessing of a missed - * deposit, not about fund custody — whether deposited funds are recoverable is - * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the - * activation lifecycle ({@link SdaActivationModel} `'ttl'` + - * {@link ISdaProtocol#renewDepositAddress}), not recovery. - * - * @typedef {'reindex' | 'none'} SdaRecoveryMode - */ -/** - * The activation lifecycle of a deposit address. `'none'` — the address is live - * as soon as it is created. `'required'` — the address must be activated (so the - * provider starts monitoring it) before it can receive deposits. `'ttl'` — - * activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}. - * - * @typedef {'none' | 'required' | 'ttl'} SdaActivationModel - */ -/** - * Who controls deposited funds while a deposit is in flight. `'trusted-operator'` - * — the provider holds the deposit address and the funds (recovery means asking - * the provider to reprocess). `'self-custodial'` — the address is an on-chain - * contract whose withdrawal rights are fixed in code (e.g. the recipient can - * withdraw immediately, an optional custodial withdrawer only after a timelock), - * so funds are recoverable on-chain without the provider. - * - * @typedef {'self-custodial' | 'trusted-operator'} SdaCustodyModel - */ -/** - * How a provider lets routes be discovered. `'full'` means - * {@link ISdaProtocol#getSupportedRoutes} returns every route with no filters; - * `'by-chain-pair'` means a source and destination chain must be supplied. - * - * @typedef {'full' | 'by-chain-pair'} SdaRouteDiscoveryMode - */ -/** - * A normalized, protocol-agnostic token reference. `token` is the identifier - * the provider expects in SDA calls; `address` is the on-chain contract address - * when applicable (absent for native gas tokens). + * A normalized token reference. `token` is the identifier the protocol expects in SDA calls; `address` is the + * on-chain contract address when applicable (absent for native gas tokens). * * @typedef {Object} SdaToken - * @property {string} token - The provider-specific token identifier to use in SDA calls. + * @property {string} token - The protocol-specific token identifier to use in SDA calls. * @property {Blockchain} chain - The chain on which the token lives. * @property {string} symbol - The token symbol (e.g., 'USDC', 'USDT'). * @property {number} decimals - The number of decimal places for the token's base unit. @@ -58,12 +20,11 @@ * @property {string} [name] - The token's full name. */ /** - * Per-route deposit limits, denominated in the base unit of the route's input - * token. Either bound may be absent when the provider does not enforce it; a - * provider that only enforces limits in another denomination (e.g. USD) omits - * `limits` rather than converting. + * Per-route deposit limits, denominated in the base unit of the route's input token. Either bound may be absent + * when the protocol does not enforce it; a protocol that only enforces limits in another denomination (e.g. USD) + * omits `limits` rather than converting. * - * @typedef {Object} SdaLimits + * @typedef {Object} SdaDepositAddressLimits * @property {number | bigint} [min] - Minimum deposit amount, in the input token's base unit. * @property {number | bigint} [max] - Maximum deposit amount, in the input token's base unit. */ @@ -74,34 +35,33 @@ * @property {Blockchain} [sourceChain] - Restrict to routes that accept deposits from this chain. * @property {string} [sourceToken] - Restrict to routes that accept this input token. * @property {Blockchain} [destinationChain] - Restrict to routes that deliver to this chain. - * @property {string} [destinationAsset] - Restrict to routes that deliver this asset. + * @property {string} [outputAsset] - Restrict to routes that deliver this asset. */ /** - * A supported conversion route: one or more source chains and their accepted - * input tokens, the destination chain, and the asset delivered there. + * A supported conversion route: one or more source chains and their accepted input tokens, the destination chain, + * and the asset delivered there. * * @typedef {Object} SdaRoute - * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some providers issue one address valid across a VM family. + * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some protocols issue one address valid across a VM family. * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {SdaToken} destinationAsset - The asset delivered to the destination (e.g., USDT). - * @property {SdaLimits} [limits] - Deposit limits for this route. + * @property {SdaToken} outputAsset - The asset delivered to the destination (e.g., USDT). + * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this route. * @property {boolean} [reusable] - Whether addresses issued for this route can receive more than one deposit. * @property {number} [estimatedDuration] - Typical end-to-end duration in seconds. */ /** - * Options for fetching a deposit quote — a non-binding estimate of what a given - * deposit would deliver. + * Options for fetching a deposit quote — a non-binding estimate of what a given deposit would deliver. * - * @typedef {Object} SdaQuoteOptions + * @typedef {Object} SdaDepositOptions * @property {Blockchain} sourceChain - The chain the deposit originates from. - * @property {string} inputToken - The provider identifier of the token being deposited. + * @property {string} inputToken - The protocol identifier of the token being deposited. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} destinationAsset - The provider identifier of the asset to deliver. + * @property {string} outputAsset - The protocol identifier of the asset to deliver. * @property {number | bigint} inputAmount - The amount to deposit, in the input token's base unit. */ /** - * The category of a fee charged by the provider. + * The category of a fee charged by the protocol. * * @typedef {'network' | 'protocol' | 'affiliate' | 'other'} SdaFeeType */ @@ -119,47 +79,47 @@ /** * A non-binding estimate of the asset delivered for a given deposit. * - * @typedef {Object} SdaQuote + * @typedef {Object} SdaDepositQuote * @property {Blockchain} inputChain - The chain the deposit originates from. - * @property {string} inputToken - The provider identifier of the deposited token. + * @property {string} inputToken - The protocol identifier of the deposited token. * @property {bigint} inputAmount - The amount deposited, in the input token's base unit. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} destinationAsset - The provider identifier of the delivered asset. + * @property {string} outputAsset - The protocol identifier of the delivered asset. * @property {bigint} outputAmount - The estimated amount delivered, in the destination asset's base unit. * @property {SdaFee[]} fees - Itemised fee breakdown. * @property {string} [rate] - The effective conversion rate as a string, to avoid precision loss. * @property {number} [expiry] - Unix timestamp (seconds) at which the quote expires. - * @property {string} [id] - The provider quote identifier, if the provider issues one. + * @property {string} [id] - The protocol quote identifier, if the protocol issues one. */ /** * Options for creating a deposit address. * - * @typedef {Object} SdaCreateOptions - * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Providers that issue one address per VM family use the full list; single-chain providers use a one-element list. + * @typedef {Object} SdaCreateDepositAddressOptions + * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Protocols that issue one address per VM family use the full list; single-chain protocols use a one-element list. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} destinationAsset - The provider identifier of the asset to deliver (e.g., USDT). + * @property {string} outputAsset - The protocol identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. - * @property {string} [inputToken] - The expected input token, when the provider needs it declared up front. + * @property {string} [inputToken] - The expected input token, when the protocol needs it declared up front. * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). - * @property {boolean} [reusable] - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. + * @property {boolean} [reusable] - Request a reusable address, for protocols that let the caller pick reusable vs single-use per request. */ /** - * A deposit address plus its normalized descriptor: where it accepts deposits - * from, what it accepts, where it delivers, and its lifecycle metadata. + * A deposit address plus its normalized descriptor: where it accepts deposits from, what it accepts, where it + * delivers, and its lifecycle metadata. * * @typedef {Object} SdaDepositAddress * @property {string} address - The deposit address the user sends funds to. - * @property {string} id - The provider identifier for this SDA, used for status, recovery and disabling. + * @property {string} id - The protocol identifier for this SDA, used for status, recovery and disabling. * @property {Blockchain[]} sourceChains - The chains this address accepts deposits from. * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {SdaToken} destinationAsset - The asset delivered to the destination. + * @property {SdaToken} outputAsset - The asset delivered to the destination. * @property {string} destinationAddress - The resolved address that receives the delivered asset. - * @property {SdaQuote} [quote] - The quote bound to this address, if any. - * @property {SdaLimits} [limits] - Deposit limits for this address. + * @property {SdaDepositQuote} [quote] - The quote bound to this address, if any. + * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, for a provider whose activation model ({@link SdaActivationModel}) is `'ttl'`. + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when the protocol's address activation is time-limited. */ /** * The lifecycle status of a deposit/transfer through an SDA. @@ -171,12 +131,12 @@ * A single deposit observed at, and processed through, an SDA. * * @typedef {Object} SdaTransfer - * @property {string} id - The provider identifier for this transfer. + * @property {string} id - The protocol identifier for this transfer. * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). * @property {SdaTransferStatus} status - The current status of the transfer. * @property {SdaToken} [inputToken] - The token that was deposited. * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. - * @property {SdaToken} [destinationAsset] - The asset delivered to the destination. + * @property {SdaToken} [outputAsset] - The asset delivered to the destination. * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. * @property {string} [sourceTxHash] - The hash of the deposit transaction on the source chain. * @property {string} [destinationTxHash] - The hash of the delivery transaction on the destination chain. @@ -188,7 +148,7 @@ * Optional pagination/filtering for transfer history. * * @typedef {Object} SdaTransfersOptions - * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by providers that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by protocols that key addresses by (address, chain). * @property {number} [limit] - The maximum number of transfers to return. * @property {number} [skip] - The number of transfers to skip, for offset-based pagination. * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. @@ -197,21 +157,18 @@ * Recover a deposit by the SDA identifier. * * @typedef {Object} SdaRecoverById - * @property {string} id - The provider SDA identifier (the `SdaDepositAddress.id`). + * @property {string} id - The protocol SDA identifier (the `SdaDepositAddress.id`). */ /** * Recover a deposit by its deposit address. * * @typedef {Object} SdaRecoverByAddress * @property {string} address - The deposit address to reindex. - * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by providers that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by protocols that key addresses by (address, chain). */ /** - * Options for re-processing a deposit that was not picked up automatically - * (`reindex`). A caller identifies the deposit either by SDA id or by its deposit - * address; the union has no empty member, so `recoverDepositAddress({})` is a - * type error. A provider needing extra inputs extends the relevant member on its - * own options type. + * Options for re-processing a deposit that was not picked up automatically (`reindex`). A caller identifies the + * deposit either by SDA id or by its deposit address. * * @typedef {SdaRecoverById | SdaRecoverByAddress} SdaRecoveryOptions */ @@ -221,32 +178,25 @@ * @typedef {Object} SdaRecoveryResult * @property {'reindexed' | 'pending' | 'failed'} status - The result of the reindex attempt. * @property {string} [address] - The address that was reindexed. - * @property {string} [id] - The provider SDA identifier. + * @property {string} [id] - The protocol SDA identifier. * @property {SdaTransfer} [transfer] - The transfer that was recovered, if one resulted. * @property {string} [message] - A human-readable description of the outcome. */ /** - * Interface for "Smart Deposit Address" (SDA) providers: services that issue a - * deposit address, accept a stablecoin (or native token) from a supported - * source chain, convert it, and deliver a chosen asset (e.g., USDT) to a chosen - * destination chain and address. + * Interface for "Smart Deposit Address" (SDA) protocols: services that issue a deposit address, accept a + * stablecoin (or native token) from a supported source chain, convert it, and deliver a chosen asset (e.g., USDT) + * to a chosen destination chain and address. * - * The required core every provider implements is route discovery and address - * creation. Every other operation is optional: a provider that does not support - * one leaves the base implementation in place, which throws - * {@link UnsupportedOperationError}. Descriptive traits that are not tied to a - * single method (custody, activation and route-discovery models) are documented - * on each provider — see {@link SdaCustodyModel}, {@link SdaActivationModel} and - * {@link SdaRouteDiscoveryMode}. + * The required core every protocol implements is route discovery and address creation; every other operation is + * optional. * * @interface */ export interface ISdaProtocol { /** - * Lists the conversion routes the provider supports: source chains, accepted - * input tokens, destination assets and per-route deposit limits. A provider - * whose route discovery is `'by-chain-pair'` ({@link SdaRouteDiscoveryMode}) - * requires `sourceChain` and `destinationChain` to be supplied. + * Lists the conversion routes the protocol supports: source chains, accepted input tokens, output assets and + * per-route deposit limits. A protocol that discovers routes by blockchain pairs might require the `sourceChain` + * and `destinationChain` options to be set. * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. @@ -254,58 +204,46 @@ export interface ISdaProtocol { */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** - * Fetches a non-binding quote (estimate) for a deposit — what a given deposit - * would deliver. Optional: only supported by providers that offer quoting. A - * standalone estimate; {@link ISdaProtocol#createDepositAddress} never requires - * a quote (a provider that binds an address to a quote fetches one internally). + * Fetches a non-binding quote (estimate) for a deposit — what a given deposit would deliver. * - * @param {SdaQuoteOptions} options - The quote options. - * @returns {Promise} The quoted deposit details. + * @param {SdaDepositOptions} options - The quote options. + * @returns {Promise} The quoted deposit details. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ - quoteDeposit(options: SdaQuoteOptions): Promise; + quoteDeposit(options: SdaDepositOptions): Promise; /** - * Creates deposit addresses for the given route and destination, ready to - * receive per the provider's activation model ({@link SdaActivationModel}) — - * for a `'required'` / `'ttl'` provider this also activates the address so it - * is monitored. Returns one entry per distinct address: a provider that issues - * a single address across a chain family returns one entry covering all of - * `sourceChains`, while a provider that issues one address per source chain - * returns one entry each. + * Creates deposit addresses for the given route and destination, ready to receive per the protocol's activation + * lifecycle — a protocol that activates addresses also activates the created address so it is monitored. Returns + * one entry per distinct address: a protocol that issues a single address across a chain family returns one entry + * covering all of `sourceChains`, while a protocol that issues one address per source chain returns one entry each. * - * @param {SdaCreateOptions} options - The address creation options. + * @param {SdaCreateDepositAddressOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ - createDepositAddress(options: SdaCreateOptions): Promise; + createDepositAddress(options: SdaCreateDepositAddressOptions): Promise; /** - * Derives a deposit address client-side, without any provider call and - * without activating or monitoring it — used to verify (derive + compare) or - * recover an address for a self-custodial provider. Optional: only supported - * by providers whose deposit address is client-derivable. + * Derives a deposit address client-side, without any protocol call and without activating or monitoring it — + * used to verify (derive + compare) or recover an address for a self-custodial protocol. * - * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). + * @param {SdaCreateDepositAddressOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own options type (which extends `SdaCreateDepositAddressOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ - deriveDepositAddress(options: SdaCreateOptions): Promise; + deriveDepositAddress(options: SdaCreateDepositAddressOptions): Promise; /** - * Looks up an existing deposit address by its identifier — the - * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, - * which round-trips any chain context the provider needs. Optional: only - * supported by providers that expose address lookup. + * Looks up an existing deposit address by its identifier — the `SdaDepositAddress.id` returned by + * {@link ISdaProtocol#createDepositAddress}, which round-trips any chain context the protocol needs. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). * @returns {Promise} The deposit address descriptor. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such address exists. */ getDepositAddress(id: string): Promise; /** - * Refreshes the activation of a deposit address so the provider keeps - * monitoring it. Optional: only relevant for providers whose activation model - * ({@link SdaActivationModel}) is `'ttl'`. + * Refreshes the activation of a deposit address so the protocol keeps monitoring it. * * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). @@ -313,20 +251,17 @@ export interface ISdaProtocol { */ renewDepositAddress(id: string): Promise; /** - * Lists the deposits observed at a deposit address. Optional: only supported - * by providers that expose pull-based history keyed by deposit address. + * Lists the deposits observed at a deposit address. * * @param {string} address - The deposit address to list transfers for. - * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key addresses by (address, chain). * @returns {Promise} The transfers for the address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ getTransfers(address: string, options?: SdaTransfersOptions): Promise; /** - * Lists transfers aggregated by recipient — every deposit routed to the given - * recipient across all of that recipient's deposit addresses and source - * chains. Optional: only supported by providers that expose recipient-keyed - * history. + * Lists transfers aggregated by recipient — every deposit routed to the given recipient across all of that + * recipient's deposit addresses and source chains. * * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {string} recipient - The recipient (destination) address to aggregate transfers for. @@ -336,19 +271,16 @@ export interface ISdaProtocol { */ getTransfersByRecipient(destinationChain: Blockchain, recipient: string, options?: SdaTransfersOptions): Promise; /** - * Retrieves the status of a single transfer by its identifier. Optional: only - * supported by providers that expose status-by-transfer-id. + * Retrieves a single transfer by its identifier. * * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such transfer exists. */ - getTransferStatus(id: string): Promise; + getTransfer(id: string): Promise; /** - * Recovers a deposit or address that was not picked up automatically, using - * the provider's recovery mode ({@link SdaRecoveryMode}). Optional: only - * supported by providers whose recovery mode is not `'none'`. + * Recovers a deposit or address that was not picked up automatically, using the protocol's recovery mode. * * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. @@ -356,18 +288,16 @@ export interface ISdaProtocol { */ recoverDepositAddress(options: SdaRecoveryOptions): Promise; /** - * Disables a deposit address so it no longer accepts deposits. Optional: only - * supported by providers that allow disabling an address. + * Disables a deposit address so it no longer accepts deposits. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). * @returns {Promise} Resolves once the address has been disabled. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ disableDepositAddress(id: string): Promise; } /** - * Abstract base class for "Smart Deposit Address" (SDA) providers. Concrete - * providers extend this and implement the provider-specific calls. + * Abstract base class for "Smart Deposit Address" (SDA) protocols. * * @abstract * @implements {ISdaProtocol} @@ -395,18 +325,17 @@ export default abstract class SdaProtocol implements ISdaProtocol { */ constructor(account: IWalletAccount); /** - * The wallet account to use to interact with the protocol. The account's - * address is the default delivery destination for created addresses. + * The wallet account to use to interact with the protocol. The account's address is the default delivery + * destination for created addresses. * * @protected * @type {IWalletAccountReadOnly | IWalletAccount | undefined} */ protected _account: IWalletAccountReadOnly | IWalletAccount | undefined; /** - * Lists the conversion routes the provider supports: source chains, accepted - * input tokens, destination assets and per-route deposit limits. A provider - * whose route discovery is `'by-chain-pair'` ({@link SdaRouteDiscoveryMode}) - * requires `sourceChain` and `destinationChain` to be supplied. + * Lists the conversion routes the protocol supports: source chains, accepted input tokens, output assets and + * per-route deposit limits. A protocol that discovers routes by blockchain pairs might require the `sourceChain` + * and `destinationChain` options to be set. * * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. @@ -415,87 +344,66 @@ export default abstract class SdaProtocol implements ISdaProtocol { */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** - * Fetches a non-binding quote (estimate) for a deposit — what a given deposit - * would deliver. Optional: only supported by providers that offer quoting. A - * standalone estimate; {@link ISdaProtocol#createDepositAddress} never requires - * a quote (a provider that binds an address to a quote fetches one internally). + * Fetches a non-binding quote (estimate) for a deposit — what a given deposit would deliver. * - * @abstract - * @param {SdaQuoteOptions} options - The quote options. - * @returns {Promise} The quoted deposit details. + * @param {SdaDepositOptions} options - The quote options. + * @returns {Promise} The quoted deposit details. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ - quoteDeposit(options: SdaQuoteOptions): Promise; + quoteDeposit(options: SdaDepositOptions): Promise; /** - * Creates deposit addresses for the given route and destination, ready to - * receive per the provider's activation model ({@link SdaActivationModel}) — - * for a `'required'` / `'ttl'` provider this also activates the address so it - * is monitored. Returns one entry per distinct address: a provider that issues - * a single address across a chain family returns one entry covering all of - * `sourceChains`, while a provider that issues one address per source chain - * returns one entry each. + * Creates deposit addresses for the given route and destination, ready to receive per the protocol's activation + * lifecycle — a protocol that activates addresses also activates the created address so it is monitored. Returns + * one entry per distinct address: a protocol that issues a single address across a chain family returns one entry + * covering all of `sourceChains`, while a protocol that issues one address per source chain returns one entry each. * * @abstract - * @param {SdaCreateOptions} options - The address creation options. + * @param {SdaCreateDepositAddressOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ - createDepositAddress(options: SdaCreateOptions): Promise; + createDepositAddress(options: SdaCreateDepositAddressOptions): Promise; /** - * Derives a deposit address client-side, without any provider call and - * without activating or monitoring it — used to verify (derive + compare) or - * recover an address for a self-custodial provider. Optional: only supported - * by providers whose deposit address is client-derivable. + * Derives a deposit address client-side, without any protocol call and without activating or monitoring it — + * used to verify (derive + compare) or recover an address for a self-custodial protocol. * - * @abstract - * @param {SdaCreateOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a provider needing extra derivation inputs declares them on its own options type (which extends `SdaCreateOptions`). + * @param {SdaCreateDepositAddressOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own options type (which extends `SdaCreateDepositAddressOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. */ - deriveDepositAddress(options: SdaCreateOptions): Promise; + deriveDepositAddress(options: SdaCreateDepositAddressOptions): Promise; /** - * Looks up an existing deposit address by its identifier — the - * `SdaDepositAddress.id` returned by {@link ISdaProtocol#createDepositAddress}, - * which round-trips any chain context the provider needs. Optional: only - * supported by providers that expose address lookup. + * Looks up an existing deposit address by its identifier — the `SdaDepositAddress.id` returned by + * {@link ISdaProtocol#createDepositAddress}, which round-trips any chain context the protocol needs. * - * @abstract - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). * @returns {Promise} The deposit address descriptor. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such address exists. */ getDepositAddress(id: string): Promise; /** - * Refreshes the activation of a deposit address so the provider keeps - * monitoring it. Optional: only relevant for providers whose activation model - * ({@link SdaActivationModel}) is `'ttl'`. + * Refreshes the activation of a deposit address so the protocol keeps monitoring it. * - * @abstract * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id`. * @returns {Promise} The refreshed deposit address descriptor (with the new `expiry`). * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ renewDepositAddress(id: string): Promise; /** - * Lists the deposits observed at a deposit address. Optional: only supported - * by providers that expose pull-based history keyed by deposit address. + * Lists the deposits observed at a deposit address. * - * @abstract * @param {string} address - The deposit address to list transfers for. - * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for providers that key addresses by (address, chain). + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key addresses by (address, chain). * @returns {Promise} The transfers for the address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ getTransfers(address: string, options?: SdaTransfersOptions): Promise; /** - * Lists transfers aggregated by recipient — every deposit routed to the given - * recipient across all of that recipient's deposit addresses and source - * chains. Optional: only supported by providers that expose recipient-keyed - * history. + * Lists transfers aggregated by recipient — every deposit routed to the given recipient across all of that + * recipient's deposit addresses and source chains. * - * @abstract * @param {Blockchain} destinationChain - The destination chain the transfers are delivered to. * @param {string} recipient - The recipient (destination) address to aggregate transfers for. * @param {SdaTransfersOptions} [options] - Optional pagination/filtering. @@ -504,33 +412,26 @@ export default abstract class SdaProtocol implements ISdaProtocol { */ getTransfersByRecipient(destinationChain: Blockchain, recipient: string, options?: SdaTransfersOptions): Promise; /** - * Retrieves the status of a single transfer by its identifier. Optional: only - * supported by providers that expose status-by-transfer-id. + * Retrieves a single transfer by its identifier. * - * @abstract * @param {string} id - The transfer identifier. * @returns {Promise} The transfer's current status. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such transfer exists. */ - getTransferStatus(id: string): Promise; + getTransfer(id: string): Promise; /** - * Recovers a deposit or address that was not picked up automatically, using - * the provider's recovery mode ({@link SdaRecoveryMode}). Optional: only - * supported by providers whose recovery mode is not `'none'`. + * Recovers a deposit or address that was not picked up automatically, using the protocol's recovery mode. * - * @abstract * @param {SdaRecoveryOptions} options - The recovery options. * @returns {Promise} The recovery outcome. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ recoverDepositAddress(options: SdaRecoveryOptions): Promise; /** - * Disables a deposit address so it no longer accepts deposits. Optional: only - * supported by providers that allow disabling an address. + * Disables a deposit address so it no longer accepts deposits. * - * @abstract - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the provider needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). * @returns {Promise} Resolves once the address has been disabled. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -541,50 +442,16 @@ export type IWalletAccount = import("../wallet-account.js").IWalletAccount; export type ValueError = import("../errors.js").ValueError; export type NoSuchElementError = import("../errors.js").NoSuchElementError; /** - * A blockchain identifier: a numeric chain id (e.g. `1`) or a provider-specific - * chain name (e.g. `'ethereum'`). + * A blockchain identifier: a numeric chain id (e.g. `1`) or a protocol-specific chain name (e.g. `'ethereum'`). */ export type Blockchain = string | number; /** - * How a provider re-processes a deposit that was not picked up automatically. - * `reindex` re-scans a known source transaction; `none` means - * no such call is exposed. This is about provider-side reprocessing of a missed - * deposit, not about fund custody — whether deposited funds are recoverable is - * governed by {@link SdaCustodyModel}. Re-enabling an idle/expired address is the - * activation lifecycle ({@link SdaActivationModel} `'ttl'` + - * {@link ISdaProtocol#renewDepositAddress}), not recovery. - */ -export type SdaRecoveryMode = "reindex" | "none"; -/** - * The activation lifecycle of a deposit address. `'none'` — the address is live - * as soon as it is created. `'required'` — the address must be activated (so the - * provider starts monitoring it) before it can receive deposits. `'ttl'` — - * activation expires and must be refreshed via {@link ISdaProtocol#renewDepositAddress}. - */ -export type SdaActivationModel = "none" | "required" | "ttl"; -/** - * Who controls deposited funds while a deposit is in flight. `'trusted-operator'` - * — the provider holds the deposit address and the funds (recovery means asking - * the provider to reprocess). `'self-custodial'` — the address is an on-chain - * contract whose withdrawal rights are fixed in code (e.g. the recipient can - * withdraw immediately, an optional custodial withdrawer only after a timelock), - * so funds are recoverable on-chain without the provider. - */ -export type SdaCustodyModel = "self-custodial" | "trusted-operator"; -/** - * How a provider lets routes be discovered. `'full'` means - * {@link ISdaProtocol#getSupportedRoutes} returns every route with no filters; - * `'by-chain-pair'` means a source and destination chain must be supplied. - */ -export type SdaRouteDiscoveryMode = "full" | "by-chain-pair"; -/** - * A normalized, protocol-agnostic token reference. `token` is the identifier - * the provider expects in SDA calls; `address` is the on-chain contract address - * when applicable (absent for native gas tokens). + * A normalized token reference. `token` is the identifier the protocol expects in SDA calls; `address` is the + * on-chain contract address when applicable (absent for native gas tokens). */ export type SdaToken = { /** - * - The provider-specific token identifier to use in SDA calls. + * - The protocol-specific token identifier to use in SDA calls. */ token: string; /** @@ -609,12 +476,11 @@ export type SdaToken = { name?: string; }; /** - * Per-route deposit limits, denominated in the base unit of the route's input - * token. Either bound may be absent when the provider does not enforce it; a - * provider that only enforces limits in another denomination (e.g. USD) omits - * `limits` rather than converting. + * Per-route deposit limits, denominated in the base unit of the route's input token. Either bound may be absent + * when the protocol does not enforce it; a protocol that only enforces limits in another denomination (e.g. USD) + * omits `limits` rather than converting. */ -export type SdaLimits = { +export type SdaDepositAddressLimits = { /** * - Minimum deposit amount, in the input token's base unit. */ @@ -643,15 +509,15 @@ export type SdaRoutesOptions = { /** * - Restrict to routes that deliver this asset. */ - destinationAsset?: string; + outputAsset?: string; }; /** - * A supported conversion route: one or more source chains and their accepted - * input tokens, the destination chain, and the asset delivered there. + * A supported conversion route: one or more source chains and their accepted input tokens, the destination chain, + * and the asset delivered there. */ export type SdaRoute = { /** - * - The source chains this route accepts deposits from. A list because some providers issue one address valid across a VM family. + * - The source chains this route accepts deposits from. A list because some protocols issue one address valid across a VM family. */ sourceChains: Blockchain[]; /** @@ -665,11 +531,11 @@ export type SdaRoute = { /** * - The asset delivered to the destination (e.g., USDT). */ - destinationAsset: SdaToken; + outputAsset: SdaToken; /** * - Deposit limits for this route. */ - limits?: SdaLimits; + limits?: SdaDepositAddressLimits; /** * - Whether addresses issued for this route can receive more than one deposit. */ @@ -680,16 +546,15 @@ export type SdaRoute = { estimatedDuration?: number; }; /** - * Options for fetching a deposit quote — a non-binding estimate of what a given - * deposit would deliver. + * Options for fetching a deposit quote — a non-binding estimate of what a given deposit would deliver. */ -export type SdaQuoteOptions = { +export type SdaDepositOptions = { /** * - The chain the deposit originates from. */ sourceChain: Blockchain; /** - * - The provider identifier of the token being deposited. + * - The protocol identifier of the token being deposited. */ inputToken: string; /** @@ -697,16 +562,16 @@ export type SdaQuoteOptions = { */ destinationChain: Blockchain; /** - * - The provider identifier of the asset to deliver. + * - The protocol identifier of the asset to deliver. */ - destinationAsset: string; + outputAsset: string; /** * - The amount to deposit, in the input token's base unit. */ inputAmount: number | bigint; }; /** - * The category of a fee charged by the provider. + * The category of a fee charged by the protocol. */ export type SdaFeeType = "network" | "protocol" | "affiliate" | "other"; /** @@ -741,13 +606,13 @@ export type SdaFee = { /** * A non-binding estimate of the asset delivered for a given deposit. */ -export type SdaQuote = { +export type SdaDepositQuote = { /** * - The chain the deposit originates from. */ inputChain: Blockchain; /** - * - The provider identifier of the deposited token. + * - The protocol identifier of the deposited token. */ inputToken: string; /** @@ -759,9 +624,9 @@ export type SdaQuote = { */ destinationChain: Blockchain; /** - * - The provider identifier of the delivered asset. + * - The protocol identifier of the delivered asset. */ - destinationAsset: string; + outputAsset: string; /** * - The estimated amount delivered, in the destination asset's base unit. */ @@ -779,16 +644,16 @@ export type SdaQuote = { */ expiry?: number; /** - * - The provider quote identifier, if the provider issues one. + * - The protocol quote identifier, if the protocol issues one. */ id?: string; }; /** * Options for creating a deposit address. */ -export type SdaCreateOptions = { +export type SdaCreateDepositAddressOptions = { /** - * - One or more source chains the address should accept deposits from. Providers that issue one address per VM family use the full list; single-chain providers use a one-element list. + * - One or more source chains the address should accept deposits from. Protocols that issue one address per VM family use the full list; single-chain protocols use a one-element list. */ sourceChains: Blockchain[]; /** @@ -796,15 +661,15 @@ export type SdaCreateOptions = { */ destinationChain: Blockchain; /** - * - The provider identifier of the asset to deliver (e.g., USDT). + * - The protocol identifier of the asset to deliver (e.g., USDT). */ - destinationAsset: string; + outputAsset: string; /** * - The address that receives the delivered asset. Defaults to the bound account's address. */ destinationAddress?: string; /** - * - The expected input token, when the provider needs it declared up front. + * - The expected input token, when the protocol needs it declared up front. */ inputToken?: string; /** @@ -812,13 +677,13 @@ export type SdaCreateOptions = { */ refundAddress?: string; /** - * - Request a reusable address, for providers that let the caller pick reusable vs single-use per request. + * - Request a reusable address, for protocols that let the caller pick reusable vs single-use per request. */ reusable?: boolean; }; /** - * A deposit address plus its normalized descriptor: where it accepts deposits - * from, what it accepts, where it delivers, and its lifecycle metadata. + * A deposit address plus its normalized descriptor: where it accepts deposits from, what it accepts, where it + * delivers, and its lifecycle metadata. */ export type SdaDepositAddress = { /** @@ -826,7 +691,7 @@ export type SdaDepositAddress = { */ address: string; /** - * - The provider identifier for this SDA, used for status, recovery and disabling. + * - The protocol identifier for this SDA, used for status, recovery and disabling. */ id: string; /** @@ -844,7 +709,7 @@ export type SdaDepositAddress = { /** * - The asset delivered to the destination. */ - destinationAsset: SdaToken; + outputAsset: SdaToken; /** * - The resolved address that receives the delivered asset. */ @@ -852,11 +717,11 @@ export type SdaDepositAddress = { /** * - The quote bound to this address, if any. */ - quote?: SdaQuote; + quote?: SdaDepositQuote; /** * - Deposit limits for this address. */ - limits?: SdaLimits; + limits?: SdaDepositAddressLimits; /** * - Whether the address can receive more than one deposit. */ @@ -866,7 +731,7 @@ export type SdaDepositAddress = { */ refundAddress?: string; /** - * - Unix timestamp (seconds) at which the address's activation expires, for a provider whose activation model ({@link SdaActivationModel}) is `'ttl'`. + * - Unix timestamp (seconds) at which the address's activation expires, when the protocol's address activation is time-limited. */ expiry?: number; }; @@ -879,7 +744,7 @@ export type SdaTransferStatus = "pending" | "detected" | "processing" | "complet */ export type SdaTransfer = { /** - * - The provider identifier for this transfer. + * - The protocol identifier for this transfer. */ id: string; /** @@ -901,7 +766,7 @@ export type SdaTransfer = { /** * - The asset delivered to the destination. */ - destinationAsset?: SdaToken; + outputAsset?: SdaToken; /** * - The amount delivered, in the destination asset's base unit. */ @@ -932,7 +797,7 @@ export type SdaTransfer = { */ export type SdaTransfersOptions = { /** - * - The source chain of the deposit address, required by providers that key addresses by (address, chain). + * - The source chain of the deposit address, required by protocols that key addresses by (address, chain). */ sourceChain?: Blockchain; /** @@ -953,7 +818,7 @@ export type SdaTransfersOptions = { */ export type SdaRecoverById = { /** - * - The provider SDA identifier (the `SdaDepositAddress.id`). + * - The protocol SDA identifier (the `SdaDepositAddress.id`). */ id: string; }; @@ -966,16 +831,13 @@ export type SdaRecoverByAddress = { */ address: string; /** - * - The chain of the deposit address, required by providers that key addresses by (address, chain). + * - The chain of the deposit address, required by protocols that key addresses by (address, chain). */ sourceChain?: Blockchain; }; /** - * Options for re-processing a deposit that was not picked up automatically - * (`reindex`). A caller identifies the deposit either by SDA id or by its deposit - * address; the union has no empty member, so `recoverDepositAddress({})` is a - * type error. A provider needing extra inputs extends the relevant member on its - * own options type. + * Options for re-processing a deposit that was not picked up automatically (`reindex`). A caller identifies the + * deposit either by SDA id or by its deposit address. */ export type SdaRecoveryOptions = SdaRecoverById | SdaRecoverByAddress; /** @@ -991,7 +853,7 @@ export type SdaRecoveryResult = { */ address?: string; /** - * - The provider SDA identifier. + * - The protocol SDA identifier. */ id?: string; /** From f6ba3b313ef10a6ce92b3e68dcd210eba00fbd0c Mon Sep 17 00:00:00 2001 From: rickalon Date: Thu, 9 Jul 2026 18:29:57 -0400 Subject: [PATCH 22/27] refactor(protocols): drop unused SdaTransfer fields inputToken, outputAsset and fees are not populated by any reference protocol, so drop them from SdaTransfer. id + status stay required; the rest are optional since protocols/endpoints report different subsets (review comment). .d.ts hand-edited. --- src/protocols/sda-protocol.js | 3 --- types/src/protocols/sda-protocol.d.ts | 15 --------------- 2 files changed, 18 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 0385c35..1dca3b1 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -167,13 +167,10 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {string} id - The protocol identifier for this transfer. * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). * @property {SdaTransferStatus} status - The current status of the transfer. - * @property {SdaToken} [inputToken] - The token that was deposited. * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. - * @property {SdaToken} [outputAsset] - The asset delivered to the destination. * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. * @property {string} [sourceTxHash] - The hash of the deposit transaction on the source chain. * @property {string} [destinationTxHash] - The hash of the delivery transaction on the destination chain. - * @property {SdaFee[]} [fees] - Itemised fees applied to this transfer. * @property {number} [createdAt] - Unix timestamp (seconds) when the transfer was first observed. * @property {number} [updatedAt] - Unix timestamp (seconds) when the transfer was last updated. */ diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 55060a9..a932ebd 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -134,13 +134,10 @@ * @property {string} id - The protocol identifier for this transfer. * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). * @property {SdaTransferStatus} status - The current status of the transfer. - * @property {SdaToken} [inputToken] - The token that was deposited. * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. - * @property {SdaToken} [outputAsset] - The asset delivered to the destination. * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. * @property {string} [sourceTxHash] - The hash of the deposit transaction on the source chain. * @property {string} [destinationTxHash] - The hash of the delivery transaction on the destination chain. - * @property {SdaFee[]} [fees] - Itemised fees applied to this transfer. * @property {number} [createdAt] - Unix timestamp (seconds) when the transfer was first observed. * @property {number} [updatedAt] - Unix timestamp (seconds) when the transfer was last updated. */ @@ -755,18 +752,10 @@ export type SdaTransfer = { * - The current status of the transfer. */ status: SdaTransferStatus; - /** - * - The token that was deposited. - */ - inputToken?: SdaToken; /** * - The amount deposited, in the input token's base unit. */ inputAmount?: bigint; - /** - * - The asset delivered to the destination. - */ - outputAsset?: SdaToken; /** * - The amount delivered, in the destination asset's base unit. */ @@ -779,10 +768,6 @@ export type SdaTransfer = { * - The hash of the delivery transaction on the destination chain. */ destinationTxHash?: string; - /** - * - Itemised fees applied to this transfer. - */ - fees?: SdaFee[]; /** * - Unix timestamp (seconds) when the transfer was first observed. */ From 412d807e83bcf686033a13074e696e7a29d6ccd7 Mon Sep 17 00:00:00 2001 From: rickalon Date: Fri, 10 Jul 2026 10:22:34 -0400 Subject: [PATCH 23/27] style(protocols): wrap SDA doc comments at 120 columns Long @property / @param descriptions ran up to 255 chars; wrap them onto continuation lines so documentation caps at 120 columns, as requested in review. {@link} tags are kept intact on a single line. .d.ts comments wrapped to match. --- src/protocols/sda-protocol.js | 59 +++++++++++++++-------- types/src/protocols/sda-protocol.d.ts | 68 ++++++++++++++++++--------- 2 files changed, 86 insertions(+), 41 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 1dca3b1..64c5ab7 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -67,7 +67,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * and the asset delivered there. * * @typedef {Object} SdaRoute - * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some protocols issue one address valid across a VM family. + * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some + * protocols issue one address valid across a VM family. * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {SdaToken} outputAsset - The asset delivered to the destination (e.g., USDT). @@ -125,13 +126,17 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * Options for creating a deposit address. * * @typedef {Object} SdaCreateDepositAddressOptions - * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Protocols that issue one address per VM family use the full list; single-chain protocols use a one-element list. + * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Protocols + * that issue one address per VM family use the full list; single-chain protocols use a one-element list. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {string} outputAsset - The protocol identifier of the asset to deliver (e.g., USDT). - * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. + * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound + * account's address. * @property {string} [inputToken] - The expected input token, when the protocol needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). - * @property {boolean} [reusable] - Request a reusable address, for protocols that let the caller pick reusable vs single-use per request. + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund + * style). + * @property {boolean} [reusable] - Request a reusable address, for protocols that let the caller pick reusable vs + * single-use per request. */ /** @@ -150,7 +155,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when the protocol's address activation is time-limited. + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when the protocol's + * address activation is time-limited. */ /** @@ -165,7 +171,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * * @typedef {Object} SdaTransfer * @property {string} id - The protocol identifier for this transfer. - * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). + * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not + * return it). * @property {SdaTransferStatus} status - The current status of the transfer. * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. @@ -179,7 +186,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * Optional pagination/filtering for transfer history. * * @typedef {Object} SdaTransfersOptions - * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by protocols that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by protocols that key + * addresses by (address, chain). * @property {number} [limit] - The maximum number of transfers to return. * @property {number} [skip] - The number of transfers to skip, for offset-based pagination. * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. @@ -197,7 +205,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * * @typedef {Object} SdaRecoverByAddress * @property {string} address - The deposit address to reindex. - * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by protocols that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by protocols that key addresses by + * (address, chain). */ /** @@ -236,7 +245,8 @@ export class ISdaProtocol { * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain is not set. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain + * is not set. */ async getSupportedRoutes (options) { throw new NotImplementedError('getSupportedRoutes(options)') @@ -271,7 +281,9 @@ export class ISdaProtocol { * Derives a deposit address client-side, without any protocol call and without activating or monitoring it — * used to verify (derive + compare) or recover an address for a self-custodial protocol. * - * @param {SdaCreateDepositAddressOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own options type (which extends `SdaCreateDepositAddressOptions`). + * @param {SdaCreateDepositAddressOptions} options - The same options passed to + * {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own + * options type (which extends `SdaCreateDepositAddressOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. @@ -284,7 +296,8 @@ export class ISdaProtocol { * Looks up an existing deposit address by its identifier — the `SdaDepositAddress.id` returned by * {@link ISdaProtocol#createDepositAddress}, which round-trips any chain context the protocol needs. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain + * context the protocol needs). * @returns {Promise} The deposit address descriptor. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such address exists. @@ -308,7 +321,8 @@ export class ISdaProtocol { * Lists the deposits observed at a deposit address. * * @param {string} address - The deposit address to list transfers for. - * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key addresses by (address, chain). + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key + * addresses by (address, chain). * @returns {Promise} The transfers for the address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -356,7 +370,8 @@ export class ISdaProtocol { /** * Disables a deposit address so it no longer accepts deposits. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain + * context the protocol needs). * @returns {Promise} Resolves once the address has been disabled. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -411,7 +426,8 @@ export default class SdaProtocol { * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain is not set. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain + * is not set. */ async getSupportedRoutes (options) { throw new NotImplementedError('getSupportedRoutes(options)') @@ -447,7 +463,9 @@ export default class SdaProtocol { * Derives a deposit address client-side, without any protocol call and without activating or monitoring it — * used to verify (derive + compare) or recover an address for a self-custodial protocol. * - * @param {SdaCreateDepositAddressOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own options type (which extends `SdaCreateDepositAddressOptions`). + * @param {SdaCreateDepositAddressOptions} options - The same options passed to + * {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own + * options type (which extends `SdaCreateDepositAddressOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. @@ -460,7 +478,8 @@ export default class SdaProtocol { * Looks up an existing deposit address by its identifier — the `SdaDepositAddress.id` returned by * {@link ISdaProtocol#createDepositAddress}, which round-trips any chain context the protocol needs. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain + * context the protocol needs). * @returns {Promise} The deposit address descriptor. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such address exists. @@ -484,7 +503,8 @@ export default class SdaProtocol { * Lists the deposits observed at a deposit address. * * @param {string} address - The deposit address to list transfers for. - * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key addresses by (address, chain). + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key + * addresses by (address, chain). * @returns {Promise} The transfers for the address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -532,7 +552,8 @@ export default class SdaProtocol { /** * Disables a deposit address so it no longer accepts deposits. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain + * context the protocol needs). * @returns {Promise} Resolves once the address has been disabled. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index a932ebd..82b4594 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -42,7 +42,8 @@ * and the asset delivered there. * * @typedef {Object} SdaRoute - * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some protocols issue one address valid across a VM family. + * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some + * protocols issue one address valid across a VM family. * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {SdaToken} outputAsset - The asset delivered to the destination (e.g., USDT). @@ -95,13 +96,17 @@ * Options for creating a deposit address. * * @typedef {Object} SdaCreateDepositAddressOptions - * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Protocols that issue one address per VM family use the full list; single-chain protocols use a one-element list. + * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Protocols + * that issue one address per VM family use the full list; single-chain protocols use a one-element list. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {string} outputAsset - The protocol identifier of the asset to deliver (e.g., USDT). - * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound account's address. + * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound + * account's address. * @property {string} [inputToken] - The expected input token, when the protocol needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund style). - * @property {boolean} [reusable] - Request a reusable address, for protocols that let the caller pick reusable vs single-use per request. + * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund + * style). + * @property {boolean} [reusable] - Request a reusable address, for protocols that let the caller pick reusable vs + * single-use per request. */ /** * A deposit address plus its normalized descriptor: where it accepts deposits from, what it accepts, where it @@ -119,7 +124,8 @@ * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when the protocol's address activation is time-limited. + * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when the protocol's + * address activation is time-limited. */ /** * The lifecycle status of a deposit/transfer through an SDA. @@ -132,7 +138,8 @@ * * @typedef {Object} SdaTransfer * @property {string} id - The protocol identifier for this transfer. - * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). + * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not + * return it). * @property {SdaTransferStatus} status - The current status of the transfer. * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. @@ -145,7 +152,8 @@ * Optional pagination/filtering for transfer history. * * @typedef {Object} SdaTransfersOptions - * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by protocols that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by protocols that key + * addresses by (address, chain). * @property {number} [limit] - The maximum number of transfers to return. * @property {number} [skip] - The number of transfers to skip, for offset-based pagination. * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. @@ -161,7 +169,8 @@ * * @typedef {Object} SdaRecoverByAddress * @property {string} address - The deposit address to reindex. - * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by protocols that key addresses by (address, chain). + * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by protocols that key addresses by + * (address, chain). */ /** * Options for re-processing a deposit that was not picked up automatically (`reindex`). A caller identifies the @@ -197,7 +206,8 @@ export interface ISdaProtocol { * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain is not set. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination + * blockchain is not set. */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** @@ -223,7 +233,9 @@ export interface ISdaProtocol { * Derives a deposit address client-side, without any protocol call and without activating or monitoring it — * used to verify (derive + compare) or recover an address for a self-custodial protocol. * - * @param {SdaCreateDepositAddressOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own options type (which extends `SdaCreateDepositAddressOptions`). + * @param {SdaCreateDepositAddressOptions} options - The same options passed to + * {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own + * options type (which extends `SdaCreateDepositAddressOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. @@ -233,7 +245,8 @@ export interface ISdaProtocol { * Looks up an existing deposit address by its identifier — the `SdaDepositAddress.id` returned by * {@link ISdaProtocol#createDepositAddress}, which round-trips any chain context the protocol needs. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain + * context the protocol needs). * @returns {Promise} The deposit address descriptor. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such address exists. @@ -251,7 +264,8 @@ export interface ISdaProtocol { * Lists the deposits observed at a deposit address. * * @param {string} address - The deposit address to list transfers for. - * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key addresses by (address, chain). + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key + * addresses by (address, chain). * @returns {Promise} The transfers for the address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -287,7 +301,8 @@ export interface ISdaProtocol { /** * Disables a deposit address so it no longer accepts deposits. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain + * context the protocol needs). * @returns {Promise} Resolves once the address has been disabled. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -337,7 +352,8 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain is not set. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination + * blockchain is not set. */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** @@ -364,7 +380,9 @@ export default abstract class SdaProtocol implements ISdaProtocol { * Derives a deposit address client-side, without any protocol call and without activating or monitoring it — * used to verify (derive + compare) or recover an address for a self-custodial protocol. * - * @param {SdaCreateDepositAddressOptions} options - The same options passed to {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own options type (which extends `SdaCreateDepositAddressOptions`). + * @param {SdaCreateDepositAddressOptions} options - The same options passed to + * {@link ISdaProtocol#createDepositAddress}; a protocol needing extra derivation inputs declares them on its own + * options type (which extends `SdaCreateDepositAddressOptions`). * @returns {Promise} The derived deposit address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. @@ -374,7 +392,8 @@ export default abstract class SdaProtocol implements ISdaProtocol { * Looks up an existing deposit address by its identifier — the `SdaDepositAddress.id` returned by * {@link ISdaProtocol#createDepositAddress}, which round-trips any chain context the protocol needs. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain + * context the protocol needs). * @returns {Promise} The deposit address descriptor. * @throws {UnsupportedOperationError} If the protocol does not support this operation. * @throws {NoSuchElementError} If no such address exists. @@ -392,7 +411,8 @@ export default abstract class SdaProtocol implements ISdaProtocol { * Lists the deposits observed at a deposit address. * * @param {string} address - The deposit address to list transfers for. - * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key addresses by (address, chain). + * @param {SdaTransfersOptions} [options] - Optional pagination/filtering, plus `sourceChain` for protocols that key + * addresses by (address, chain). * @returns {Promise} The transfers for the address. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -428,7 +448,8 @@ export default abstract class SdaProtocol implements ISdaProtocol { /** * Disables a deposit address so it no longer accepts deposits. * - * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain context the protocol needs). + * @param {string} id - The deposit-address identifier returned in `SdaDepositAddress.id` (round-trips any chain + * context the protocol needs). * @returns {Promise} Resolves once the address has been disabled. * @throws {UnsupportedOperationError} If the protocol does not support this operation. */ @@ -514,7 +535,8 @@ export type SdaRoutesOptions = { */ export type SdaRoute = { /** - * - The source chains this route accepts deposits from. A list because some protocols issue one address valid across a VM family. + * - The source chains this route accepts deposits from. A list because some protocols issue one address valid + * across a VM family. */ sourceChains: Blockchain[]; /** @@ -650,7 +672,8 @@ export type SdaDepositQuote = { */ export type SdaCreateDepositAddressOptions = { /** - * - One or more source chains the address should accept deposits from. Protocols that issue one address per VM family use the full list; single-chain protocols use a one-element list. + * - One or more source chains the address should accept deposits from. Protocols that issue one address per VM + * family use the full list; single-chain protocols use a one-element list. */ sourceChains: Blockchain[]; /** @@ -728,7 +751,8 @@ export type SdaDepositAddress = { */ refundAddress?: string; /** - * - Unix timestamp (seconds) at which the address's activation expires, when the protocol's address activation is time-limited. + * - Unix timestamp (seconds) at which the address's activation expires, when the protocol's address activation is + * time-limited. */ expiry?: number; }; From d82dca3d64370da5c50e04488455b5eabba58503 Mon Sep 17 00:00:00 2001 From: rickalon Date: Fri, 10 Jul 2026 11:39:43 -0400 Subject: [PATCH 24/27] style(types): normalize SDA .d.ts comments to tsc output The committed declarations had their doc comments re-wrapped independently at 120 columns, which diverged from what tsc actually emits: it inherits the source's line breaks for verbatim-copied JSDoc, and uses a flat `* ` continuation for the synthesized @property comments. Normalize to tsc's layout so the only remaining delta from a fresh regen is the two hand-maintained keywords (interface ISdaProtocol / abstract class SdaProtocol), which makes future type-check diffs meaningful instead of noisy. Comment-only: no signature, type or member changes. --- types/src/protocols/sda-protocol.d.ts | 38 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 82b4594..1a36a28 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -206,8 +206,8 @@ export interface ISdaProtocol { * * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination - * blockchain is not set. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain + * is not set. */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** @@ -352,8 +352,8 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @abstract * @param {SdaRoutesOptions} [options] - Optional filters for route discovery. * @returns {Promise} The supported routes. - * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination - * blockchain is not set. + * @throws {ValueError} If the protocol discovers routes by blockchain pairs and the source or destination blockchain + * is not set. */ getSupportedRoutes(options?: SdaRoutesOptions): Promise; /** @@ -535,8 +535,8 @@ export type SdaRoutesOptions = { */ export type SdaRoute = { /** - * - The source chains this route accepts deposits from. A list because some protocols issue one address valid - * across a VM family. + * - The source chains this route accepts deposits from. A list because some + * protocols issue one address valid across a VM family. */ sourceChains: Blockchain[]; /** @@ -672,8 +672,8 @@ export type SdaDepositQuote = { */ export type SdaCreateDepositAddressOptions = { /** - * - One or more source chains the address should accept deposits from. Protocols that issue one address per VM - * family use the full list; single-chain protocols use a one-element list. + * - One or more source chains the address should accept deposits from. Protocols + * that issue one address per VM family use the full list; single-chain protocols use a one-element list. */ sourceChains: Blockchain[]; /** @@ -685,7 +685,8 @@ export type SdaCreateDepositAddressOptions = { */ outputAsset: string; /** - * - The address that receives the delivered asset. Defaults to the bound account's address. + * - The address that receives the delivered asset. Defaults to the bound + * account's address. */ destinationAddress?: string; /** @@ -693,11 +694,13 @@ export type SdaCreateDepositAddressOptions = { */ inputToken?: string; /** - * - The address that receives refunds if a deposit cannot be processed (push-refund style). + * - The address that receives refunds if a deposit cannot be processed (push-refund + * style). */ refundAddress?: string; /** - * - Request a reusable address, for protocols that let the caller pick reusable vs single-use per request. + * - Request a reusable address, for protocols that let the caller pick reusable vs + * single-use per request. */ reusable?: boolean; }; @@ -751,8 +754,8 @@ export type SdaDepositAddress = { */ refundAddress?: string; /** - * - Unix timestamp (seconds) at which the address's activation expires, when the protocol's address activation is - * time-limited. + * - Unix timestamp (seconds) at which the address's activation expires, when the protocol's + * address activation is time-limited. */ expiry?: number; }; @@ -769,7 +772,8 @@ export type SdaTransfer = { */ id: string; /** - * - The SDA the deposit was sent to, when known (a status-by-id lookup may not return it). + * - The SDA the deposit was sent to, when known (a status-by-id lookup may not + * return it). */ depositAddress?: string; /** @@ -806,7 +810,8 @@ export type SdaTransfer = { */ export type SdaTransfersOptions = { /** - * - The source chain of the deposit address, required by protocols that key addresses by (address, chain). + * - The source chain of the deposit address, required by protocols that key + * addresses by (address, chain). */ sourceChain?: Blockchain; /** @@ -840,7 +845,8 @@ export type SdaRecoverByAddress = { */ address: string; /** - * - The chain of the deposit address, required by protocols that key addresses by (address, chain). + * - The chain of the deposit address, required by protocols that key addresses by + * (address, chain). */ sourceChain?: Blockchain; }; From e145969c20051a327a156d8b81f8f9309c346dd2 Mon Sep 17 00:00:00 2001 From: rickalon Date: Mon, 13 Jul 2026 11:11:58 -0400 Subject: [PATCH 25/27] refactor(protocols): apply review round 3 to the SDA interface - errors: trim the ValueError description. - types/: strip the leading JSDoc @typedef blocks from the SDA declarations, so the file opens at its first declaration like every sibling .d.ts. - SdaDepositAddress: drop the `quote` field. Only a quote-bound protocol populates it, so it belongs on that protocol's own type, not the shared one. - SdaTransfer: reduce to `id` + `status`. Verified against the four providers' APIs: Candide's forwarding_getStatus returns no amounts, fees, assets or updatedAt, so those fields cannot be shared. Protocols with richer records extend the type; fields can be added back later without a breaking change. --- src/errors.js | 3 +- src/protocols/sda-protocol.js | 9 - types/src/errors.d.ts | 3 +- types/src/protocols/sda-protocol.d.ts | 234 +------------------------- 4 files changed, 3 insertions(+), 246 deletions(-) diff --git a/src/errors.js b/src/errors.js index 1374763..42beff1 100644 --- a/src/errors.js +++ b/src/errors.js @@ -55,8 +55,7 @@ export class UnsupportedOperationError extends Error { export class ValueError extends Error { /** - * Create a new value error. Thrown when an argument has the correct type but - * violates a validation rule. + * Create a new value error. Thrown when an argument fails validation. * * @param {string} message - The error's message. */ diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 64c5ab7..8171549 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -151,7 +151,6 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. * @property {SdaToken} outputAsset - The asset delivered to the destination. * @property {string} destinationAddress - The resolved address that receives the delivered asset. - * @property {SdaDepositQuote} [quote] - The quote bound to this address, if any. * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. * @property {string} [refundAddress] - The refund address bound to this address. @@ -171,15 +170,7 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * * @typedef {Object} SdaTransfer * @property {string} id - The protocol identifier for this transfer. - * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not - * return it). * @property {SdaTransferStatus} status - The current status of the transfer. - * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. - * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. - * @property {string} [sourceTxHash] - The hash of the deposit transaction on the source chain. - * @property {string} [destinationTxHash] - The hash of the delivery transaction on the destination chain. - * @property {number} [createdAt] - Unix timestamp (seconds) when the transfer was first observed. - * @property {number} [updatedAt] - Unix timestamp (seconds) when the transfer was last updated. */ /** diff --git a/types/src/errors.d.ts b/types/src/errors.d.ts index 42caa20..f759bf8 100644 --- a/types/src/errors.d.ts +++ b/types/src/errors.d.ts @@ -25,8 +25,7 @@ export class UnsupportedOperationError extends Error { } export class ValueError extends Error { /** - * Create a new value error. Thrown when an argument has the correct type but - * violates a validation rule. + * Create a new value error. Thrown when an argument fails validation. * * @param {string} message - The error's message. */ diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 1a36a28..039cfa2 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -1,203 +1,4 @@ -/** @typedef {import('../wallet-account-read-only.js').IWalletAccountReadOnly} IWalletAccountReadOnly */ -/** @typedef {import('../wallet-account.js').IWalletAccount} IWalletAccount */ -/** @typedef {import('../errors.js').ValueError} ValueError */ -/** @typedef {import('../errors.js').NoSuchElementError} NoSuchElementError */ -/** - * A blockchain identifier: a numeric chain id (e.g. `1`) or a protocol-specific chain name (e.g. `'ethereum'`). - * - * @typedef {string | number} Blockchain - */ -/** - * A normalized token reference. `token` is the identifier the protocol expects in SDA calls; `address` is the - * on-chain contract address when applicable (absent for native gas tokens). - * - * @typedef {Object} SdaToken - * @property {string} token - The protocol-specific token identifier to use in SDA calls. - * @property {Blockchain} chain - The chain on which the token lives. - * @property {string} symbol - The token symbol (e.g., 'USDC', 'USDT'). - * @property {number} decimals - The number of decimal places for the token's base unit. - * @property {string} [address] - The token contract address, if applicable. - * @property {string} [name] - The token's full name. - */ -/** - * Per-route deposit limits, denominated in the base unit of the route's input token. Either bound may be absent - * when the protocol does not enforce it; a protocol that only enforces limits in another denomination (e.g. USD) - * omits `limits` rather than converting. - * - * @typedef {Object} SdaDepositAddressLimits - * @property {number | bigint} [min] - Minimum deposit amount, in the input token's base unit. - * @property {number | bigint} [max] - Maximum deposit amount, in the input token's base unit. - */ -/** - * Optional filters for narrowing route discovery. - * - * @typedef {Object} SdaRoutesOptions - * @property {Blockchain} [sourceChain] - Restrict to routes that accept deposits from this chain. - * @property {string} [sourceToken] - Restrict to routes that accept this input token. - * @property {Blockchain} [destinationChain] - Restrict to routes that deliver to this chain. - * @property {string} [outputAsset] - Restrict to routes that deliver this asset. - */ -/** - * A supported conversion route: one or more source chains and their accepted input tokens, the destination chain, - * and the asset delivered there. - * - * @typedef {Object} SdaRoute - * @property {Blockchain[]} sourceChains - The source chains this route accepts deposits from. A list because some - * protocols issue one address valid across a VM family. - * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. - * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {SdaToken} outputAsset - The asset delivered to the destination (e.g., USDT). - * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this route. - * @property {boolean} [reusable] - Whether addresses issued for this route can receive more than one deposit. - * @property {number} [estimatedDuration] - Typical end-to-end duration in seconds. - */ -/** - * Options for fetching a deposit quote — a non-binding estimate of what a given deposit would deliver. - * - * @typedef {Object} SdaDepositOptions - * @property {Blockchain} sourceChain - The chain the deposit originates from. - * @property {string} inputToken - The protocol identifier of the token being deposited. - * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} outputAsset - The protocol identifier of the asset to deliver. - * @property {number | bigint} inputAmount - The amount to deposit, in the input token's base unit. - */ -/** - * The category of a fee charged by the protocol. - * - * @typedef {'network' | 'protocol' | 'affiliate' | 'other'} SdaFeeType - */ -/** - * A single itemised fee. - * - * @typedef {Object} SdaFee - * @property {SdaFeeType} type - The category of the fee. - * @property {bigint} amount - The fee amount, in the fee token's base unit. - * @property {string} token - The token in which the fee is denominated. - * @property {Blockchain} [chain] - The chain on which the fee is charged. - * @property {boolean} [included] - Whether the fee is already reflected in the quoted output amount. - * @property {string} [description] - A human-readable description of the fee. - */ -/** - * A non-binding estimate of the asset delivered for a given deposit. - * - * @typedef {Object} SdaDepositQuote - * @property {Blockchain} inputChain - The chain the deposit originates from. - * @property {string} inputToken - The protocol identifier of the deposited token. - * @property {bigint} inputAmount - The amount deposited, in the input token's base unit. - * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} outputAsset - The protocol identifier of the delivered asset. - * @property {bigint} outputAmount - The estimated amount delivered, in the destination asset's base unit. - * @property {SdaFee[]} fees - Itemised fee breakdown. - * @property {string} [rate] - The effective conversion rate as a string, to avoid precision loss. - * @property {number} [expiry] - Unix timestamp (seconds) at which the quote expires. - * @property {string} [id] - The protocol quote identifier, if the protocol issues one. - */ -/** - * Options for creating a deposit address. - * - * @typedef {Object} SdaCreateDepositAddressOptions - * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Protocols - * that issue one address per VM family use the full list; single-chain protocols use a one-element list. - * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} outputAsset - The protocol identifier of the asset to deliver (e.g., USDT). - * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound - * account's address. - * @property {string} [inputToken] - The expected input token, when the protocol needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund - * style). - * @property {boolean} [reusable] - Request a reusable address, for protocols that let the caller pick reusable vs - * single-use per request. - */ -/** - * A deposit address plus its normalized descriptor: where it accepts deposits from, what it accepts, where it - * delivers, and its lifecycle metadata. - * - * @typedef {Object} SdaDepositAddress - * @property {string} address - The deposit address the user sends funds to. - * @property {string} id - The protocol identifier for this SDA, used for status, recovery and disabling. - * @property {Blockchain[]} sourceChains - The chains this address accepts deposits from. - * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. - * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {SdaToken} outputAsset - The asset delivered to the destination. - * @property {string} destinationAddress - The resolved address that receives the delivered asset. - * @property {SdaDepositQuote} [quote] - The quote bound to this address, if any. - * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this address. - * @property {boolean} reusable - Whether the address can receive more than one deposit. - * @property {string} [refundAddress] - The refund address bound to this address. - * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when the protocol's - * address activation is time-limited. - */ -/** - * The lifecycle status of a deposit/transfer through an SDA. - * - * @typedef {'pending' | 'detected' | 'processing' | 'completed' | 'failed' - * | 'refund-pending' | 'refunded' | 'expired'} SdaTransferStatus - */ -/** - * A single deposit observed at, and processed through, an SDA. - * - * @typedef {Object} SdaTransfer - * @property {string} id - The protocol identifier for this transfer. - * @property {string} [depositAddress] - The SDA the deposit was sent to, when known (a status-by-id lookup may not - * return it). - * @property {SdaTransferStatus} status - The current status of the transfer. - * @property {bigint} [inputAmount] - The amount deposited, in the input token's base unit. - * @property {bigint} [outputAmount] - The amount delivered, in the destination asset's base unit. - * @property {string} [sourceTxHash] - The hash of the deposit transaction on the source chain. - * @property {string} [destinationTxHash] - The hash of the delivery transaction on the destination chain. - * @property {number} [createdAt] - Unix timestamp (seconds) when the transfer was first observed. - * @property {number} [updatedAt] - Unix timestamp (seconds) when the transfer was last updated. - */ -/** - * Optional pagination/filtering for transfer history. - * - * @typedef {Object} SdaTransfersOptions - * @property {Blockchain} [sourceChain] - The source chain of the deposit address, required by protocols that key - * addresses by (address, chain). - * @property {number} [limit] - The maximum number of transfers to return. - * @property {number} [skip] - The number of transfers to skip, for offset-based pagination. - * @property {SdaTransferStatus} [status] - Restrict to transfers in this status. - */ -/** - * Recover a deposit by the SDA identifier. - * - * @typedef {Object} SdaRecoverById - * @property {string} id - The protocol SDA identifier (the `SdaDepositAddress.id`). - */ -/** - * Recover a deposit by its deposit address. - * - * @typedef {Object} SdaRecoverByAddress - * @property {string} address - The deposit address to reindex. - * @property {Blockchain} [sourceChain] - The chain of the deposit address, required by protocols that key addresses by - * (address, chain). - */ -/** - * Options for re-processing a deposit that was not picked up automatically (`reindex`). A caller identifies the - * deposit either by SDA id or by its deposit address. - * - * @typedef {SdaRecoverById | SdaRecoverByAddress} SdaRecoveryOptions - */ -/** - * The outcome of a recovery attempt. - * - * @typedef {Object} SdaRecoveryResult - * @property {'reindexed' | 'pending' | 'failed'} status - The result of the reindex attempt. - * @property {string} [address] - The address that was reindexed. - * @property {string} [id] - The protocol SDA identifier. - * @property {SdaTransfer} [transfer] - The transfer that was recovered, if one resulted. - * @property {string} [message] - A human-readable description of the outcome. - */ -/** - * Interface for "Smart Deposit Address" (SDA) protocols: services that issue a deposit address, accept a - * stablecoin (or native token) from a supported source chain, convert it, and deliver a chosen asset (e.g., USDT) - * to a chosen destination chain and address. - * - * The required core every protocol implements is route discovery and address creation; every other operation is - * optional. - * - * @interface - */ +/** @interface */ export interface ISdaProtocol { /** * Lists the conversion routes the protocol supports: source chains, accepted input tokens, output assets and @@ -737,10 +538,6 @@ export type SdaDepositAddress = { * - The resolved address that receives the delivered asset. */ destinationAddress: string; - /** - * - The quote bound to this address, if any. - */ - quote?: SdaDepositQuote; /** * - Deposit limits for this address. */ @@ -771,39 +568,10 @@ export type SdaTransfer = { * - The protocol identifier for this transfer. */ id: string; - /** - * - The SDA the deposit was sent to, when known (a status-by-id lookup may not - * return it). - */ - depositAddress?: string; /** * - The current status of the transfer. */ status: SdaTransferStatus; - /** - * - The amount deposited, in the input token's base unit. - */ - inputAmount?: bigint; - /** - * - The amount delivered, in the destination asset's base unit. - */ - outputAmount?: bigint; - /** - * - The hash of the deposit transaction on the source chain. - */ - sourceTxHash?: string; - /** - * - The hash of the delivery transaction on the destination chain. - */ - destinationTxHash?: string; - /** - * - Unix timestamp (seconds) when the transfer was first observed. - */ - createdAt?: number; - /** - * - Unix timestamp (seconds) when the transfer was last updated. - */ - updatedAt?: number; }; /** * Optional pagination/filtering for transfer history. From fa4cd7e5b5837e5f4c39a057d22ed4f263440115 Mon Sep 17 00:00:00 2001 From: rickalon Date: Mon, 13 Jul 2026 14:40:43 -0400 Subject: [PATCH 26/27] refactor(protocols): drop non-shared fields from SdaCreateDepositAddressOptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply the shared-field rule to the create options: a field belongs on the shared type only when every protocol has it. Verified against the providers' APIs: - inputToken: Relay (originCurrency) and Orchestra (sourceAsset, which binds the address to a single deposit token) both REQUIRE it; Rhino and Candide have no such parameter. Required for two, absent for two — so it belongs on their own options types, where it can be typed as required rather than shared as optional. - reusable: only Relay has a single-vs-multi-use toggle (strict). Rhino's reusePolicy is a different concept (reuse-existing vs create-new address), which the boolean cannot express; Orchestra and Candide addresses are always reusable. - refundAddress: supported by Relay (refundTo) and Rhino, absent from Orchestra's schema, and structurally N/A for self-custodial Candide. The descriptor keeps supportedInputTokens and reusable (required) — those are how a consumer learns what an address accepts and whether it is single-use. Fields can be added back later without a breaking change. .d.ts hand-edited, no build:types. --- src/protocols/sda-protocol.js | 6 ------ types/src/protocols/sda-protocol.d.ts | 18 ------------------ 2 files changed, 24 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 8171549..7d95c54 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -132,11 +132,6 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {string} outputAsset - The protocol identifier of the asset to deliver (e.g., USDT). * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound * account's address. - * @property {string} [inputToken] - The expected input token, when the protocol needs it declared up front. - * @property {string} [refundAddress] - The address that receives refunds if a deposit cannot be processed (push-refund - * style). - * @property {boolean} [reusable] - Request a reusable address, for protocols that let the caller pick reusable vs - * single-use per request. */ /** @@ -153,7 +148,6 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {string} destinationAddress - The resolved address that receives the delivered asset. * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. - * @property {string} [refundAddress] - The refund address bound to this address. * @property {number} [expiry] - Unix timestamp (seconds) at which the address's activation expires, when the protocol's * address activation is time-limited. */ diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 039cfa2..30b7923 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -490,20 +490,6 @@ export type SdaCreateDepositAddressOptions = { * account's address. */ destinationAddress?: string; - /** - * - The expected input token, when the protocol needs it declared up front. - */ - inputToken?: string; - /** - * - The address that receives refunds if a deposit cannot be processed (push-refund - * style). - */ - refundAddress?: string; - /** - * - Request a reusable address, for protocols that let the caller pick reusable vs - * single-use per request. - */ - reusable?: boolean; }; /** * A deposit address plus its normalized descriptor: where it accepts deposits from, what it accepts, where it @@ -546,10 +532,6 @@ export type SdaDepositAddress = { * - Whether the address can receive more than one deposit. */ reusable: boolean; - /** - * - The refund address bound to this address. - */ - refundAddress?: string; /** * - Unix timestamp (seconds) at which the address's activation expires, when the protocol's * address activation is time-limited. From 6538533c08f66357bc5a8d6bb5e8a41b3c83b266 Mon Sep 17 00:00:00 2001 From: rickalon Date: Fri, 17 Jul 2026 10:15:44 -0400 Subject: [PATCH 27/27] refactor(protocols): make outputAsset optional for per-token forwarding SDAs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some SDA protocols (Candide's forwarding address) don't converge to one output asset — they forward each deposited token to its own equivalent on the destination chain (USDC->USDC, USDT->USDT). The single mandatory outputAsset asserted one-address-one-asset, which is false for them and could make an integrator misattribute a deposit. Make outputAsset optional on the inputs and standing descriptors, meaning "the protocol delivers each input token as its own equivalent" when unset: - SdaCreateDepositAddressOptions.outputAsset, SdaDepositOptions.outputAsset (quote input) -> optional; createDepositAddress / quoteDeposit throw ValueError when the protocol requires one and none is provided. - SdaDepositAddress.outputAsset, SdaRoute.outputAsset (standing descriptors) -> optional; unset means per-token self-mapping. SdaDepositQuote.outputAsset stays required — a quote is per-deposit, so the delivered asset is always one concrete value. .d.ts hand-edited, no build:types. --- src/protocols/sda-protocol.js | 16 ++++++++++++---- types/src/protocols/sda-protocol.d.ts | 24 ++++++++++++++++-------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/protocols/sda-protocol.js b/src/protocols/sda-protocol.js index 7d95c54..d6cf997 100644 --- a/src/protocols/sda-protocol.js +++ b/src/protocols/sda-protocol.js @@ -71,7 +71,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * protocols issue one address valid across a VM family. * @property {SdaToken[]} inputTokens - The deposit tokens accepted on the source side. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {SdaToken} outputAsset - The asset delivered to the destination (e.g., USDT). + * @property {SdaToken} [outputAsset] - The asset delivered to the destination (e.g., USDT). If unset, the route + * delivers each input token as its own equivalent on the destination chain. * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this route. * @property {boolean} [reusable] - Whether addresses issued for this route can receive more than one deposit. * @property {number} [estimatedDuration] - Typical end-to-end duration in seconds. @@ -84,7 +85,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {Blockchain} sourceChain - The chain the deposit originates from. * @property {string} inputToken - The protocol identifier of the token being deposited. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} outputAsset - The protocol identifier of the asset to deliver. + * @property {string} [outputAsset] - The protocol identifier of the asset to deliver. Omit for protocols that deliver + * each input token as its own equivalent. * @property {number | bigint} inputAmount - The amount to deposit, in the input token's base unit. */ @@ -129,7 +131,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {Blockchain[]} sourceChains - One or more source chains the address should accept deposits from. Protocols * that issue one address per VM family use the full list; single-chain protocols use a one-element list. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {string} outputAsset - The protocol identifier of the asset to deliver (e.g., USDT). + * @property {string} [outputAsset] - The protocol identifier of the asset to deliver (e.g., USDT). Omit for protocols + * that deliver each input token as its own equivalent. * @property {string} [destinationAddress] - The address that receives the delivered asset. Defaults to the bound * account's address. */ @@ -144,7 +147,8 @@ import { NotImplementedError, UnsupportedOperationError } from '../errors.js' * @property {Blockchain[]} sourceChains - The chains this address accepts deposits from. * @property {SdaToken[]} supportedInputTokens - The tokens this address accepts. * @property {Blockchain} destinationChain - The chain the converted asset is delivered to. - * @property {SdaToken} outputAsset - The asset delivered to the destination. + * @property {SdaToken} [outputAsset] - The asset delivered to the destination. If unset, the address delivers each + * input token as its own equivalent on the destination chain. * @property {string} destinationAddress - The resolved address that receives the delivered asset. * @property {SdaDepositAddressLimits} [limits] - Deposit limits for this address. * @property {boolean} reusable - Whether the address can receive more than one deposit. @@ -243,6 +247,7 @@ export class ISdaProtocol { * @param {SdaDepositOptions} options - The quote options. * @returns {Promise} The quoted deposit details. * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {ValueError} If the protocol requires an output asset and none is provided. */ async quoteDeposit (options) { throw new NotImplementedError('quoteDeposit(options)') @@ -257,6 +262,7 @@ export class ISdaProtocol { * @param {SdaCreateDepositAddressOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {ValueError} If the protocol requires an output asset and none is provided. */ async createDepositAddress (options) { throw new NotImplementedError('createDepositAddress(options)') @@ -424,6 +430,7 @@ export default class SdaProtocol { * @param {SdaDepositOptions} options - The quote options. * @returns {Promise} The quoted deposit details. * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {ValueError} If the protocol requires an output asset and none is provided. */ async quoteDeposit (options) { throw new UnsupportedOperationError('quoteDeposit(options)') @@ -439,6 +446,7 @@ export default class SdaProtocol { * @param {SdaCreateDepositAddressOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {ValueError} If the protocol requires an output asset and none is provided. */ async createDepositAddress (options) { throw new NotImplementedError('createDepositAddress(options)') diff --git a/types/src/protocols/sda-protocol.d.ts b/types/src/protocols/sda-protocol.d.ts index 30b7923..144c97d 100644 --- a/types/src/protocols/sda-protocol.d.ts +++ b/types/src/protocols/sda-protocol.d.ts @@ -17,6 +17,7 @@ export interface ISdaProtocol { * @param {SdaDepositOptions} options - The quote options. * @returns {Promise} The quoted deposit details. * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {ValueError} If the protocol requires an output asset and none is provided. */ quoteDeposit(options: SdaDepositOptions): Promise; /** @@ -28,6 +29,7 @@ export interface ISdaProtocol { * @param {SdaCreateDepositAddressOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {ValueError} If the protocol requires an output asset and none is provided. */ createDepositAddress(options: SdaCreateDepositAddressOptions): Promise; /** @@ -163,6 +165,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @param {SdaDepositOptions} options - The quote options. * @returns {Promise} The quoted deposit details. * @throws {UnsupportedOperationError} If the protocol does not support this operation. + * @throws {ValueError} If the protocol requires an output asset and none is provided. */ quoteDeposit(options: SdaDepositOptions): Promise; /** @@ -175,6 +178,7 @@ export default abstract class SdaProtocol implements ISdaProtocol { * @param {SdaCreateDepositAddressOptions} options - The address creation options. * @returns {Promise} The created deposit addresses, one per distinct address. * @throws {ValueError} If `destinationAddress` is omitted and no account was bound at construction. + * @throws {ValueError} If the protocol requires an output asset and none is provided. */ createDepositAddress(options: SdaCreateDepositAddressOptions): Promise; /** @@ -349,9 +353,10 @@ export type SdaRoute = { */ destinationChain: Blockchain; /** - * - The asset delivered to the destination (e.g., USDT). + * - The asset delivered to the destination (e.g., USDT). If unset, the route + * delivers each input token as its own equivalent on the destination chain. */ - outputAsset: SdaToken; + outputAsset?: SdaToken; /** * - Deposit limits for this route. */ @@ -382,9 +387,10 @@ export type SdaDepositOptions = { */ destinationChain: Blockchain; /** - * - The protocol identifier of the asset to deliver. + * - The protocol identifier of the asset to deliver. Omit for protocols that deliver each input token as its + * own equivalent. */ - outputAsset: string; + outputAsset?: string; /** * - The amount to deposit, in the input token's base unit. */ @@ -482,9 +488,10 @@ export type SdaCreateDepositAddressOptions = { */ destinationChain: Blockchain; /** - * - The protocol identifier of the asset to deliver (e.g., USDT). + * - The protocol identifier of the asset to deliver (e.g., USDT). Omit for protocols that deliver each input + * token as its own equivalent. */ - outputAsset: string; + outputAsset?: string; /** * - The address that receives the delivered asset. Defaults to the bound * account's address. @@ -517,9 +524,10 @@ export type SdaDepositAddress = { */ destinationChain: Blockchain; /** - * - The asset delivered to the destination. + * - The asset delivered to the destination. If unset, the address delivers each input token as its own + * equivalent on the destination chain. */ - outputAsset: SdaToken; + outputAsset?: SdaToken; /** * - The resolved address that receives the delivered asset. */