Skip to main content

Driver

The driver: runs swaps to completion against the chain, in the SDK's wasm, over a server client, a chain client and a store the application supplies.

It creates swaps from a quote, watches the swap script's address, sends the protocol messages when their moment comes, claims a swap-out and refunds a swap-in through the cheapest path available, and saves every change to your store, so a restart resumes where it left off. It trusts only what it can verify: the terms are checked against your own request at creation and frozen, and from each later read it takes the server's key and preimage on their own merit, and its status label and cancel reason for display only.

Two ways to drive. Driver.run drives every stored swap in the background and Driver.subscribe yields each change; a SwapHandle drives one swap by hand with SwapHandle.advance.

What you supply is three interfaces, two of which the SDK's own classes satisfy as they are: AnySwapClient is a ServerClient, BitcoinEsplora and LiquidEsplora are ChainClients, and the SwapStore is yours, with MemoryStore for tests. AnySwapClient.signals is the optional Signals channel that paces the loops, and Destinations the optional hook for a wallet that mints its addresses on demand.

Classes​

Driver​

Drives every swap in its store. Build one at startup, register a chain client per chain, then either spawn Driver.run and watch Driver.subscribe, or drive one swap at a time through its SwapHandle.

Constructors​

Constructor​
new Driver(
ctx: SwapContext,
server: ServerClient,
store: SwapStore): Driver;
Parameters​
ParameterTypeDescription
ctxSwapContextThe wallet and chain parameters every swap shares.
serverServerClientThe server API; an AnySwapClient with auth.
storeSwapStoreWhere records live between passes and across restarts.
Returns​

Driver

Accessors​

state​
Get Signature​
get state(): RunState;

Where run is: Idle before it, then Running, Stopping and Stopped.

Returns​

RunState

Methods​

get()​
get(swapId: string): Promise<SwapHandle | undefined>;

The stored swap as a fresh handle, or undefined.

Parameters​
ParameterType
swapIdstring
Returns​

Promise<SwapHandle | undefined>

quote()​
quote(
source: SwapEndpoint,
dest: SwapEndpoint,
target: TargetAmount): Promise<Quote>;

A quote for a swap from source to dest: one read of the server's terms for one amount, with the quote_id that locks its base fee until expires_at. target pins either end, { receive } or { send }. Show it, create the swap when the user accepts.

Parameters​
ParameterType
sourceSwapEndpoint
destSwapEndpoint
targetTargetAmount
Returns​

Promise<Quote>

restore()​
restore(swapId: string): Promise<SwapHandle>;

Rebuilds one swap from the server's copy of the signed request that created it, verified against this wallet's identity key, for a store that lost it. It is Restoring until its first pass, which Driver.run makes, or SwapHandle.step. A record already stored is returned as is.

Parameters​
ParameterType
swapIdstring
Returns​

Promise<SwapHandle>

restoreAll()​
restoreAll(): Promise<RestoreReport>;

Rebuilds every swap the server lists for this wallet that the store lacks, as Driver.restore does one, and reports each swap restored or the check that failed it.

Returns​

Promise<RestoreReport>

run()​
run(): void;

Drives every swap in the store that is not quiet, in the background, until Driver.stop. Every pass re-reads the store, so a swap created or restored is driven from the next pass on; a second call is a no-op. Watch Driver.subscribe for what changed.

Returns​

void

signal()​
signal(swapId: string): void;

Wakes any loop waiting on swapId, as a signal from the channel would.

Parameters​
ParameterType
swapIdstring
Returns​

void

stop()​
stop(): void;

Ends Driver.run after the pass in flight. Final: a stopped driver does not run again, and its Updates end once drained. Handles still step and advance.

Returns​

void

subscribe()​
subscribe(): Updates;

Every swap a pass changed, from Driver.run and from SwapHandle.step.

Returns​

Updates

swapIn()​
swapIn(
quote: Quote,
claimInvoice: string,
refundAddress?: string): Promise<SwapHandle>;

Creates a swap-in against quote with the invoice minted for its receive_amount. The handle carries where and how much to deposit. A refundAddress is signed with the request and paid by any refund; without one the Destinations hook is asked when there is something to take, or SwapHandle.refund is called with an address.

Parameters​
ParameterType
quoteQuote
claimInvoicestring
refundAddress?string
Returns​

