@dfm-fi/agent 0.2.715 → 0.2.730
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/dist/recovery-flow.d.ts.map +1 -1
- package/dist/recovery-flow.deadleg.test.d.ts +2 -0
- package/dist/recovery-flow.deadleg.test.d.ts.map +1 -0
- package/dist/recovery-flow.deadleg.test.js +143 -0
- package/dist/recovery-flow.deadleg.test.js.map +1 -0
- package/dist/recovery-flow.js +50 -1
- package/dist/recovery-flow.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery-flow.d.ts","sourceRoot":"","sources":["../src/recovery-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"recovery-flow.d.ts","sourceRoot":"","sources":["../src/recovery-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAgB9C,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,YAAY,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,GAAG,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC,CAAC;IAC1G,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,SAAS,EAAE;QACT,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,YAAY,CAAC,CAmCvB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAmBxF;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE,OAAO,CAAC,oBAAoB,CAAC,CAgH/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-flow.deadleg.test.d.ts","sourceRoot":"","sources":["../src/recovery-flow.deadleg.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #64 regression (runnable: `npx tsx agent/src/recovery-flow.deadleg.test.ts`).
|
|
3
|
+
*
|
|
4
|
+
* A dead-legged REDEEM reaches READY_TO_CLOSE with an unsellable leg paid in-kind
|
|
5
|
+
* (`actualOut = 0`). Its pinned `min_usdc_out` assumed EVERY leg sells, so the
|
|
6
|
+
* realized USDC is below the floor and `zap_out_close_v2` reverts `UsdcBelowMin`
|
|
7
|
+
* (6023). Before the fix, `runUpdateEnvelope` short-circuited on `readyToClose`
|
|
8
|
+
* and refused the exact recovery it implements (lower `min_usdc_out`, then close).
|
|
9
|
+
*
|
|
10
|
+
* This asserts the NEW contract:
|
|
11
|
+
* 1. a dead-legged redeem REACHES the update-envelope prepare (guard no longer
|
|
12
|
+
* blocks it);
|
|
13
|
+
* 2. the new `min_usdc_out` is the DETERMINISTIC dead-leg reduction
|
|
14
|
+
* (`old − Σ dead-leg minOuts`), not a blind pct — and is low enough to clear;
|
|
15
|
+
* 3. a CLEANLY-sold readyToClose redeem STILL throws "already ready" (nothing to
|
|
16
|
+
* loosen);
|
|
17
|
+
* 4. a readyToClose DEPOSIT STILL throws "already ready".
|
|
18
|
+
*/
|
|
19
|
+
import { runUpdateEnvelope } from './recovery-flow.js';
|
|
20
|
+
const VAULT = 'Vau1t1111111111111111111111111111111111111';
|
|
21
|
+
const USER = 'User1111111111111111111111111111111111111111';
|
|
22
|
+
const ESCROW = 'Escrow11111111111111111111111111111111111111';
|
|
23
|
+
/** A 3-leg REDEEM at READY_TO_CLOSE; leg `deadIdx` was marked dead (actualOut 0). */
|
|
24
|
+
function deadLeggedRedeem(deadIdx) {
|
|
25
|
+
const minOuts = ['10000000', '7500000', '2079179']; // per-leg gross USDC floors
|
|
26
|
+
const actualOuts = deadIdx === null
|
|
27
|
+
? ['10100000', '7600000', '2100000'] // all sold
|
|
28
|
+
: minOuts.map((v, i) => (i === deadIdx ? '0' : String(Number(v) + 100_000)));
|
|
29
|
+
return {
|
|
30
|
+
escrowPda: ESCROW,
|
|
31
|
+
vault: VAULT,
|
|
32
|
+
user: USER,
|
|
33
|
+
mode: 1, // redeem
|
|
34
|
+
state: 5, // READY_TO_CLOSE
|
|
35
|
+
nAssets: 3,
|
|
36
|
+
assetMints: [],
|
|
37
|
+
filledMask: 0x7, // 3/3 (sold or dead)
|
|
38
|
+
legProgress: [],
|
|
39
|
+
legsFilled: 3,
|
|
40
|
+
legsTotal: 3,
|
|
41
|
+
readyToClose: true,
|
|
42
|
+
legFailures: [],
|
|
43
|
+
openedAt: 0,
|
|
44
|
+
expiresAt: 1_000,
|
|
45
|
+
isExpired: true,
|
|
46
|
+
slippageBps: 300,
|
|
47
|
+
plannedAmountIn: [],
|
|
48
|
+
minOuts,
|
|
49
|
+
actualOuts,
|
|
50
|
+
inputAmount: '0',
|
|
51
|
+
sharesIn: '1000000',
|
|
52
|
+
minSharesOut: '0',
|
|
53
|
+
minUsdcOut: '19496890', // pinned assuming ALL legs sell
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function readyDeposit() {
|
|
57
|
+
const s = deadLeggedRedeem(null);
|
|
58
|
+
return { ...s, mode: 0, minUsdcOut: '0' };
|
|
59
|
+
}
|
|
60
|
+
let failures = 0;
|
|
61
|
+
function assert(cond, msg) {
|
|
62
|
+
if (!cond) {
|
|
63
|
+
console.error(` ✗ ${msg}`);
|
|
64
|
+
failures++;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.log(` ✓ ${msg}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const STOP = '__STOP_BEFORE_SIGN__';
|
|
71
|
+
/** Run runUpdateEnvelope with a mock API that captures the prepare opts, then
|
|
72
|
+
* throws STOP so we never reach real signing/submit. Returns capture or the
|
|
73
|
+
* guard error message. */
|
|
74
|
+
async function driveCapture(status, opts = {}) {
|
|
75
|
+
let captured;
|
|
76
|
+
const mockApi = {
|
|
77
|
+
getZapV2Status: async () => status,
|
|
78
|
+
prepareUpdateEnvelopeV2: async (_v, o) => {
|
|
79
|
+
captured = o;
|
|
80
|
+
throw new Error(STOP);
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
await runUpdateEnvelope({}, mockApi, USER, VAULT, opts);
|
|
85
|
+
return { err: '(no throw — unexpected)' };
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
const msg = e.message;
|
|
89
|
+
if (msg === STOP)
|
|
90
|
+
return { captured };
|
|
91
|
+
return { err: msg };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function caseDeadLegRecovers() {
|
|
95
|
+
console.log('case: dead-legged redeem → guard passes, min_usdc_out reduced by dead leg');
|
|
96
|
+
const { captured, err } = await driveCapture(deadLeggedRedeem(2));
|
|
97
|
+
assert(!err, `reached prepare, no guard throw (got err='${err ?? ''}')`);
|
|
98
|
+
// old 19,496,890 − dead-leg minOuts[2] 2,079,179 = 17,417,711 (more relaxed than
|
|
99
|
+
// the 5% pct path 18,522,045, so it dominates).
|
|
100
|
+
assert(captured?.newMinUsdcOut === '17417711', `newMinUsdcOut === '17417711' (deterministic dead-leg reduction) — got '${captured?.newMinUsdcOut}'`);
|
|
101
|
+
// The reduced floor must be ≤ realized net USDC (17,433,306 in the live #64 repro)
|
|
102
|
+
// so zap_out_close_v2 clears instead of reverting UsdcBelowMin.
|
|
103
|
+
assert(BigInt(captured?.newMinUsdcOut ?? '0') <= 17433306n, 'reduced floor ≤ live realized USDC (17,433,306) → close clears');
|
|
104
|
+
}
|
|
105
|
+
async function caseCleanRedeemStillBlocked() {
|
|
106
|
+
console.log('case: cleanly-sold readyToClose redeem → still blocked (nothing to loosen)');
|
|
107
|
+
const { captured, err } = await driveCapture(deadLeggedRedeem(null));
|
|
108
|
+
assert(captured === undefined, 'did NOT reach prepare');
|
|
109
|
+
assert(!!err && /already READY to close/i.test(err), `threw "already ready" (got '${err}')`);
|
|
110
|
+
}
|
|
111
|
+
async function caseDepositStillBlocked() {
|
|
112
|
+
console.log('case: readyToClose deposit → still blocked');
|
|
113
|
+
const { captured, err } = await driveCapture(readyDeposit());
|
|
114
|
+
assert(captured === undefined, 'did NOT reach prepare');
|
|
115
|
+
assert(!!err && /already READY to close/i.test(err), `threw "already ready" (got '${err}')`);
|
|
116
|
+
}
|
|
117
|
+
async function caseMidCrankNoDeadLegReduction() {
|
|
118
|
+
// C3 guard: a MID-CRANK (PARTIAL_OUT, not readyToClose) redeem with unfilled
|
|
119
|
+
// legs (actualOut 0) must NOT be treated as dead-legged — only the pct loosen
|
|
120
|
+
// applies. Otherwise the still-pending legs' floors get subtracted and
|
|
121
|
+
// min_usdc_out is slashed far below the intended 5%.
|
|
122
|
+
console.log('case: mid-crank redeem with unfilled legs → pct loosen only (no dead-leg reduction)');
|
|
123
|
+
const mid = deadLeggedRedeem(2); // has an actualOut-0 leg…
|
|
124
|
+
mid.readyToClose = false; // …but it's NOT ready → those 0s are "pending", not "dead"
|
|
125
|
+
mid.state = 4; // PARTIAL_OUT
|
|
126
|
+
const { captured, err } = await driveCapture(mid);
|
|
127
|
+
assert(!err, `reached prepare (mid-crank loosen allowed) — got err='${err ?? ''}'`);
|
|
128
|
+
// Pure 5% pct path: floor(19,496,890 * 95 / 100) = 18,522,045. The dead-leg
|
|
129
|
+
// reduction (→17,417,711) must NOT have been applied.
|
|
130
|
+
assert(captured?.newMinUsdcOut === '18522045', `newMinUsdcOut === '18522045' (pct-only, NOT dead-leg-reduced) — got '${captured?.newMinUsdcOut}'`);
|
|
131
|
+
}
|
|
132
|
+
(async () => {
|
|
133
|
+
await caseDeadLegRecovers();
|
|
134
|
+
await caseCleanRedeemStillBlocked();
|
|
135
|
+
await caseDepositStillBlocked();
|
|
136
|
+
await caseMidCrankNoDeadLegReduction();
|
|
137
|
+
if (failures > 0) {
|
|
138
|
+
console.error(`\n#64 regression FAILED (${failures} assertion(s))`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
console.log('\n#64 regression PASSED');
|
|
142
|
+
})();
|
|
143
|
+
//# sourceMappingURL=recovery-flow.deadleg.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-flow.deadleg.test.js","sourceRoot":"","sources":["../src/recovery-flow.deadleg.test.ts"],"names":[],"mappings":"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"}
|
package/dist/recovery-flow.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { signBase64Transaction } from './signing.js';
|
|
2
2
|
import { submitSignedTx } from './submit.js';
|
|
3
3
|
const MAX_SLIPPAGE_BPS = 1_000; // MAX_ZAP_SLIPPAGE_BPS — the per-leg on-chain cap.
|
|
4
|
+
/** Parse a raw-units decimal string to bigint, treating missing/garbage as 0. */
|
|
5
|
+
function safeBig(s) {
|
|
6
|
+
if (!s)
|
|
7
|
+
return 0n;
|
|
8
|
+
try {
|
|
9
|
+
return BigInt(s);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return 0n;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
4
15
|
/**
|
|
5
16
|
* Cancel a stalled escrow and return its current holdings to the test wallet.
|
|
6
17
|
* Reads the live escrow first (for a clean "no escrow" message + to report the
|
|
@@ -75,7 +86,29 @@ export async function runUpdateEnvelope(config, api, user, vault, opts) {
|
|
|
75
86
|
if (!live) {
|
|
76
87
|
throw new Error(`No open zap-v2 escrow for vault ${vault.slice(0, 8)}… and this wallet — nothing to update.`);
|
|
77
88
|
}
|
|
78
|
-
|
|
89
|
+
// #64 — DEAD-LEG redeem recovery. A REDEEM can reach READY_TO_CLOSE with a leg
|
|
90
|
+
// that was marked DEAD (unsellable → paid in-kind, `actualOut = 0`). Its pinned
|
|
91
|
+
// `min_usdc_out` assumed EVERY leg sells, so realized USDC < floor and
|
|
92
|
+
// `zap_out_close_v2` reverts `UsdcBelowMin` (6023) — a permanently-stuck redeem.
|
|
93
|
+
// On-chain `update_slippage_envelope` EXPLICITLY accepts READY_TO_CLOSE precisely
|
|
94
|
+
// so the owner can LOWER `min_usdc_out` here and then close. So the readyToClose
|
|
95
|
+
// guard must NOT short-circuit a dead-legged redeem — that's the exact recovery
|
|
96
|
+
// this function implements (previously unreachable → the #64 bug).
|
|
97
|
+
// A leg reads `actualOut === 0` when it is DEAD *or* simply not-yet-cranked, so
|
|
98
|
+
// it only means "dead" on a READY_TO_CLOSE escrow (every leg is terminal — sold
|
|
99
|
+
// or dead). Gating on `readyToClose` is essential: on a mid-crank (PARTIAL_OUT)
|
|
100
|
+
// stall — the ordinary update-envelope path — the still-unfilled legs also read
|
|
101
|
+
// 0, and treating them as dead would over-subtract their floors and slash
|
|
102
|
+
// `min_usdc_out` far below the intended pct loosen.
|
|
103
|
+
const isRedeem = live.mode === 1;
|
|
104
|
+
const deadLegIdxs = isRedeem && live.readyToClose
|
|
105
|
+
? (live.actualOuts ?? [])
|
|
106
|
+
.map((o, i) => (safeBig(o) === 0n ? i : -1))
|
|
107
|
+
.filter((i) => i >= 0)
|
|
108
|
+
: [];
|
|
109
|
+
if (live.readyToClose && deadLegIdxs.length === 0) {
|
|
110
|
+
// Genuinely nothing to loosen: a deposit (no USDC floor), or a redeem whose
|
|
111
|
+
// legs all sold (realized already ≥ floor). Close, don't update-envelope.
|
|
79
112
|
throw new Error(`Escrow is already READY to close (${live.legsFilled}/${live.legsTotal} legs filled) — ` +
|
|
80
113
|
`call the deposit/redeem close path, not update-envelope.`);
|
|
81
114
|
}
|
|
@@ -95,6 +128,22 @@ export async function runUpdateEnvelope(config, api, user, vault, opts) {
|
|
|
95
128
|
// above — use it directly (no Math.round, which would silently no-op a
|
|
96
129
|
// fractional pct).
|
|
97
130
|
let relaxed = (oldUsdc * BigInt(100 - loosenPct)) / 100n;
|
|
131
|
+
// #64 — DEAD-LEG exact reduction. A dead leg realizes 0 USDC (paid in-kind),
|
|
132
|
+
// so the net floor must drop by its pinned per-leg USDC contribution
|
|
133
|
+
// (`minOuts[i]`) for the close to clear. Reducing by exactly the dead legs'
|
|
134
|
+
// floors leaves a floor equal to the SURVIVING legs' floors — which those
|
|
135
|
+
// legs provably met (they cleared their own `min_out` to fill). Take the MORE
|
|
136
|
+
// relaxed of the pct path and this exact reduction: lower is always safe (the
|
|
137
|
+
// exit fee is still taken on gross realized USDC at close), and it guarantees
|
|
138
|
+
// the reverting `UsdcBelowMin` floor is actually cleared, not just softened.
|
|
139
|
+
if (deadLegIdxs.length > 0) {
|
|
140
|
+
let deadSum = 0n;
|
|
141
|
+
for (const i of deadLegIdxs)
|
|
142
|
+
deadSum += safeBig((live.minOuts ?? [])[i]);
|
|
143
|
+
const byDeadLeg = oldUsdc > deadSum ? oldUsdc - deadSum : 0n;
|
|
144
|
+
if (byDeadLeg < relaxed)
|
|
145
|
+
relaxed = byDeadLeg;
|
|
146
|
+
}
|
|
98
147
|
if (relaxed > oldUsdc)
|
|
99
148
|
relaxed = oldUsdc;
|
|
100
149
|
newMinUsdcOut = relaxed.toString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery-flow.js","sourceRoot":"","sources":["../src/recovery-flow.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,gBAAgB,GAAG,KAAK,CAAC,CAAC,mDAAmD;
|
|
1
|
+
{"version":3,"file":"recovery-flow.js","sourceRoot":"","sources":["../src/recovery-flow.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED