@dcasegr/amm-client 0.0.0-stage → 0.38.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -2
- package/dist/client.d.ts +534 -0
- package/dist/client.js +1319 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +75 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +53 -0
- package/dist/lib/config.d.ts +42 -0
- package/dist/lib/config.js +64 -0
- package/dist/lib/keys.d.ts +42 -0
- package/dist/lib/keys.js +48 -0
- package/dist/lib/overlay.d.ts +190 -0
- package/dist/lib/overlay.js +302 -0
- package/dist/lib/skein.d.ts +82 -0
- package/dist/lib/skein.js +142 -0
- package/dist/lib/tokenId.d.ts +72 -0
- package/dist/lib/tokenId.js +121 -0
- package/dist/lp/amounts.d.ts +12 -0
- package/dist/lp/amounts.js +38 -0
- package/dist/lp/close.d.ts +90 -0
- package/dist/lp/close.js +140 -0
- package/dist/lp/images.d.ts +11 -0
- package/dist/lp/images.js +45 -0
- package/dist/lp/inventory.d.ts +68 -0
- package/dist/lp/inventory.js +84 -0
- package/dist/lp/poolDeploy.d.ts +220 -0
- package/dist/lp/poolDeploy.js +291 -0
- package/dist/lp/poolRows.d.ts +13 -0
- package/dist/lp/poolRows.js +19 -0
- package/dist/lp/positions.d.ts +81 -0
- package/dist/lp/positions.js +144 -0
- package/dist/lp/rebalance.d.ts +101 -0
- package/dist/lp/rebalance.js +168 -0
- package/dist/lp/wallet.d.ts +12 -0
- package/dist/lp/wallet.js +19 -0
- package/dist/market/activeSwap.d.ts +158 -0
- package/dist/market/activeSwap.js +320 -0
- package/dist/market/plan.d.ts +85 -0
- package/dist/market/plan.js +149 -0
- package/dist/market/prices.d.ts +13 -0
- package/dist/market/prices.js +14 -0
- package/dist/market/relay.d.ts +84 -0
- package/dist/market/relay.js +201 -0
- package/dist/market/swapAction.d.ts +306 -0
- package/dist/market/swapAction.js +508 -0
- package/dist/market/swapFlow.d.ts +58 -0
- package/dist/market/swapFlow.js +86 -0
- package/dist/market/view.d.ts +58 -0
- package/dist/market/view.js +63 -0
- package/dist/pool/artifact.d.ts +23 -0
- package/dist/pool/artifact.js +38 -0
- package/dist/pool/index.d.ts +8 -0
- package/dist/pool/index.js +8 -0
- package/dist/pool/oneColor.d.ts +19 -0
- package/dist/pool/oneColor.js +39 -0
- package/dist/pool/pool.artifact.json +5069 -0
- package/dist/pool/runar-ir-schema.d.ts +21 -0
- package/dist/pool/runar-ir-schema.js +18 -0
- package/dist/pool/template.d.ts +295 -0
- package/dist/pool/template.js +715 -0
- package/dist/vendor/runar/LICENSE +21 -0
- package/dist/vendor/runar/NOTICE +15 -0
- package/dist/vendor/runar/runar-ir-schema/src/abi-type-encoding.d.ts +42 -0
- package/dist/vendor/runar/runar-ir-schema/src/abi-type-encoding.js +53 -0
- package/dist/vendor/runar/runar-ir-schema/src/anf-ir.d.ts +221 -0
- package/dist/vendor/runar/runar-ir-schema/src/anf-ir.js +33 -0
- package/dist/vendor/runar/runar-ir-schema/src/artifact.d.ts +288 -0
- package/dist/vendor/runar/runar-ir-schema/src/artifact.js +8 -0
- package/dist/vendor/runar/runar-ir-schema/src/canonical-json.d.ts +43 -0
- package/dist/vendor/runar/runar-ir-schema/src/canonical-json.js +204 -0
- package/dist/vendor/runar/runar-ir-schema/src/input-limits.d.ts +89 -0
- package/dist/vendor/runar/runar-ir-schema/src/input-limits.js +100 -0
- package/dist/vendor/runar/runar-ir-schema/src/stack-ir.d.ts +118 -0
- package/dist/vendor/runar/runar-ir-schema/src/stack-ir.js +8 -0
- package/dist/vendor/runar/runar-ir-schema/src/state-layout.d.ts +56 -0
- package/dist/vendor/runar/runar-ir-schema/src/state-layout.js +145 -0
- package/dist/vendor/runar/runar-ir-schema/src/unknown-anf-kind-error.d.ts +18 -0
- package/dist/vendor/runar/runar-ir-schema/src/unknown-anf-kind-error.js +25 -0
- package/dist/vendor/runar/runar-sdk/src/anf-interpreter.d.ts +160 -0
- package/dist/vendor/runar/runar-sdk/src/anf-interpreter.js +1149 -0
- package/dist/vendor/runar/runar-sdk/src/calling.d.ts +98 -0
- package/dist/vendor/runar/runar-sdk/src/calling.js +209 -0
- package/dist/vendor/runar/runar-sdk/src/contract.d.ts +443 -0
- package/dist/vendor/runar/runar-sdk/src/contract.js +2516 -0
- package/dist/vendor/runar/runar-sdk/src/deployment.d.ts +36 -0
- package/dist/vendor/runar/runar-sdk/src/deployment.js +119 -0
- package/dist/vendor/runar/runar-sdk/src/errors.d.ts +46 -0
- package/dist/vendor/runar/runar-sdk/src/errors.js +59 -0
- package/dist/vendor/runar/runar-sdk/src/oppushtx.d.ts +23 -0
- package/dist/vendor/runar/runar-sdk/src/oppushtx.js +95 -0
- package/dist/vendor/runar/runar-sdk/src/ordinals/envelope.d.ts +31 -0
- package/dist/vendor/runar/runar-sdk/src/ordinals/envelope.js +272 -0
- package/dist/vendor/runar/runar-sdk/src/ordinals/types.d.ts +21 -0
- package/dist/vendor/runar/runar-sdk/src/ordinals/types.js +4 -0
- package/dist/vendor/runar/runar-sdk/src/providers/provider.d.ts +65 -0
- package/dist/vendor/runar/runar-sdk/src/providers/provider.js +59 -0
- package/dist/vendor/runar/runar-sdk/src/providers/wallet-provider.d.ts +94 -0
- package/dist/vendor/runar/runar-sdk/src/providers/wallet-provider.js +297 -0
- package/dist/vendor/runar/runar-sdk/src/script-utils.d.ts +54 -0
- package/dist/vendor/runar/runar-sdk/src/script-utils.js +252 -0
- package/dist/vendor/runar/runar-sdk/src/signers/signer.d.ts +17 -0
- package/dist/vendor/runar/runar-sdk/src/signers/signer.js +4 -0
- package/dist/vendor/runar/runar-sdk/src/spend-safety.d.ts +31 -0
- package/dist/vendor/runar/runar-sdk/src/spend-safety.js +35 -0
- package/dist/vendor/runar/runar-sdk/src/state.d.ts +54 -0
- package/dist/vendor/runar/runar-sdk/src/state.js +584 -0
- package/dist/vendor/runar/runar-sdk/src/types.d.ts +227 -0
- package/dist/vendor/runar/runar-sdk/src/types.js +4 -0
- package/dist/vendor/runar/runar-sdk/src/unsound-primitives.d.ts +30 -0
- package/dist/vendor/runar/runar-sdk/src/unsound-primitives.js +42 -0
- package/dist/vendor/runar/runar-sdk.d.ts +9 -0
- package/dist/vendor/runar/runar-sdk.js +8 -0
- package/dist/wallet/action.d.ts +28 -0
- package/dist/wallet/action.js +42 -0
- package/dist/wallet/brc29.d.ts +83 -0
- package/dist/wallet/brc29.js +80 -0
- package/dist/wallet/color.d.ts +110 -0
- package/dist/wallet/color.js +134 -0
- package/dist/wallet/swapPayouts.d.ts +35 -0
- package/dist/wallet/swapPayouts.js +146 -0
- package/package.json +39 -4
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The marketplace relay (docs/notes.md, "Marketplace relay"): the user's own
|
|
3
|
+
* skein instance takes the funding and swap transactions and carries them to
|
|
4
|
+
* the pool's validator, so the browser needs no libp2p. Two functions on the
|
|
5
|
+
* AMM app's box, hosted by programs/amm-p2p:
|
|
6
|
+
*
|
|
7
|
+
* amm.swap.terms (reads) {} → {commissionPkh: bytes(20) | null, minCommissionBps}
|
|
8
|
+
* amm.swap.submit (writes) {funding, swap, pool, validator, peerId, expires} → the record
|
|
9
|
+
* (0.34.0, D15: the host holds the request until the relay thread
|
|
10
|
+
* rests, so the answer is the settled record; past the host's bound,
|
|
11
|
+
* 503: no answer yet)
|
|
12
|
+
* amm.swap.status (reads) {id} → the record (0.34.0: the pages read it plain, `GET
|
|
13
|
+
* <base>/swaps?id=`, src/lib/overlay.ts `readSwap`, behind "Check again")
|
|
14
|
+
*
|
|
15
|
+
* commissionPkh the address the relay takes the pool's commission at
|
|
16
|
+
* (Swap's `commissionPkh`; 0.16.0: hash160 of the instance's
|
|
17
|
+
* own derived key, no config); null when the instance names
|
|
18
|
+
* none, in which case a pool with a nonzero CommissionBps pays
|
|
19
|
+
* the commission to the taker's own key
|
|
20
|
+
* (src/market/swapAction.ts, "the commission")
|
|
21
|
+
* minCommissionBps this market's minimum commission (0.16.0: the record
|
|
22
|
+
* root sets, `amm/market`; absent or unset, 0): a listing
|
|
23
|
+
* preference, the pools the Swap page lists, not a rule
|
|
24
|
+
*
|
|
25
|
+
* funding bytes: the funding transaction as the wallet's AtomicBEEF (its
|
|
26
|
+
* ancestry with it: the validator's overlay verifies the unproven
|
|
27
|
+
* funding against parents in the BEEF); 0.29.0: the transaction
|
|
28
|
+
* of the leg's `amm-swap` sats output, the swap's input 1
|
|
29
|
+
* swap bytes: the raw swap, the validator's slot `OP_0`
|
|
30
|
+
* pool text `<txid>_<vout>`; validator bytes(33); expires unix ms
|
|
31
|
+
* peerId text: the validator's libp2p peer ID, from the pools route's row
|
|
32
|
+
* (`validatorPeerId`, 0.34.0: the host joined it from the beat): the
|
|
33
|
+
* relay dials it (or, when it is the relay's own node, hands the
|
|
34
|
+
* swap to its own validator program); it looks at no liveness
|
|
35
|
+
*
|
|
36
|
+
* record (programs/amm-p2p relay.zig `Record.answer`; id = sha256 of the swap, hex):
|
|
37
|
+
* {id, status: "pending" | "accepted" | "refused" | "timeout" | "failed", pool, expires, …,
|
|
38
|
+
* tx?, txid? accepted: the validator's signed swap
|
|
39
|
+
* reason?, detail?, refused / timeout / failed
|
|
40
|
+
* poolState?} refused: the pool's current state (the validator's)
|
|
41
|
+
* "failed" is the relay's transport failure (retryable by submitting again):
|
|
42
|
+
* the validator may or may not have the swap, so the page treats it as no
|
|
43
|
+
* answer, not as a refusal.
|
|
44
|
+
*
|
|
45
|
+
* Transport: the app's `/call` route (skein docs/APPS.md §4): `POST
|
|
46
|
+
* <VITE_AMM_OVERLAY>/call`, JSON body `{fn, args}`, answer `{fn, result}`
|
|
47
|
+
* (200) or `{fn, error: {code, message}}` (400 bad-request / bad-args, 403
|
|
48
|
+
* not-admitted, 404 unknown-fn, 409 read-only, 500 failed). The route is
|
|
49
|
+
* BRC-104 authenticated: the caller must be admitted to the app's box, so
|
|
50
|
+
* both calls go through the wallet-backed AuthFetch (src/wallet/authFetch.ts).
|
|
51
|
+
*
|
|
52
|
+
* Bytes in `args` are DAG-JSON bytes (`{"/": {"bytes": "<base64, unpadded>"}}`),
|
|
53
|
+
* how skein reads JSON bodies (docs/MESSAGES.md "The messagebox"); answers are
|
|
54
|
+
* read as DAG-JSON bytes, hex or a byte array.
|
|
55
|
+
*/
|
|
56
|
+
import { Utils } from "@bsv/sdk";
|
|
57
|
+
export const SWAP_SUBMIT_FN = "amm.swap.submit";
|
|
58
|
+
export const SWAP_STATUS_FN = "amm.swap.status";
|
|
59
|
+
export const SWAP_TERMS_FN = "amm.swap.terms";
|
|
60
|
+
export class RelayError extends Error {
|
|
61
|
+
code;
|
|
62
|
+
httpStatus;
|
|
63
|
+
constructor(code, message, httpStatus) {
|
|
64
|
+
super(`${code}: ${message}`);
|
|
65
|
+
this.code = code;
|
|
66
|
+
this.httpStatus = httpStatus;
|
|
67
|
+
this.name = "RelayError";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function dagBytes(bytes) {
|
|
71
|
+
return { "/": { bytes: Utils.toBase64(bytes).replace(/=+$/, "") } };
|
|
72
|
+
}
|
|
73
|
+
/** DAG-JSON bytes, hex, or a byte array → bytes; null otherwise. */
|
|
74
|
+
export function readBytes(x) {
|
|
75
|
+
if (Array.isArray(x) && x.every((b) => Number.isInteger(b) && b >= 0 && b < 256))
|
|
76
|
+
return x;
|
|
77
|
+
if (typeof x === "string" && /^([0-9a-f]{2})+$/i.test(x))
|
|
78
|
+
return Utils.toArray(x, "hex");
|
|
79
|
+
const b = x?.["/"]?.bytes;
|
|
80
|
+
if (typeof b === "string") {
|
|
81
|
+
const padded = b + "=".repeat((4 - (b.length % 4)) % 4);
|
|
82
|
+
return Utils.toArray(padded, "base64");
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
export function callUrl(base) {
|
|
87
|
+
return `${base.replace(/\/+$/, "")}/call`;
|
|
88
|
+
}
|
|
89
|
+
export function submitBody(s) {
|
|
90
|
+
return {
|
|
91
|
+
fn: SWAP_SUBMIT_FN,
|
|
92
|
+
args: { funding: dagBytes(s.funding), swap: dagBytes(s.swap), pool: s.pool, validator: dagBytes(Utils.toArray(s.validator, "hex")), peerId: s.peerId, expires: s.expires },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/** One `{fn, args}` call on the app's `/call` route. Throws `RelayError` on an error answer. */
|
|
96
|
+
export async function callApp(af, base, fn, args) {
|
|
97
|
+
const res = await af.fetch(callUrl(base), {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: { "content-type": "application/json" },
|
|
100
|
+
body: JSON.stringify({ fn, args }),
|
|
101
|
+
});
|
|
102
|
+
const text = await res.text();
|
|
103
|
+
let answer = null;
|
|
104
|
+
try {
|
|
105
|
+
answer = JSON.parse(text);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
/* not JSON */
|
|
109
|
+
}
|
|
110
|
+
if (res.status === 200 && answer && "result" in answer)
|
|
111
|
+
return answer.result;
|
|
112
|
+
const e = answer?.error;
|
|
113
|
+
throw new RelayError(typeof e?.code === "string" ? e.code : `HTTP ${res.status}`, typeof e?.message === "string" ? e.message : text.slice(0, 200), res.status);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A refusal's pool, as the validator writes it (messages.zig poolValue:
|
|
117
|
+
* `lpFeeBps`, `validatorFeeBps`, `validatorIdentity`) or as the lookup does
|
|
118
|
+
* (the engine's names), into the engine's PoolState. A closed pool, or one
|
|
119
|
+
* missing a field, is undefined: nothing left to re-price.
|
|
120
|
+
*/
|
|
121
|
+
export function toPoolState(raw) {
|
|
122
|
+
if (!raw || typeof raw !== "object")
|
|
123
|
+
return undefined;
|
|
124
|
+
const p = raw;
|
|
125
|
+
if (p.closed)
|
|
126
|
+
return undefined;
|
|
127
|
+
const num = (...ks) => ks.map((k) => p[k]).find((v) => typeof v === "number" || typeof v === "string");
|
|
128
|
+
const outpoint = p.outpoint;
|
|
129
|
+
const bsv = num("bsvReserve");
|
|
130
|
+
const tokens = num("tokenReserve");
|
|
131
|
+
const lp = num("liquidityFeeBps", "lpFeeBps");
|
|
132
|
+
const val = num("validationFeeBps", "validatorFeeBps");
|
|
133
|
+
const comm = num("commissionBps");
|
|
134
|
+
const rawId = p.validatorIdentityKey ?? p.validatorIdentity;
|
|
135
|
+
const idBytes = typeof rawId === "string" ? null : readBytes(rawId);
|
|
136
|
+
const identity = typeof rawId === "string" ? rawId : idBytes?.length === 33 ? Utils.toHex(idBytes) : undefined;
|
|
137
|
+
if (typeof outpoint !== "string" || bsv === undefined || tokens === undefined || lp === undefined || val === undefined || typeof identity !== "string") {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
outpoint,
|
|
142
|
+
bsvReserve: BigInt(bsv),
|
|
143
|
+
tokenReserve: BigInt(tokens),
|
|
144
|
+
liquidityFeeBps: BigInt(lp),
|
|
145
|
+
validationFeeBps: BigInt(val),
|
|
146
|
+
commissionBps: BigInt((comm ?? 0)),
|
|
147
|
+
validatorIdentityKey: identity,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
export function parseSwapRecord(x) {
|
|
151
|
+
const r = x;
|
|
152
|
+
const id = r?.id ?? r?.swapId;
|
|
153
|
+
const status = r?.status ?? r?.state;
|
|
154
|
+
if (!r || typeof id !== "string")
|
|
155
|
+
throw new Error(`relay: not a swap record: ${JSON.stringify(x)?.slice(0, 200)}`);
|
|
156
|
+
switch (status) {
|
|
157
|
+
case "pending":
|
|
158
|
+
case "timeout":
|
|
159
|
+
return { id, status };
|
|
160
|
+
case "failed":
|
|
161
|
+
return { id, status, reason: (typeof r.reason === "string" ? r.reason : "failed") + (typeof r.detail === "string" && r.detail ? `: ${r.detail}` : "") };
|
|
162
|
+
case "accepted": {
|
|
163
|
+
const tx = readBytes(r.tx);
|
|
164
|
+
if (!tx || typeof r.txid !== "string")
|
|
165
|
+
throw new Error("relay: accepted without tx / txid");
|
|
166
|
+
return { id, status, tx, txid: r.txid };
|
|
167
|
+
}
|
|
168
|
+
case "refused": {
|
|
169
|
+
const pool = toPoolState(r.poolState ?? (typeof r.pool === "object" ? r.pool : undefined));
|
|
170
|
+
const reason = (typeof r.reason === "string" ? r.reason : "refused") + (typeof r.detail === "string" && r.detail ? `: ${r.detail}` : "");
|
|
171
|
+
return pool ? { id, status, reason, pool } : { id, status, reason };
|
|
172
|
+
}
|
|
173
|
+
default:
|
|
174
|
+
throw new Error(`relay: unknown swap status ${JSON.stringify(status)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** `amm.swap.terms`'s answer: `commissionPkh` as DAG-JSON bytes (or hex, or a byte array) of 20 bytes, or null / absent; `minCommissionBps` an integer 0 to 10 000, absent 0 (0.16.0). */
|
|
178
|
+
export function parseSwapTerms(x) {
|
|
179
|
+
const r = x;
|
|
180
|
+
if (!r || typeof r !== "object")
|
|
181
|
+
throw new Error(`relay: not swap terms: ${JSON.stringify(x)?.slice(0, 200)}`);
|
|
182
|
+
const m = r.minCommissionBps ?? 0;
|
|
183
|
+
if (typeof m !== "number" || !Number.isInteger(m) || m < 0 || m > 10_000)
|
|
184
|
+
throw new Error("relay: minCommissionBps is not an integer, 0 to 10000");
|
|
185
|
+
if (r.commissionPkh === null || r.commissionPkh === undefined)
|
|
186
|
+
return { commissionPkh: null, minCommissionBps: m };
|
|
187
|
+
const b = readBytes(r.commissionPkh);
|
|
188
|
+
if (!b || b.length !== 20)
|
|
189
|
+
throw new Error("relay: commissionPkh is not 20 bytes");
|
|
190
|
+
return { commissionPkh: Utils.toHex(b), minCommissionBps: m };
|
|
191
|
+
}
|
|
192
|
+
export async function swapTerms(af, base) {
|
|
193
|
+
return parseSwapTerms(await callApp(af, base, SWAP_TERMS_FN, {}));
|
|
194
|
+
}
|
|
195
|
+
export async function submitSwap(af, base, s) {
|
|
196
|
+
const { fn, args } = submitBody(s);
|
|
197
|
+
return parseSwapRecord(await callApp(af, base, fn, args));
|
|
198
|
+
}
|
|
199
|
+
export async function swapStatus(af, base, id) {
|
|
200
|
+
return parseSwapRecord(await callApp(af, base, SWAP_STATUS_FN, { id }));
|
|
201
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One swap leg through the user's BRC-100 wallet (docs/notes.md, "Swap
|
|
3
|
+
* funding and signing"). The page holds no keys: every key and signature
|
|
4
|
+
* comes from the wallet (`getPublicKey`, `createAction`, `createSignature`).
|
|
5
|
+
*
|
|
6
|
+
* 1. `planSwapLeg` (`PoolTemplate.planSwap`): the outputs the contract
|
|
7
|
+
* requires (pool, payout, LP fee, validator fee, commission) and the
|
|
8
|
+
* method arguments. The commission goes to the relay's `amm.swap.terms`
|
|
9
|
+
* address; when the relay names none and the pool's CommissionBps is
|
|
10
|
+
* nonzero, to a key of the taker's own (see "the commission" below). The
|
|
11
|
+
* leg's sats are exact: what the contract's outputs take beyond the
|
|
12
|
+
* pool's and the token inputs' sats (amount in + 1 sat per 1Color output
|
|
13
|
+
* the taker pays for), plus the miner fee of the final swap
|
|
14
|
+
* (`swapFunding`). Sized with placeholder addresses: the payout keys
|
|
15
|
+
* (and the taker's own commission key) are derived at step 3 from the
|
|
16
|
+
* leg's first staged input (`legDerivation`, 0.29.1), not random.
|
|
17
|
+
* 2. **The leg's inputs** (0.29.0, David Case 2026-11-02): every one an
|
|
18
|
+
* output of the active swap basket `amm-swap` (src/market/activeSwap.ts):
|
|
19
|
+
* ONE sats output of exactly that amount (`SatsInput`) and, token → sats,
|
|
20
|
+
* the token outputs carrying exactly the amount in (`TokenInput`), staged
|
|
21
|
+
* there by one wallet send before the legs (no funding transaction, no
|
|
22
|
+
* `1sat-deposit`).
|
|
23
|
+
* 3. **Swap transaction**, built here (`prepareSwap`): inputs pool
|
|
24
|
+
* (`callUnlock`, the validator's slot `OP_0`, `_changeAmount = 0`), the
|
|
25
|
+
* sats output, the token outputs; outputs exactly the contract's, no
|
|
26
|
+
* change. The sats and token inputs are signed with `createSignature`
|
|
27
|
+
* (SIGHASH_ALL|FORKID over the BIP-143 sighash computed here, under the
|
|
28
|
+
* key each input's customInstructions name) and every input but the
|
|
29
|
+
* pool's is checked with `Spend`.
|
|
30
|
+
* 4. The relay (src/market/relay.ts) carries it to the validator, with the
|
|
31
|
+
* sats output's transaction as the swap's funding transaction (its
|
|
32
|
+
* AtomicBEEF, from the wallet).
|
|
33
|
+
* 5. Accepted: `completeSwap` internalizes the payout (sats: BRC-29 wallet
|
|
34
|
+
* payment; tokens, 0.12.0: the output is locked to a BRC-207 module
|
|
35
|
+
* receiving key whose sender is the pool's validator, taken in with a
|
|
36
|
+
* basket insertion into `p color <assetTag>` and the receipt,
|
|
37
|
+
* src/wallet/color.ts), and the commission when it is the taker's own
|
|
38
|
+
* (same two forms). No relinquishOutput (0.29.0): the wallet marks the
|
|
39
|
+
* `amm-swap` outputs the swap spends spent when it takes the swap in.
|
|
40
|
+
* Refused / timed out: nothing to undo; the leg's outputs stay in
|
|
41
|
+
* `amm-swap` for a later swap.
|
|
42
|
+
*/
|
|
43
|
+
import { Beef, Transaction, UnlockingScript, type WalletInterface, type WalletProtocol } from "@bsv/sdk";
|
|
44
|
+
import type { Direction, Leg, PoolState } from "@dcasegr/amm-engine";
|
|
45
|
+
import { type CallPlan, type PoolUtxo } from "../pool/index.js";
|
|
46
|
+
import type { LookupOutput } from "../lib/overlay.js";
|
|
47
|
+
import { type Brc29Payout } from "../wallet/brc29.js";
|
|
48
|
+
import { type ColorReceipt } from "../wallet/color.js";
|
|
49
|
+
/** A P2PKH unlocking script's upper bound: push(DER ≤ 72 + sighash byte) + push(33-byte key). */
|
|
50
|
+
export declare const P2PKH_UNLOCK_LENGTH = 108;
|
|
51
|
+
/**
|
|
52
|
+
* How long the relay may take (the swap's `expires`): 15 s (0.34.0, D15, David Case 2026-11-04;
|
|
53
|
+
* was 120 s, a 10-05 builder number): the held submit returns inside it.
|
|
54
|
+
*/
|
|
55
|
+
export declare const SWAP_TTL_MS = 15000;
|
|
56
|
+
export declare class LegShapeError extends Error {
|
|
57
|
+
constructor(message: string);
|
|
58
|
+
}
|
|
59
|
+
/** An `amm-swap` basket output the wallet can sign: a 1Color value output of the token, with its key. */
|
|
60
|
+
export interface TokenInput {
|
|
61
|
+
/** `txid.vout` (BRC-100 outpoint form). */
|
|
62
|
+
outpoint: string;
|
|
63
|
+
txid: string;
|
|
64
|
+
vout: number;
|
|
65
|
+
satoshis: number;
|
|
66
|
+
lockingScript: string;
|
|
67
|
+
amount: bigint;
|
|
68
|
+
protocolID: WalletProtocol;
|
|
69
|
+
keyID: string;
|
|
70
|
+
counterparty: string;
|
|
71
|
+
}
|
|
72
|
+
/** The token's 32-byte wire id (internal byte order) for a token with a 32-byte id (`<txid>_0`; `<txid>` and `<txid>.0` read the same). */
|
|
73
|
+
export declare function assetIdOf(tokenId: string): string;
|
|
74
|
+
/**
|
|
75
|
+
* The basket rows usable as token inputs for `tokenId`: 1Color value outputs
|
|
76
|
+
* with this 32-byte id (the pool template counts only those) whose
|
|
77
|
+
* customInstructions name the key (protocolID, keyID).
|
|
78
|
+
*/
|
|
79
|
+
export declare function tokenInputsOf(rows: {
|
|
80
|
+
outpoint: string;
|
|
81
|
+
satoshis: number;
|
|
82
|
+
lockingScript?: string;
|
|
83
|
+
tags?: string[];
|
|
84
|
+
customInstructions?: string;
|
|
85
|
+
}[], tokenId: string): TokenInput[];
|
|
86
|
+
/**
|
|
87
|
+
* Token outputs summing to exactly `amount`, or null. The pool contract has
|
|
88
|
+
* no token change output (Pool.runar.go Swap: the token inputs are the
|
|
89
|
+
* amountIn), so an inexact selection cannot be spent into a swap. Fewest
|
|
90
|
+
* outputs first; a bounded search.
|
|
91
|
+
*/
|
|
92
|
+
export declare function selectExactTokenInputs(candidates: TokenInput[], amount: bigint): TokenInput[] | null;
|
|
93
|
+
/** An `amm-swap` sats output (customInstructions `{sats: true, assetId, protocolID, keyID, counterparty}`): a plain P2PKH to an "amm swap" key. */
|
|
94
|
+
export interface SatsInput {
|
|
95
|
+
/** `txid.vout` */
|
|
96
|
+
outpoint: string;
|
|
97
|
+
txid: string;
|
|
98
|
+
vout: number;
|
|
99
|
+
satoshis: number;
|
|
100
|
+
lockingScript: string;
|
|
101
|
+
protocolID: WalletProtocol;
|
|
102
|
+
keyID: string;
|
|
103
|
+
counterparty: string;
|
|
104
|
+
/** The token whose swap staged it (`<txid>_<vout>`, 0.29.1), when its row names one. */
|
|
105
|
+
assetId?: string;
|
|
106
|
+
}
|
|
107
|
+
/** The basket rows usable as a leg's sats: plain P2PKH outputs whose customInstructions say `sats: true` and name the key. */
|
|
108
|
+
export declare function satsInputsOf(rows: {
|
|
109
|
+
outpoint: string;
|
|
110
|
+
satoshis: number;
|
|
111
|
+
lockingScript?: string;
|
|
112
|
+
customInstructions?: string;
|
|
113
|
+
}[]): SatsInput[];
|
|
114
|
+
/** The sats twin of `selectExactTokenInputs`: one output of exactly `satoshis` (the leg has no change), or null. */
|
|
115
|
+
export declare function selectExactSatsInput(candidates: SatsInput[], satoshis: number): SatsInput | null;
|
|
116
|
+
/** The pool output from the lookup's BEEF, as the template's PoolUtxo. */
|
|
117
|
+
export declare function poolUtxoFrom(poolOutput: LookupOutput): PoolUtxo;
|
|
118
|
+
/**
|
|
119
|
+
* A P2PKH (or 1Color-on-P2PKH) input signed by the wallet: BIP-143 over
|
|
120
|
+
* the finished transaction under `scope` (ALL|FORKID), `createSignature` with `hashToDirectlySign`
|
|
121
|
+
* (1sat-sdk's `signP2PKHInputWallet`).
|
|
122
|
+
*/
|
|
123
|
+
export declare function signP2pkhWithWallet(wallet: WalletInterface, tx: Transaction, inputIndex: number, src: {
|
|
124
|
+
satoshis: number;
|
|
125
|
+
lockingScript: string;
|
|
126
|
+
protocolID: WalletProtocol;
|
|
127
|
+
keyID: string;
|
|
128
|
+
counterparty: string;
|
|
129
|
+
}, scope?: number): Promise<UnlockingScript>;
|
|
130
|
+
/**
|
|
131
|
+
* The final swap's size in bytes: the pool input at `maxCallUnlockLength`
|
|
132
|
+
* (validator's signature in), `takerInputs` P2PKH inputs (the sats, tokens)
|
|
133
|
+
* at 108 bytes of unlocking script, the contract's outputs, no change.
|
|
134
|
+
*/
|
|
135
|
+
export declare function swapTxSize(plan: CallPlan, takerInputs: number): number;
|
|
136
|
+
export interface SwapFunding {
|
|
137
|
+
/** The leg's sats output's exact amount (0.29.0: an `amm-swap` output). */
|
|
138
|
+
satoshis: number;
|
|
139
|
+
/** What the contract's outputs take beyond the pool's and the token inputs' sats. */
|
|
140
|
+
outputs: number;
|
|
141
|
+
fee: number;
|
|
142
|
+
size: number;
|
|
143
|
+
satsPerKb: number;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The leg's exact sats: Σ contract outputs − pool sats − token inputs' sats
|
|
147
|
+
* (sats in: amount in + 1 for the token payout, the fees and the commission
|
|
148
|
+
* being part of amount in; tokens in: 1 per 1Color fee or commission output
|
|
149
|
+
* − the token inputs' sats), plus
|
|
150
|
+
* ceil(size × rate / 1000) for the final swap.
|
|
151
|
+
*/
|
|
152
|
+
export declare function swapFunding(plan: CallPlan, tokenInputs: {
|
|
153
|
+
satoshis: number;
|
|
154
|
+
}[], satsPerKb: number): SwapFunding;
|
|
155
|
+
export interface PlanLegInput {
|
|
156
|
+
wallet: WalletInterface;
|
|
157
|
+
/** `<txid>_0` (src/lib/tokenId.ts). */
|
|
158
|
+
tokenId: string;
|
|
159
|
+
meta?: {
|
|
160
|
+
sym?: string;
|
|
161
|
+
dec?: number;
|
|
162
|
+
};
|
|
163
|
+
direction: Direction;
|
|
164
|
+
leg: Leg;
|
|
165
|
+
pool: PoolState;
|
|
166
|
+
/** The lookup's `{outpoint, beef: true}` answer for `leg.outpoint`. */
|
|
167
|
+
poolOutput: LookupOutput;
|
|
168
|
+
/**
|
|
169
|
+
* The relay's commission address (`amm.swap.terms`, hash160 hex), or null
|
|
170
|
+
* when the relay names none: a pool with a nonzero CommissionBps then pays
|
|
171
|
+
* the commission to a fresh key of the taker's own.
|
|
172
|
+
*/
|
|
173
|
+
commissionPkh: string | null;
|
|
174
|
+
/**
|
|
175
|
+
* token → sats: the token inputs carrying exactly `leg.amountIn`, or (0.29.0) what they will be
|
|
176
|
+
* once staged: one output of 1 sat per leg (`sendToSwapBasket`). Their count and sats size the
|
|
177
|
+
* leg's sats.
|
|
178
|
+
*/
|
|
179
|
+
tokenInputs?: Pick<TokenInput, "satoshis" | "amount">[];
|
|
180
|
+
/** sats per 1000 bytes (`VITE_FEE_RATE`). */
|
|
181
|
+
satsPerKb: number;
|
|
182
|
+
}
|
|
183
|
+
/** Step 1: the leg's plan (placeholder addresses) and exact sats (`funding.satoshis`: the `amm-swap` sats output the leg spends). */
|
|
184
|
+
export interface PlannedLeg {
|
|
185
|
+
/** The pool outpoint `<txid>_<vout>`. */
|
|
186
|
+
pool: string;
|
|
187
|
+
validator: string;
|
|
188
|
+
bsvIn: boolean;
|
|
189
|
+
poolUtxo: PoolUtxo;
|
|
190
|
+
/** Sized with placeholder payout and commission addresses (prepareSwap re-plans with the derived keys). */
|
|
191
|
+
plan: CallPlan;
|
|
192
|
+
funding: SwapFunding;
|
|
193
|
+
commissionAmount: bigint;
|
|
194
|
+
commissionTo: SwapCommission["to"];
|
|
195
|
+
}
|
|
196
|
+
export interface PrepareSwapInput extends PlanLegInput {
|
|
197
|
+
/** token → sats: `amm-swap` outputs carrying exactly `leg.amountIn`. */
|
|
198
|
+
tokenInputs?: TokenInput[];
|
|
199
|
+
/** The leg's `amm-swap` sats output: exactly the planned `funding.satoshis` (0.29.0). */
|
|
200
|
+
satsInput: SatsInput;
|
|
201
|
+
/** The swap basket's source transactions: the sats and token inputs' (default: `amm-swap`'s, from the wallet). */
|
|
202
|
+
holdingBeef?: Beef;
|
|
203
|
+
/** Step 1 already done (`planSwapLeg` over the same input); planned here when absent. */
|
|
204
|
+
planned?: PlannedLeg;
|
|
205
|
+
now?: number;
|
|
206
|
+
ttlMs?: number;
|
|
207
|
+
}
|
|
208
|
+
export type SwapPayout = {
|
|
209
|
+
kind: "color";
|
|
210
|
+
outputIndex: number;
|
|
211
|
+
satoshis: number;
|
|
212
|
+
lockingScript: string;
|
|
213
|
+
receipt: ColorReceipt;
|
|
214
|
+
} | ({
|
|
215
|
+
kind: "brc29";
|
|
216
|
+
} & Brc29Payout);
|
|
217
|
+
export interface PreparedSwap {
|
|
218
|
+
/** The pool outpoint `<txid>_<vout>`. */
|
|
219
|
+
pool: string;
|
|
220
|
+
validator: string;
|
|
221
|
+
expires: number;
|
|
222
|
+
plan: CallPlan;
|
|
223
|
+
funding: SwapFunding & {
|
|
224
|
+
/** The leg's `amm-swap` sats output (input 1). */
|
|
225
|
+
input: SatsInput;
|
|
226
|
+
/** Its transaction as the wallet's AtomicBEEF: the swap's funding transaction, what the relay is sent. */
|
|
227
|
+
atomicBeef: number[];
|
|
228
|
+
txid: string;
|
|
229
|
+
/** `txid.vout` */
|
|
230
|
+
outpoint: string;
|
|
231
|
+
};
|
|
232
|
+
/** Every input signed except the pool's validator slot. */
|
|
233
|
+
swap: Transaction;
|
|
234
|
+
tokenInputs: TokenInput[];
|
|
235
|
+
payout: SwapPayout;
|
|
236
|
+
commission: SwapCommission;
|
|
237
|
+
}
|
|
238
|
+
/** The leg's commission (the pool's CommissionBps of amount in, in the input asset). */
|
|
239
|
+
export interface SwapCommission {
|
|
240
|
+
/** 0 when the pool's CommissionBps is 0: no output. */
|
|
241
|
+
amount: bigint;
|
|
242
|
+
/** Swap's `commissionPkh` (pushed even when there is no output). */
|
|
243
|
+
pkh: string;
|
|
244
|
+
/** "relay": the relay's address; "own": the taker's key, internalized with the payout; "none": no output. */
|
|
245
|
+
to: "relay" | "own" | "none";
|
|
246
|
+
/** "own" only: the wallet's record of the commission output. */
|
|
247
|
+
payout?: SwapPayout;
|
|
248
|
+
}
|
|
249
|
+
/** BRC-29 / BRC-207 derivationPrefix of a leg's payout (0.29.1): base64 of the UTF-8 text "amm swap". */
|
|
250
|
+
export declare const LEG_PAYOUT_PREFIX: string;
|
|
251
|
+
/** derivationPrefix of the taker's own commission output (0.29.1): base64 of "amm swap commission". */
|
|
252
|
+
export declare const LEG_COMMISSION_PREFIX: string;
|
|
253
|
+
/**
|
|
254
|
+
* The derivation of a leg's payout keys (0.29.1, David Case 2026-11-02: deterministic from what
|
|
255
|
+
* the leg spends). `keyInput` is the leg's first staged `amm-swap` input, `<txid>.<vout>` (txid in
|
|
256
|
+
* display hex, lowercase): tokens in, its first token input (the swap's input 2); sats in, its sats
|
|
257
|
+
* input (input 1). derivationSuffix = base64(sha256(UTF-8 of that text)); derivationPrefix
|
|
258
|
+
* `LEG_PAYOUT_PREFIX` for the payout, `LEG_COMMISSION_PREFIX` for the taker's own commission. The
|
|
259
|
+
* same pair keys the BRC-29 payment (keyID "<prefix> <suffix>", sender the user) and the BRC-207
|
|
260
|
+
* receipt (`p color <assetTag> <prefixHex> <suffixHex>`, sender the pool's validator). A staged
|
|
261
|
+
* output is spent by one transaction only, so the keys are the leg's alone.
|
|
262
|
+
*/
|
|
263
|
+
export declare function legDerivation(keyInput: string, which: "payout" | "commission"): {
|
|
264
|
+
derivationPrefix: string;
|
|
265
|
+
derivationSuffix: string;
|
|
266
|
+
};
|
|
267
|
+
/** The leg's key input in a swap transaction (see `legDerivation`): input 2 when it spends tokens, else input 1. */
|
|
268
|
+
export declare function legKeyInputOf(swap: Transaction, bsvIn: boolean): string;
|
|
269
|
+
/**
|
|
270
|
+
* Step 1 (see the module comment): the leg's plan and exact sats, sized with placeholder payout
|
|
271
|
+
* and commission addresses (20 bytes each: the sizes are the same). No wallet call: the payout
|
|
272
|
+
* keys are derived at step 3 from the leg's staged input (`legDerivation`). Throws
|
|
273
|
+
* `PoolBuildError` / `LegShapeError`.
|
|
274
|
+
*/
|
|
275
|
+
export declare function planSwapLeg(i: PlanLegInput): Promise<PlannedLeg>;
|
|
276
|
+
/** Steps 1 (unless `planned` is given) and 3 (see the module comment). Throws `PoolBuildError` / `LegShapeError`; the wallet is left as it was. */
|
|
277
|
+
export declare function prepareSwap(i: PrepareSwapInput): Promise<PreparedSwap>;
|
|
278
|
+
/** @bsv/sdk's `Spend` over input `n` of `tx` (its source output from `sourceTransaction`). */
|
|
279
|
+
export declare function spendValid(tx: Transaction, n: number): boolean;
|
|
280
|
+
/**
|
|
281
|
+
* The validator's final transaction, checked against ours: same inputs (in
|
|
282
|
+
* order, the taker's unlocking scripts unchanged), same outputs; only input
|
|
283
|
+
* 0's unlocking script (the validator's slot) differs. Its inputs get our
|
|
284
|
+
* source transactions, so it serializes as AtomicBEEF.
|
|
285
|
+
*/
|
|
286
|
+
export declare function finalSwapOf(p: PreparedSwap, raw: number[], txid?: string): Transaction;
|
|
287
|
+
/**
|
|
288
|
+
* A validator-signed pool call checked against the one we built (`ours`):
|
|
289
|
+
* same inputs in order with our unlocking scripts on every input but the
|
|
290
|
+
* pool's (input 0), same outputs. Shared by the swap and AddLiquidity.
|
|
291
|
+
*/
|
|
292
|
+
export declare function finalCallOf(ours: Transaction, raw: number[], txid: string | undefined, what: string): Transaction;
|
|
293
|
+
export interface CompletedSwap {
|
|
294
|
+
txid: string;
|
|
295
|
+
internalized: boolean;
|
|
296
|
+
/** Internalize errors (the swap itself is final). */
|
|
297
|
+
errors: string[];
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Accepted: the wallet takes the payout in (`internalizeAction`). No
|
|
301
|
+
* relinquishOutput (0.29.0, David Case 2026-11-02: "Anything we internalize
|
|
302
|
+
* should relinquish outputs itself"): taking the swap in marks the wallet's
|
|
303
|
+
* outputs it spends — the `amm-swap` sats and token inputs — spent
|
|
304
|
+
* (wallet-toolbox `markUserInputsSpent`).
|
|
305
|
+
*/
|
|
306
|
+
export declare function completeSwap(wallet: WalletInterface, p: PreparedSwap, raw: number[], txid?: string): Promise<CompletedSwap>;
|