Promise<SwapHandle>

swapOut()​
swapOut(quote: Quote, claimAddress?: string): Promise<SwapHandle>;

Creates a swap-out against quote paying claimAddress, or the address Destinations gives without one. The handle carries the hold invoice to pay.

Parameters​
ParameterType
quoteQuote
claimAddress?string
Returns​

Promise<SwapHandle>

waitStopped()​
waitStopped(): Promise<void>;

Resolves once run has exited and no pass writes to the store.

Returns​

Promise<void>

withChain()​
withChain(
chain: Chain,
client: ChainClient,
config?: ChainConfig): this;

Registers a chain with its client, under the chain's default policy with config applied over it; a chain without one cannot be swapped on. Throws an Invalid DriverError for a maxFeeRate that is not finite and positive.

Parameters​
ParameterType
chainChain
clientChainClient
config?ChainConfig
Returns​

this

withConfig()​
withConfig(config: DriverConfig): this;

Sets the driver-wide knobs, each one left out at its default.

Parameters​
ParameterType
configDriverConfig
Returns​

this

withDestinations()​
withDestinations(destinations: Destinations): this;

Takes the hook asked for an address when a swap is created without one: Driver.swapOut with no claimAddress, and a swap-in with no refund address once there is something to take.

Parameters​
ParameterType
destinationsDestinations
Returns​

this

withSignals()​
withSignals(signals: Signals): this;

Takes a push channel to pace the loops by, typically AnySwapClient.signals. Without one they pace on the poll interval alone.

Parameters​
ParameterType
signalsSignals
Returns​

this


DriverError​

What a driver call failed with. code names the kind of failure, keeping the server's own code names for its answers; retryable means the chain or server was unreachable and the next call sees the same world, where anything else needs attention before acting again.

Extends​

  • Error

Constructors​

Constructor​
new DriverError(
message: string,
code: DriverErrorCode,
retryable: boolean,
httpStatus?: number): DriverError;
Parameters​
ParameterTypeDescription
messagestring-
codeDriverErrorCode-
retryableboolean-
httpStatus?numberThe HTTP status the server answered with, undefined when the failure is not an answer from the server.
Returns​

DriverError

Overrides​
Error.constructor

Properties​

PropertyModifierTypeDescriptionInherited from
cause?publicunknown-Error.cause
codereadonlyDriverErrorCode--
httpStatus?readonlynumberThe HTTP status the server answered with, undefined when the failure is not an answer from the server.-
messagepublicstring-Error.message
namepublicstring-Error.name
retryablereadonlyboolean--
stack?publicstring-Error.stack
stackTraceLimitstaticnumberThe Error.stackTraceLimit property specifies the number of stack frames collected by a stack trace (whether generated by new Error().stack or Error.captureStackTrace(obj)). The default value is 10 but may be set to any valid JavaScript number. Changes will affect any stack trace captured after the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames.Error.stackTraceLimit

Methods​

captureStackTrace()​
static captureStackTrace(targetObject: object, constructorOpt?: Function): void;

Creates a .stack property on targetObject, which when accessed returns a string representing the location in the code at which Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // Similar to `new Error().stack`

