@dcasegr/amm-client 0.39.0 → 0.42.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 +70 -31
- package/dist/client.d.ts +190 -62
- package/dist/client.js +493 -322
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +6 -3
- package/dist/index.d.ts +16 -4
- package/dist/index.js +16 -4
- package/dist/lib/overlay.d.ts +132 -50
- package/dist/lib/overlay.js +169 -160
- package/dist/lib/skein.d.ts +22 -0
- package/dist/lib/skein.js +16 -1
- package/dist/market/activeSwap.d.ts +77 -96
- package/dist/market/activeSwap.js +146 -166
- package/dist/market/plan.d.ts +2 -1
- package/dist/market/plan.js +1 -1
- package/dist/market/swapAction.d.ts +150 -145
- package/dist/market/swapAction.js +292 -213
- package/dist/market/swapFlow.d.ts +3 -6
- package/dist/market/swapFlow.js +4 -22
- package/dist/pool/pool.artifact.json +760 -528
- package/dist/pool/template.d.ts +10 -2
- package/dist/pool/template.js +16 -6
- package/dist/wallet/color.d.ts +13 -9
- package/dist/wallet/color.js +13 -10
- package/dist/wallet/swapPayouts.d.ts +45 -28
- package/dist/wallet/swapPayouts.js +129 -117
- package/package.json +2 -2
|
@@ -1,53 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The active swap basket (skein-amm 0.12.0, David Case 2026-10-21/22; 0.
|
|
3
|
-
* 2026-11-
|
|
4
|
-
* in the exchange's own basket `amm-swap` (src/wallet/color.ts)
|
|
5
|
-
*
|
|
2
|
+
* The active swap basket (skein-amm 0.12.0, David Case 2026-10-21/22; 0.40.0, agent-env
|
|
3
|
+
* plans/swap-balance-build-2026-11-05.md §0 7, Builder 2 item 2): what a swap stages for its legs,
|
|
4
|
+
* held outside the BRC-207 module in the exchange's own basket `amm-swap` (src/wallet/color.ts).
|
|
5
|
+
* No deposit, no withdraw, no balance the user manages.
|
|
6
6
|
*
|
|
7
|
-
* - **
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `amm-swap`
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* `
|
|
26
|
-
* wallet marks them spent when it takes the accepted swap in (0.29.0: no
|
|
27
|
-
* relinquishOutput). An output a failed leg did not spend stays for a
|
|
28
|
-
* later swap.
|
|
29
|
-
* - **Move back** (`moveBack`, the recovery path; 0.29.3: a module plan,
|
|
30
|
-
* David Case 2026-11-02): ONE createAction through the wallet's BRC-207
|
|
31
|
-
* module, a `p color plan` transfer: every `amm-swap` token output of the
|
|
32
|
-
* token and (0.29.1) the sats rows staged for its legs (their `assetId`)
|
|
33
|
-
* as explicit inputs (their source transactions in `inputBEEF`) — to the
|
|
34
|
-
* module FOREIGN inputs (not in a `p color` basket): the token rows
|
|
35
|
-
* checked admitted and unspent on the token's overlay, the sats rows
|
|
36
|
-
* adding nothing; one output, the whole amount to a NEW module receiving
|
|
37
|
-
* key of the user's own, the plan's owned output 0 (the module checks it
|
|
38
|
-
* pays the remittance's key and files it in `p color <assetTag>` with its
|
|
39
|
-
* custody in the same call). The page unlocks its inputs at signAction;
|
|
40
|
-
* the wallet funds the fee and takes the sats rows into its change; the
|
|
41
|
-
* module approves and submits, the wallet broadcasts. The page submits it
|
|
42
|
-
* to this skein's token topic too. Nothing is taken in afterwards and
|
|
43
|
-
* nothing is kept in the browser. * (0.29.6) With no token row and only sats rows staged for the token
|
|
44
|
-
* (a sats-in leg that failed): one plain createAction (no `p color`
|
|
45
|
-
* labels), the sats rows as explicit inputs and no outputs — the wallet
|
|
46
|
-
* keeps them as its change and pays the fee (`moveBackSats`).
|
|
7
|
+
* - **Staging** (`stage`): at Swap, ONE wallet action. Sats only (a buy, or a sell whose tokens
|
|
8
|
+
* the basket holds): a plain createAction, ONE P2PKH output to a fresh key under `SWAP_PROTOCOL`
|
|
9
|
+
* (self, keyID random, `amm-swap-<hex>`), filed in `amm-swap` by the action with its
|
|
10
|
+
* customInstructions (`{sats: true, protocolID, keyID, counterparty}`: no token tag). With
|
|
11
|
+
* tokens: the module transfer plan — ONE token output to a fresh key and the ONE sats output in
|
|
12
|
+
* the same action, the token key's id ALSO in that output's description (`… · amm-swap <hex>`,
|
|
13
|
+
* as the LP key's `amm-lp <hex>`), the action labelled `SWAP_STAGE_LABEL`; the module selects
|
|
14
|
+
* the token inputs, adds change, signs and submits; the page submits it too, then files the
|
|
15
|
+
* token output in `amm-swap` (basket insertion, `{assetId, amount, protocolID, keyID,
|
|
16
|
+
* counterparty}`). A labelled staged token output the basket lacks (the page died before the
|
|
17
|
+
* filing) is filed on load from the action's description (`unfiledStagedTokens`).
|
|
18
|
+
* - **Spent** by the legs (src/market/swapAction.ts), their change kept here between legs.
|
|
19
|
+
* - **Move back** (`moveBack`, as 0.29.3): ONE createAction through the wallet's BRC-207 module,
|
|
20
|
+
* a `p color plan` transfer: every `amm-swap` token row of the token and every sats row as
|
|
21
|
+
* explicit inputs (their source transactions in `inputBEEF`), one output — the whole amount to
|
|
22
|
+
* a NEW module receiving key of the user's own, the plan's owned output 0. The page unlocks its
|
|
23
|
+
* inputs at signAction; the wallet funds the fee and takes the sats into its change. With no
|
|
24
|
+
* token row and sats rows staged: one plain createAction, the sats rows as explicit inputs and
|
|
25
|
+
* no outputs (`moveBackSats`).
|
|
47
26
|
*/
|
|
48
27
|
import { Beef, P2PKH, PublicKey, Transaction, Utils } from "@bsv/sdk";
|
|
49
28
|
import { decodeOneColor } from "../pool/index.js";
|
|
50
|
-
import {
|
|
29
|
+
import { sdkTokenId, tokenIdOfWire } from "../lib/tokenId.js";
|
|
51
30
|
import { basketBeef, basketRows } from "../lp/wallet.js";
|
|
52
31
|
import { tokenP2pkhScript } from "../lp/poolDeploy.js";
|
|
53
32
|
import { steakText, submitToOverlay } from "../lp/close.js";
|
|
@@ -57,21 +36,28 @@ import { inputIndexOf, runAppSignedAction } from "../wallet/action.js";
|
|
|
57
36
|
import { P2PKH_UNLOCK_LENGTH, satsInputsOf, signP2pkhWithWallet, spendValid, tokenInputsOf } from "./swapAction.js";
|
|
58
37
|
/** The keyID prefix of an `amm-swap` key (`SWAP_PROTOCOL`, counterparty self). */
|
|
59
38
|
export const SWAP_KEY_PREFIX = "amm-swap-";
|
|
39
|
+
/** The label of a staging action that carries tokens (0.40.0): the staged token output's key id is in its output description. */
|
|
40
|
+
export const SWAP_STAGE_LABEL = "amm-swap-stage";
|
|
41
|
+
/** The marker a staged token output's description ends with (` · amm-swap <hex>`, 0.40.0): it names the key id. */
|
|
42
|
+
export const SWAP_KEY_MARK = " · amm-swap ";
|
|
60
43
|
function describe(text) {
|
|
61
44
|
return text.length <= 50 ? text : `${text.slice(0, 49)}…`;
|
|
62
45
|
}
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
46
|
+
/** A staged row's keyID (0.40.0): `amm-swap-<16 random bytes, hex>`. */
|
|
47
|
+
export function newSwapKeyID() {
|
|
48
|
+
return `${SWAP_KEY_PREFIX}${Utils.toHex(Array.from(crypto.getRandomValues(new Uint8Array(16))))}`;
|
|
49
|
+
}
|
|
50
|
+
/** A staged token output's description: the readable text (at most 50 characters), then `SWAP_KEY_MARK` and the keyID's hex. */
|
|
51
|
+
export function swapKeyDescription(readable, keyID) {
|
|
52
|
+
const hex = keyID.startsWith(SWAP_KEY_PREFIX) ? keyID.slice(SWAP_KEY_PREFIX.length) : "";
|
|
53
|
+
if (!/^[0-9a-f]{2,128}$/.test(hex))
|
|
54
|
+
throw new Error(`not a staged key id: ${keyID}`);
|
|
55
|
+
return `${describe(readable)}${SWAP_KEY_MARK}${hex}`;
|
|
56
|
+
}
|
|
57
|
+
/** The keyID a staged token output's description names (`swapKeyDescription`), or null. */
|
|
58
|
+
export function swapKeyIdOfDescription(description) {
|
|
59
|
+
const m = / · amm-swap ([0-9a-f]{2,128})$/.exec(description ?? "");
|
|
60
|
+
return m && m[1].length % 2 === 0 ? `${SWAP_KEY_PREFIX}${m[1]}` : null;
|
|
75
61
|
}
|
|
76
62
|
/** The `amm-swap` outputs of `tokenId` the legs can spend. */
|
|
77
63
|
export async function swapHoldings(wallet, tokenId) {
|
|
@@ -81,37 +67,27 @@ export async function swapHoldings(wallet, tokenId) {
|
|
|
81
67
|
export async function swapSatsHoldings(wallet) {
|
|
82
68
|
return satsInputsOf(await basketRows(wallet, SWAP_BASKET));
|
|
83
69
|
}
|
|
84
|
-
/**
|
|
85
|
-
* What `amm-swap` holds, per token id: the token amount and the number of token outputs, and
|
|
86
|
-
* (0.29.6) the sats staged for the token's legs (the sats rows whose customInstructions name its
|
|
87
|
-
* `assetId`). A token with only sats staged has amount 0n and outputs 0.
|
|
88
|
-
*/
|
|
70
|
+
/** What `amm-swap` holds, per token id: the token amount and the number of token outputs (0.40.0: the sats rows are no token's, `swapSatsTotal`). */
|
|
89
71
|
export async function swapTotals(wallet) {
|
|
90
72
|
const totals = new Map();
|
|
91
|
-
const
|
|
92
|
-
for (const r of rows) {
|
|
73
|
+
for (const r of await basketRows(wallet, SWAP_BASKET)) {
|
|
93
74
|
if (!r.lockingScript)
|
|
94
75
|
continue;
|
|
95
76
|
const t = decodeOneColor(Utils.toArray(r.lockingScript, "hex"));
|
|
96
77
|
if (!t || t.role !== "value" || !t.idBytes)
|
|
97
78
|
continue;
|
|
98
79
|
const id = tokenIdOfWire(t.idBytes);
|
|
99
|
-
const s = totals.get(id) ?? { amount: 0n, outputs: 0
|
|
80
|
+
const s = totals.get(id) ?? { amount: 0n, outputs: 0 };
|
|
100
81
|
s.amount += t.amount;
|
|
101
82
|
s.outputs++;
|
|
102
83
|
totals.set(id, s);
|
|
103
84
|
}
|
|
104
|
-
for (const r of satsInputsOf(rows)) {
|
|
105
|
-
const ref = r.assetId ? parseTokenId(r.assetId) : null;
|
|
106
|
-
if (!ref)
|
|
107
|
-
continue;
|
|
108
|
-
const id = tokenIdText(ref);
|
|
109
|
-
const s = totals.get(id) ?? { amount: 0n, outputs: 0, sats: 0 };
|
|
110
|
-
s.sats += r.satoshis;
|
|
111
|
-
totals.set(id, s);
|
|
112
|
-
}
|
|
113
85
|
return totals;
|
|
114
86
|
}
|
|
87
|
+
/** The sats `amm-swap` holds (0.40.0: shown once, not per token). */
|
|
88
|
+
export async function swapSatsTotal(wallet) {
|
|
89
|
+
return (await swapSatsHoldings(wallet)).reduce((a, r) => a + r.satoshis, 0);
|
|
90
|
+
}
|
|
115
91
|
/** The source transactions of `amm-swap`'s outputs (a leg's BEEF), with `extra` merged in. */
|
|
116
92
|
export async function swapHoldingBeef(wallet, extra) {
|
|
117
93
|
const beef = (await basketBeef(wallet, SWAP_BASKET)) ?? new Beef();
|
|
@@ -119,92 +95,78 @@ export async function swapHoldingBeef(wallet, extra) {
|
|
|
119
95
|
beef.mergeBeef(Beef.fromBinary(extra));
|
|
120
96
|
return beef;
|
|
121
97
|
}
|
|
122
|
-
/** The "amm swap" keys (`swapKeyID`) for `sats` and their outputs, filed in `amm-swap` by the createAction itself. */
|
|
123
|
-
async function satsOutputs(wallet, sats, sym, assetId) {
|
|
124
|
-
return await Promise.all(sats.map(async ({ satoshis, pool }) => {
|
|
125
|
-
if (!Number.isSafeInteger(satoshis) || satoshis <= 0)
|
|
126
|
-
throw new Error("nothing to send to the swap basket");
|
|
127
|
-
const keyID = swapKeyID(pool, "sats");
|
|
128
|
-
const { publicKey } = await wallet.getPublicKey({ protocolID: SWAP_PROTOCOL, keyID, counterparty: "self", forSelf: true });
|
|
129
|
-
const lockingScript = new P2PKH().lock(PublicKey.fromString(publicKey).toAddress()).toHex();
|
|
130
|
-
return {
|
|
131
|
-
keyID,
|
|
132
|
-
assetId,
|
|
133
|
-
satoshis,
|
|
134
|
-
lockingScript,
|
|
135
|
-
output: {
|
|
136
|
-
lockingScript,
|
|
137
|
-
satoshis,
|
|
138
|
-
outputDescription: describe(`Sats for a ${sym} swap leg`),
|
|
139
|
-
basket: SWAP_BASKET,
|
|
140
|
-
customInstructions: swapSatsInstructions({ assetId, protocolID: SWAP_PROTOCOL, keyID, counterparty: "self" }),
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
}));
|
|
144
|
-
}
|
|
145
|
-
/** The send's sats outputs from `first` on, as `SatsInput`s (an output the wallet moved is an error). */
|
|
146
|
-
function satsHeld(final, txid, keys, first, errors) {
|
|
147
|
-
const out = [];
|
|
148
|
-
keys.forEach((k, n) => {
|
|
149
|
-
const vout = first + n;
|
|
150
|
-
const o = final.outputs[vout];
|
|
151
|
-
if (!o || o.lockingScript.toHex() !== k.lockingScript || o.satoshis !== k.satoshis) {
|
|
152
|
-
errors.push(`the wallet's send has no output ${vout} to the swap key`);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
out.push({ outpoint: `${txid}.${vout}`, txid, vout, satoshis: k.satoshis, lockingScript: k.lockingScript, protocolID: SWAP_PROTOCOL, keyID: k.keyID, counterparty: "self", assetId: k.assetId });
|
|
156
|
-
});
|
|
157
|
-
return out;
|
|
158
|
-
}
|
|
159
98
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
* (each one output) after them. Throws when the wallet refuses or fails the
|
|
163
|
-
* send; filing and the page's own submit are reported in `errors`.
|
|
99
|
+
* Staging (see the module comment): ONE wallet action. Throws when the wallet refuses or fails it;
|
|
100
|
+
* the token row's filing and the page's own submit are reported in `errors`.
|
|
164
101
|
*/
|
|
165
|
-
export async function
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
throw new Error("nothing to
|
|
102
|
+
export async function stage(wallet, req, o = {}) {
|
|
103
|
+
// The sats output: what the basket lacks of the fee sats (with tokens it may lack none).
|
|
104
|
+
if (!Number.isSafeInteger(req.sats) || req.sats < 0 || (req.sats === 0 && !req.tokens))
|
|
105
|
+
throw new Error("nothing to stage in the swap basket");
|
|
106
|
+
const sym = o.sym ?? "token";
|
|
107
|
+
const satsKeyID = newSwapKeyID();
|
|
108
|
+
const satsPub = (await wallet.getPublicKey({ protocolID: SWAP_PROTOCOL, keyID: satsKeyID, counterparty: "self", forSelf: true })).publicKey;
|
|
109
|
+
const satsScript = new P2PKH().lock(PublicKey.fromString(satsPub).toAddress()).toHex();
|
|
110
|
+
const satsOutput = {
|
|
111
|
+
lockingScript: satsScript,
|
|
112
|
+
satoshis: req.sats,
|
|
113
|
+
outputDescription: describe(`Sats for a ${sym} swap`),
|
|
114
|
+
basket: SWAP_BASKET,
|
|
115
|
+
customInstructions: swapSatsInstructions({ protocolID: SWAP_PROTOCOL, keyID: satsKeyID, counterparty: "self" }),
|
|
116
|
+
};
|
|
117
|
+
const satsRow = (txid, vout) => ({ outpoint: `${txid}.${vout}`, txid, vout, satoshis: req.sats, lockingScript: satsScript, protocolID: SWAP_PROTOCOL, keyID: satsKeyID, counterparty: "self" });
|
|
118
|
+
if (!req.tokens) {
|
|
119
|
+
const { txid, tx } = await runTokenAction(wallet, {
|
|
120
|
+
description: describe("AMM swap: sats into the swap basket"),
|
|
121
|
+
labels: ["amm-swap"],
|
|
122
|
+
outputs: [satsOutput],
|
|
123
|
+
options: { randomizeOutputs: false, acceptDelayedBroadcast: false },
|
|
124
|
+
});
|
|
125
|
+
const final = Transaction.fromAtomicBEEF(tx);
|
|
126
|
+
if (final.outputs[0]?.lockingScript.toHex() !== satsScript)
|
|
127
|
+
throw new Error("the wallet's action has no output 0 to the swap key");
|
|
128
|
+
return { txid, tx, sats: satsRow(txid, 0), filed: false, submitted: "not submitted (no token moved)", errors: [] };
|
|
129
|
+
}
|
|
130
|
+
const { tokenId, amount } = req.tokens;
|
|
131
|
+
if (amount <= 0n)
|
|
132
|
+
throw new Error("nothing to stage in the swap basket");
|
|
169
133
|
const assetId = sdkTokenId(tokenId);
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
const { publicKey } = await wallet.getPublicKey({ protocolID: SWAP_PROTOCOL, keyID, counterparty: "self", forSelf: true });
|
|
174
|
-
return { amount, keyID, publicKey, lockingScript: tokenP2pkhScript(assetId, amount, publicKey) };
|
|
175
|
-
}));
|
|
176
|
-
const satsKeys = await satsOutputs(wallet, stage.sats, sym, assetId);
|
|
134
|
+
const keyID = newSwapKeyID();
|
|
135
|
+
const pub = (await wallet.getPublicKey({ protocolID: SWAP_PROTOCOL, keyID, counterparty: "self", forSelf: true })).publicKey;
|
|
136
|
+
const tokenScript = tokenP2pkhScript(assetId, amount, pub);
|
|
177
137
|
const { txid, tx } = await runTokenAction(wallet, {
|
|
178
138
|
description: describe(`AMM swap: ${sym} into the swap basket`),
|
|
179
|
-
labels: [...transferPlanLabels(assetId), "amm-swap"],
|
|
180
|
-
outputs: [
|
|
139
|
+
labels: [...transferPlanLabels(assetId), "amm-swap", SWAP_STAGE_LABEL],
|
|
140
|
+
outputs: [{ lockingScript: tokenScript, satoshis: 1, outputDescription: swapKeyDescription(`${sym} for a swap`, keyID) }, ...(req.sats > 0 ? [satsOutput] : [])],
|
|
181
141
|
options: { randomizeOutputs: false, acceptDelayedBroadcast: false },
|
|
182
142
|
});
|
|
183
|
-
const out = { txid, tx, holdings: [], sats: [], filed: false, submitted: "not submitted", errors: [] };
|
|
184
143
|
const final = Transaction.fromAtomicBEEF(tx);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
144
|
+
if (final.outputs[0]?.lockingScript.toHex() !== tokenScript || (req.sats > 0 && final.outputs[1]?.lockingScript.toHex() !== satsScript))
|
|
145
|
+
throw new Error("the wallet's action does not have the staged outputs at 0 and 1");
|
|
146
|
+
const out = {
|
|
147
|
+
txid,
|
|
148
|
+
tx,
|
|
149
|
+
...(req.sats > 0 ? { sats: satsRow(txid, 1) } : {}),
|
|
150
|
+
tokens: { outpoint: `${txid}.0`, txid, vout: 0, satoshis: 1, lockingScript: tokenScript, amount, protocolID: SWAP_PROTOCOL, keyID, counterparty: "self" },
|
|
151
|
+
filed: false,
|
|
152
|
+
submitted: "not submitted",
|
|
153
|
+
errors: [],
|
|
154
|
+
};
|
|
194
155
|
try {
|
|
195
|
-
|
|
156
|
+
if (o.submit)
|
|
157
|
+
out.submitted = steakText(await o.submit(`tm_color_${assetId}`, tx));
|
|
196
158
|
}
|
|
197
159
|
catch (err) {
|
|
198
160
|
out.submitted = "the submit failed";
|
|
199
161
|
out.errors.push(`submit: ${err instanceof Error ? err.message : String(err)}`);
|
|
200
162
|
}
|
|
201
|
-
const rows = out.holdings.map((h) => ({
|
|
202
|
-
outputIndex: h.vout,
|
|
203
|
-
protocol: "basket insertion",
|
|
204
|
-
insertionRemittance: { basket: SWAP_BASKET, customInstructions: swapInstructions({ assetId, amount: h.amount, protocolID: SWAP_PROTOCOL, keyID: h.keyID, counterparty: "self" }) },
|
|
205
|
-
}));
|
|
206
163
|
try {
|
|
207
|
-
const r = await wallet.internalizeAction({
|
|
164
|
+
const r = await wallet.internalizeAction({
|
|
165
|
+
tx,
|
|
166
|
+
outputs: [{ outputIndex: 0, protocol: "basket insertion", insertionRemittance: { basket: SWAP_BASKET, customInstructions: swapInstructions({ assetId, amount, protocolID: SWAP_PROTOCOL, keyID, counterparty: "self" }) } }],
|
|
167
|
+
description: describe(`AMM swap: ${sym} into the swap basket`),
|
|
168
|
+
labels: ["amm-swap"],
|
|
169
|
+
});
|
|
208
170
|
out.filed = r.accepted;
|
|
209
171
|
}
|
|
210
172
|
catch (err) {
|
|
@@ -213,23 +175,38 @@ export async function sendToSwapBasket(wallet, base, tokenId, stage, meta = {},
|
|
|
213
175
|
return out;
|
|
214
176
|
}
|
|
215
177
|
/**
|
|
216
|
-
* The
|
|
217
|
-
*
|
|
218
|
-
*
|
|
178
|
+
* The labelled staging actions' token outputs not in `amm-swap` (0.40.0): every output of an action
|
|
179
|
+
* labelled `SWAP_STAGE_LABEL` whose description names a staged key and whose script is a 1Color
|
|
180
|
+
* value output, its basket not `amm-swap`. A wallet that refuses the read: none.
|
|
219
181
|
*/
|
|
220
|
-
export async function
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
182
|
+
export async function unfiledStagedTokens(wallet) {
|
|
183
|
+
const out = [];
|
|
184
|
+
try {
|
|
185
|
+
const r = await wallet.listActions({ labels: [SWAP_STAGE_LABEL], includeOutputs: true, includeOutputLockingScripts: true, limit: 1000 });
|
|
186
|
+
for (const a of r.actions ?? []) {
|
|
187
|
+
for (const o of a.outputs ?? []) {
|
|
188
|
+
const keyID = swapKeyIdOfDescription(o.outputDescription);
|
|
189
|
+
const t = keyID && o.lockingScript ? decodeOneColor(Utils.toArray(o.lockingScript, "hex")) : null;
|
|
190
|
+
if (!keyID || !t || t.role !== "value" || !t.idBytes || o.basket === SWAP_BASKET)
|
|
191
|
+
continue;
|
|
192
|
+
out.push({ txid: a.txid, vout: o.outputIndex, tokenId: tokenIdOfWire(t.idBytes), amount: t.amount, keyID });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
/* no history read: none */
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
/** One unfiled staged token output filed in `amm-swap` from its transaction's BEEF (basket insertion, as `stage` files it). */
|
|
202
|
+
export async function fileStagedToken(wallet, u, beef) {
|
|
203
|
+
const r = await wallet.internalizeAction({
|
|
204
|
+
tx: beef,
|
|
205
|
+
outputs: [{ outputIndex: u.vout, protocol: "basket insertion", insertionRemittance: { basket: SWAP_BASKET, customInstructions: swapInstructions({ assetId: sdkTokenId(u.tokenId), amount: u.amount, protocolID: SWAP_PROTOCOL, keyID: u.keyID, counterparty: "self" }) } }],
|
|
206
|
+
description: "AMM swap: a staged token filed",
|
|
227
207
|
labels: ["amm-swap"],
|
|
228
|
-
outputs: keys.map((k) => k.output),
|
|
229
|
-
options: { randomizeOutputs: false, acceptDelayedBroadcast: false },
|
|
230
208
|
});
|
|
231
|
-
|
|
232
|
-
return { txid, tx, sats: satsHeld(Transaction.fromAtomicBEEF(tx), txid, keys, 0, errors), errors };
|
|
209
|
+
return r.accepted;
|
|
233
210
|
}
|
|
234
211
|
/** The page's unlocking scripts for the `amm-swap` outputs `holdings` in the wallet's transaction `tx`, each checked with `Spend`. */
|
|
235
212
|
export async function swapBasketSpends(wallet, tx, holdings, what) {
|
|
@@ -259,8 +236,8 @@ export async function moveBack(i) {
|
|
|
259
236
|
const topic = `tm_color_${assetId}`;
|
|
260
237
|
const rows = await basketRows(wallet, SWAP_BASKET);
|
|
261
238
|
const holdings = tokenInputsOf(rows, assetId);
|
|
262
|
-
// 0.
|
|
263
|
-
const satsRows = satsInputsOf(rows)
|
|
239
|
+
// 0.40.0: every sats row goes in too (they are no token's); the wallet's change takes their sats.
|
|
240
|
+
const satsRows = satsInputsOf(rows);
|
|
264
241
|
if (holdings.length === 0 && satsRows.length > 0)
|
|
265
242
|
return await moveBackSats(wallet, sym, satsRows);
|
|
266
243
|
if (holdings.length === 0)
|
|
@@ -279,7 +256,7 @@ export async function moveBack(i) {
|
|
|
279
256
|
inputBEEF: sources.toBinary(),
|
|
280
257
|
inputs: [
|
|
281
258
|
...holdings.map((h) => ({ outpoint: h.outpoint, unlockingScriptLength: P2PKH_UNLOCK_LENGTH, inputDescription: describe(`${sym} in the swap basket`) })),
|
|
282
|
-
...satsRows.map((h) => ({ outpoint: h.outpoint, unlockingScriptLength: P2PKH_UNLOCK_LENGTH, inputDescription: describe(
|
|
259
|
+
...satsRows.map((h) => ({ outpoint: h.outpoint, unlockingScriptLength: P2PKH_UNLOCK_LENGTH, inputDescription: describe("Sats in the swap basket") })),
|
|
283
260
|
],
|
|
284
261
|
outputs: [{ lockingScript, satoshis: 1, outputDescription: describe(`${sym} back to your wallet`) }],
|
|
285
262
|
labels: [...transferPlanLabels(assetId, { ownedOutputs: [{ outputIndex: 0, ...key.receipt }] }), "amm-swap"],
|
|
@@ -296,14 +273,17 @@ export async function moveBack(i) {
|
|
|
296
273
|
return out;
|
|
297
274
|
}
|
|
298
275
|
/**
|
|
299
|
-
* Move back with only sats
|
|
300
|
-
*
|
|
276
|
+
* Move back with only sats in the swap basket (0.29.6, David Case 2026-11-03; 0.40.0: every sats
|
|
277
|
+
* row, `satsRows` default the basket's): ONE plain wallet createAction (no `p color` labels, the module
|
|
301
278
|
* is not in the path) with the token's `amm-swap` sats rows as explicit inputs (their sources in
|
|
302
279
|
* `inputBEEF`) and NO outputs: the wallet keeps everything as its change and pays the fee from it.
|
|
303
280
|
* The page unlocks the rows at signAction; the wallet broadcasts and marks the rows spent itself
|
|
304
281
|
* (no relinquish, no take-in, nothing kept). Nothing is submitted to an overlay (no token moves).
|
|
305
282
|
*/
|
|
306
|
-
async function moveBackSats(wallet, sym, satsRows) {
|
|
283
|
+
export async function moveBackSats(wallet, sym = "the swap", satsRows) {
|
|
284
|
+
satsRows ??= await swapSatsHoldings(wallet);
|
|
285
|
+
if (satsRows.length === 0)
|
|
286
|
+
throw new Error("the swap basket holds no sats");
|
|
307
287
|
const sources = await swapHoldingBeef(wallet);
|
|
308
288
|
for (const h of satsRows)
|
|
309
289
|
if (!sources.findTxid(h.txid))
|
|
@@ -311,7 +291,7 @@ async function moveBackSats(wallet, sym, satsRows) {
|
|
|
311
291
|
const { txid, tx: beef } = await runAppSignedAction(wallet, {
|
|
312
292
|
description: describe(`AMM move back: ${sym} sats`),
|
|
313
293
|
inputBEEF: sources.toBinary(),
|
|
314
|
-
inputs: satsRows.map((h) => ({ outpoint: h.outpoint, unlockingScriptLength: P2PKH_UNLOCK_LENGTH, inputDescription: describe(
|
|
294
|
+
inputs: satsRows.map((h) => ({ outpoint: h.outpoint, unlockingScriptLength: P2PKH_UNLOCK_LENGTH, inputDescription: describe("Sats in the swap basket") })),
|
|
315
295
|
outputs: [],
|
|
316
296
|
labels: ["amm-swap"],
|
|
317
297
|
options: { randomizeOutputs: false, acceptDelayedBroadcast: false },
|
package/dist/market/plan.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export interface SwapForm {
|
|
|
15
15
|
/** Sats (bsvToToken) or tokens in display units (tokenToBsv). */
|
|
16
16
|
amount: string;
|
|
17
17
|
slippageBps: string;
|
|
18
|
-
|
|
18
|
+
/** 0.40.2: the most pools the order may draw on, a positive integer the caller gives. */
|
|
19
|
+
maxSources: number;
|
|
19
20
|
}
|
|
20
21
|
export type PlanInput = {
|
|
21
22
|
ok: true;
|
package/dist/market/plan.js
CHANGED