The first line of the trace will be prefixed with ${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames above constructorOpt, including constructorOpt, will be omitted from the generated stack trace.

The constructorOpt argument is useful for hiding implementation details of error generation from the user. For instance:

function a() {
b();
}

function b() {
c();
}

function c() {
// Create an error without stack trace to avoid calculating the stack trace twice.
const { stackTraceLimit } = Error;
Error.stackTraceLimit = 0;
const error = new Error();
Error.stackTraceLimit = stackTraceLimit;

// Capture the stack trace above function b
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
throw error;
}

a();
Parameters​
ParameterType
targetObjectobject
constructorOpt?Function
Returns​

void

Inherited from​
Error.captureStackTrace
prepareStackTrace()​
static prepareStackTrace(err: Error, stackTraces: CallSite[]): any;
Parameters​
ParameterType
errError
stackTracesCallSite[]
Returns​

any

See​

https://v8.dev/docs/stack-trace-api#customizing-stack-traces

Inherited from​
Error.prepareStackTrace

MemoryStore​

Records kept in memory, for tests and short-lived tools.

Implements​

Constructors​

Constructor​
new MemoryStore(): MemoryStore;
Returns​

MemoryStore

Methods​

list()​
list(): Promise<string[]>;

Every stored swap_id, without reading the records, so one that fails to load stops only its own swap.

Returns​

Promise<string[]>

Implementation of​

SwapStore.list

load()​
load(swapId: string): Promise<SwapRecord | undefined>;
Parameters​
ParameterType
swapIdstring
Returns​

Promise<SwapRecord | undefined>

Implementation of​

SwapStore.load

save()​
save(record: SwapRecord): Promise<void>;

Replaces any record already stored under the same swap_id.

Parameters​
ParameterType
recordSwapRecord
Returns​

Promise<void>

Implementation of​

SwapStore.save


SwapHandle​

A swap bound to its driver: its id and type, and the calls that act on it. It keeps no copy of the swap, so every read is the store's newest.

Accessors​

error​
Get Signature​
get error(): DriverError | undefined;

The error of this swap's last failing pass under Driver.run, undefined after a working one.

Returns​

DriverError | undefined

swapId​
Get Signature​
get swapId(): string;
Returns​

string

swapType​
Get Signature​
get swapType(): SwapType;
Returns​

SwapType

Methods​

address()​
address(): Promise<string>;

Swap-in: the deposit address; the record's send_amount goes here.

Returns​

Promise<string>

advance()​
advance(): Promise<DriverStatus>;

Waits for a signal or the poll interval, whichever comes first, then makes one pass. Resolves to the status it left.

Returns​

Promise<DriverStatus>

cancel()​
cancel(): Promise<Cancellation>;
Returns​

Promise<Cancellation>

claimInvoice()​
claimInvoice(): Promise<string>;

Swap-out: the hold invoice to pay; the record's send_amount is its amount.

Returns​

Promise<string>

funds()​
funds(): Promise<Fund[]>;

Swap-in: what under the script is the user's, as the last pass left it.

Returns​

Promise<Fund[]>

isTerminal()​
isTerminal(): Promise<boolean>;

Completed or Canceled: the deal is settled. A swap-in's SwapHandle.funds may still be moving.

Returns​

Promise<boolean>

record()​
record(): Promise<SwapRecord>;

The record as the last pass left it.

Returns​

Promise<SwapRecord>

refund()​
refund(destination: string): Promise<string | undefined>;

Swap-in: one pass that refunds the Refundable funds to destination, in any status. The refund's txid, or undefined with nothing refundable yet or an earlier refund still Refunding.

Parameters​
ParameterType
destinationstring
Returns​

Promise<string | undefined>

status()​
status(): Promise<DriverStatus>;

The status as the last pass left it.

Returns​

Promise<DriverStatus>

step()​
step(): Promise<DriverStatus>;

One pass, now. Resolves to the status it left.

Returns​

Promise<DriverStatus>


Updates​

Every swap a pass changed, as Driver.run and SwapHandle.step produce them, plus a handle for every failing pass under run. Iterate it with for await.

Implements​

Methods​

[asyncIterator]()​
asyncIterator: AsyncIterator<SwapHandle>;
Returns​

AsyncIterator<SwapHandle>

Implementation of​
AsyncIterable.[asyncIterator]
next()​
next(): Promise<SwapHandle | undefined>;

The next changed swap, or undefined once the driver is stopped and drained, or gone.

Returns​

Promise<SwapHandle | undefined>

Interfaces​

ChainClient​

Query and broadcast on one chain. A throw fails the pass, and the next pass asks again.

Methods​

addressHistory()​
addressHistory(address: string): Promise<HistoryTx[]>;

Every transaction paying or spending address, mempool included.

Parameters​
ParameterType
addressstring
Returns​

Promise<HistoryTx[]>

broadcast()​
broadcast(txHex: string): Promise<string>;

Broadcasts a signed transaction and returns its txid.

Parameters​
ParameterType
txHexstring
Returns​

Promise<string>

getFeeRate()​
getFeeRate(target: number): Promise<number>;

Estimate for confirmation within target blocks, in sat/vB.

Parameters​
ParameterType
targetnumber
Returns​

Promise<number>

getHeight()​
getHeight(): Promise<number>;
Returns​

Promise<number>

outspend()​
outspend(txid: string, vout: number): Promise<HistoryTx | undefined>;

The transaction spending output vout of txid, mempool included, or undefined while it is unspent: Esplora's /tx/:txid/outspend/:vout with the spending transaction fetched. Read from the output itself, so it is found however many other transactions crowd the address's history.

Parameters​
ParameterType
txidstring
voutnumber
Returns​

Promise<HistoryTx | undefined>


ChainConfig​

The knobs that mean something per chain, for Driver.withChain. Each one left out keeps the chain's default. Depths are in blocks, counted from a confirmation, since a timelock on an unconfirmed output has not started.

Properties​

PropertyTypeDescription
blockTime?numberExpected block interval in seconds, to compare a block deadline with a wall-clock one. Defaults to 600 on Bitcoin, 60 on Liquid.
claimMargin?numberHow much CSV must remain when a swap-out's preimage goes out. Defaults to 252 on Bitcoin, 2520 on Liquid.
maxFeeRate?numberThe rate no spend goes above, in sat/vB, finite and positive. A swap-out holds its preimage while the estimate is above it. Defaults to 100 on Bitcoin, 1 on Liquid.
maxRefundCsv?numberThe longest CSV accepted on a swap-in deposit. Defaults to 1008 on Bitcoin, 10080 on Liquid.
minConfs?numberThe depth that releases a swap-out's preimage, and that a spend reaches before it closes a swap. Defaults to 3 on Bitcoin, 2 on Liquid.

Destinations​

Where funds land when a swap is created without an address, for a wallet that mints one on demand. A throw or a rejection leaves no address and fails whatever asked for one. Resolve promptly: the loop ticks swaps one at a time and waits on it.

Methods​

claimAddress()​
claimAddress(chain: Chain): string | Promise<string>;

Where a swap-out on chain claims to; asked at creation.

Parameters​
ParameterType
chainChain
Returns​

string | Promise<string>

refundAddress()​
refundAddress(chain: Chain): string | Promise<string>;

Where a swap-in on chain refunds to; asked with something refundable under the script.

Parameters​
ParameterType
chainChain
Returns​

string | Promise<string>


DriverConfig​

The knobs true of the driver whatever it swaps, for Driver.withConfig. Each one left out keeps its default. Times are seconds on the driver's own clock, never one the server reports.

Properties​

PropertyTypeDescription
feeConfTarget?numberThe target every fee estimate is read at, in blocks. Defaults to 3.
feeRateCacheTtl?numberHow long a chain's fee estimate is reused before it is read again; zero reads it every pass. Defaults to 30.
fundingTimeout?numberFrom creation, the latest a swap with nothing on chain asks the server to cancel; the server's window for a swap-in deposit, or a swap-out's claim invoice expiry, ends it sooner. Defaults to 86400.
maxHtlcHold?numberThe longest a swap-out's hold invoice may hold the payment, in Lightning blocks. Defaults to 1008.
pollInterval?numberThe longest wait between passes without a signal. Defaults to 15.
refundTimeout?numberFrom a swap-in's end, how long it keeps getting passes once nothing of the user's is left to move. Defaults to 3600.
serverTimeout?numberHow long a call to the server may go unanswered before it fails as Transport. Defaults to 30.

Endpoint​

One end of a swap: on chain when chain is set, Lightning when it is not. Keys are hex strings.

Properties​

PropertyTypeDescription
assetstring | null-
chainChain | null-
csvnumber | null-
destinationstring | nullOn chain, where the end pays out: the refund address of a source, the claim address of a dest.
invoicestring | nullLightning: the invoice, paid to a swap-in, paid by a swap-out.
server_blinding_pubkeystring | null-
server_privkeystring | nullThe server's key, once it arrived and verified against server_pubkey.
server_pubkeystring | null-

Fund​

Swap-in: one output under the script that is the user's. The deposit the server was paid for is not one, nor is an output the server took by its own path.

Properties​

PropertyTypeDescription
amountstringIn sats, as a decimal string.
outpointOutPoint-
stateFundState-

Quote​

A quote: one read of the server's terms, locked by quote_id until expires_at. Amounts in sats, as decimal strings.

Properties​

PropertyTypeDescription
await_opening_txnumber | nullSwap-in: how long after creation the server waits for the deposit to confirm, in seconds, and so the expiry to give the claim invoice.
base_feestring-
dest_asset_idstring | null-
dest_chainstring | null-
expires_atnumberUnix seconds.
feestringThe whole spread, send_amount - receive_amount.
quote_idstring-
receive_amountstring-
send_amountstring-
service_feestringsend_amount - receive_amount - base_fee.
source_asset_idstring | null-
source_chainstring | null-

RestoreReport​

What Driver.restoreAll found among the swaps the server lists and the store lacks.

Properties​

PropertyTypeDescription
failed{ error: DriverError; swapId: string; }[]Each swap that failed a check, with the check; Unsigned for one the server holds no signed request for.
restoredstring[]The ids of the swaps now in the store.

Sent​

The messages already delivered, since a message leaves no trace on chain.

Properties​

PropertyType
cancelboolean
preimageboolean
refund_keyboolean

ServerCancel​

The server's cancel in its words: its error code and the message the cancel came with. Shown, never verified.

Properties​

PropertyType
cancel_messagestring | null
error_typeSwapErrorCode | null

ServerClient​

The server API as the driver calls it, over the wire objects as they are. Amounts in a body are decimal strings, as on the wire; every integer is a number. A throw with a numeric status is the server's answer, its code the server's error code; any other throw is the server being unreachable.

Methods​

cancel()​
cancel(swapId: string, body: unknown): Promise<void>;
Parameters​
ParameterType
swapIdstring
bodyunknown
Returns​

Promise<void>

createSwap()​
createSwap(body: unknown): Promise<void>;
Parameters​
ParameterType
bodyunknown
Returns​

Promise<void>

getInfo()​
getInfo(): Promise<unknown>;
Returns​

Promise<unknown>

getSwap()​
getSwap(swapId: string): Promise<unknown>;
Parameters​
ParameterType
swapIdstring
Returns​

Promise<unknown>

listSwaps()​
listSwaps(query: unknown): Promise<unknown>;
Parameters​
ParameterType
queryunknown
Returns​

Promise<unknown>

revealClaim()​
revealClaim(swapId: string, body: unknown): Promise<void>;
Parameters​
ParameterType
swapIdstring
bodyunknown
Returns​

Promise<void>

revealPreimage()​
revealPreimage(swapId: string, body: unknown): Promise<void>;
Parameters​
ParameterType
swapIdstring
bodyunknown
Returns​

Promise<void>


Signals​

A push channel naming swaps worth reading sooner; resolves undefined while it is down.

Methods​

next()​
next(): Promise<string | undefined>;
Returns​

Promise<string | undefined>


SwapRecord​

One swap as the driver keeps it: both ends frozen at acceptance, the status, and what the chain cannot report. The JSON form of the Rust record, and the document a SwapStore holds. Amounts are sats as decimal strings.

Properties​

PropertyTypeDescription
base_feestringThe server's split of send_amount - receive_amount: base_fee for its transactions, service_fee the rest.
destEndpoint-
ended_atnumber | nullWhen the status first became final: the driver's clock, or the server's on restore.
fundsFund[]Swap-in: what under the script is the user's, as the last pass left it.
payment_hashnumber[]-
preimagenumber[] | nullThe preimage once seen and hashed to payment_hash: a swap-in's from the server, a swap-out's own from a claim of ours on chain.
receive_amountstring-
send_amountstring-
sentSent-
server_cancelServerCancel | nullThe server's cancel, kept from the first pass that read one.
server_statusstring | nullThe server's status label as the last pass that reached it read it. Shown, never decided on.
service_feestring-
sourceEndpoint-
started_atnumberThe driver's own clock at acceptance.
statusDriverStatus-
swap_idstring-
versionnumber-

SwapStore​

Durable swap records, wanted by id and nothing else. Back it with anything that outlives the page, keeping each record as the object given: a future SDK can upgrade old records only if they kept their self-describing form.

Methods​

list()​
list(): Promise<string[]>;

Every stored swap_id, without reading the records, so one that fails to load stops only its own swap.

Returns​

Promise<string[]>

load()​
load(swapId: string): Promise<SwapRecord | undefined>;
Parameters​
ParameterType
swapIdstring
Returns​

Promise<SwapRecord | undefined>

save()​
save(record: SwapRecord): Promise<void>;

Replaces any record already stored under the same swap_id.

Parameters​
ParameterType
recordSwapRecord
Returns​

Promise<void>

Type Aliases​

Cancellation​

type Cancellation = "Requested" | "TooLate";

The answer to SwapHandle.cancel: Requested means the swap is canceled or closes when the server confirms it, and TooLate means the payment landed first and the swap continues.


CancelReason​

type CancelReason =
| {
kind: "ServerCanceled";
} & ServerCancel
| {
kind: "CsvMatured";
}
| {
kind: "ServerRefunded";
}
| {
at: Height;
kind: "ClaimWindowClosed";
};

Why a swap ended Canceled, named by kind. ServerCanceled: the server gave the swap up, with its ServerCancel words beside. CsvMatured: a swap-in's deposit matured its CSV with no word from the server. ServerRefunded: the server's CSV refund took a swap-out's opening. ClaimWindowClosed: less than claimMargin of a swap-out's CSV was left before its preimage went out, so the payment fails back once the server's CSV refund takes the opening, from block at.


DriverErrorCode​

type DriverErrorCode =
| "Internal"
| "BadRequest"
| "NotImplemented"
| "QuoteExpired"
| "SwapNotFound"
| "SwapAlreadyExists"
| "TransitionRejected"
| "PolicyError"
| "TooManyConnections"
| "Unavailable"
| "Forbidden"
| "PayloadTooLarge"
| "InsufficientLiquidity"
| "Transport"
| "Chain"
| "Store"
| "Wallet"
| "Destination"
| "Refused"
| "Unsigned"
| "BadSignature"
| "ChainNotConfigured"
| "UnknownSwap"
| "Unsupported"
| "WrongSwapType"
| "NotQuotable"
| "AmountOutOfRange"
| "Invalid";

The kind of a DriverError. The server's codes keep the server's names; the rest name what failed on this side.


DriverStatus​

type DriverStatus =
| {
status: "Restoring";
}
| {
cancel_sent: boolean;
expires_at: Timestamp;
status: "Funding";
}
| {
confs: number;
fee_rate_spike?: number;
max_fee_rate: number;
needed: number;
status: "Confirming";
txid: string;
}
| {
claim: string | null;
confs: number;
fee: string | null;
opening: string;
status: "Claiming";
}
| {
fee: string | null;
opening: string;
payout: string | null;
status: "Completed";
}
| {
opening: string | null;
reason: CancelReason;
status: "Canceled";
};

Where a swap stands, resolved from the chain on every pass. status names the variant and its fields sit beside it, so status.status === "Canceled" narrows. Deadlines are absolute, so two quiet passes produce an equal status. expires_at is the time to pay by: a swap-in's is minConfs + 1 blocks before its invoice expires, a swap-out's no later than the expiry of its claim invoice. Restoring is no pass's answer: Driver.restore sets it and the first pass replaces it. fee is what a swap-out's claim pays, in sats as a decimal string, read from the transaction on chain; null on a swap-in's Completed, whose payout is the server's. Confirming carries the chain's maxFeeRate as max_fee_rate, and a swap-out's carries fee_rate_spike, the fee estimate in sat/vB, while it is above that and holds the preimage, and leaves it out otherwise.


FundState​

type FundState =
| {
at: Height | null;
state: "Locked";
}
| {
state: "Refundable";
}
| {
state: "Dust";
}
| {
fee: string | null;
state: "Refunding";
txid: string;
}
| {
fee: string | null;
state: "Refunded";
txid: string;
};

Where the refund of one output of the user's stands, named by state. Locked is under the CSV until block at, or unconfirmed (null); Refundable is spendable and worth its refund, so the next refund takes it; Dust is spendable but not worth its fee plus the smallest refund output at the last rate, and run refunds it once the rate falls far enough; Refunding and Refunded are in our refund txid, out of a block and in one. Their fee is the whole refund transaction's, in sats as a decimal string, so every fund it took shows the same one: group by txid before adding fees up.


Height​

type Height = number;

A block height on the swap's chain.


RunState​

type RunState = "Idle" | "Running" | "Stopping" | "Stopped";

Where run is. Stopped is final.


TargetAmount​

type TargetAmount =
| {
receive: bigint;
}
| {
send: bigint;
};

The end a quote is pinned by: the amount to receive, or the amount to send, in sats.


Timestamp​

type Timestamp = number;

Unix seconds.