@xochi/client 0.0.0-stage → 0.1.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/CHANGELOG.md +64 -0
- package/LICENSE +21 -0
- package/README.md +120 -2
- package/dist/cjs/claim.d.ts +33 -0
- package/dist/cjs/claim.js +44 -0
- package/dist/cjs/fee.d.ts +247 -0
- package/dist/cjs/fee.js +295 -0
- package/dist/cjs/index.d.ts +18 -0
- package/dist/cjs/index.js +33 -0
- package/dist/cjs/machine/claimInfo.d.ts +29 -0
- package/dist/cjs/machine/claimInfo.js +26 -0
- package/dist/cjs/machine/decisions.d.ts +452 -0
- package/dist/cjs/machine/decisions.js +665 -0
- package/dist/cjs/machine/deposit.d.ts +113 -0
- package/dist/cjs/machine/deposit.js +265 -0
- package/dist/cjs/machine/index.d.ts +23 -0
- package/dist/cjs/machine/index.js +53 -0
- package/dist/cjs/machine/machine.d.ts +411 -0
- package/dist/cjs/machine/machine.js +1627 -0
- package/dist/cjs/machine/phase.d.ts +24 -0
- package/dist/cjs/machine/phase.js +45 -0
- package/dist/cjs/machine/quoteChecks.d.ts +41 -0
- package/dist/cjs/machine/quoteChecks.js +82 -0
- package/dist/cjs/machine/receipt.d.ts +151 -0
- package/dist/cjs/machine/receipt.js +69 -0
- package/dist/cjs/machine/recipientEcho.d.ts +16 -0
- package/dist/cjs/machine/recipientEcho.js +27 -0
- package/dist/cjs/machine/solverPin.d.ts +110 -0
- package/dist/cjs/machine/solverPin.js +214 -0
- package/dist/cjs/machine/state.d.ts +167 -0
- package/dist/cjs/machine/state.js +31 -0
- package/dist/cjs/machine/transition.d.ts +15 -0
- package/dist/cjs/machine/transition.js +20 -0
- package/dist/cjs/machine/validateRequest.d.ts +15 -0
- package/dist/cjs/machine/validateRequest.js +127 -0
- package/dist/cjs/machine/view.d.ts +3 -0
- package/dist/cjs/machine/view.js +74 -0
- package/dist/cjs/machine/wire.d.ts +527 -0
- package/dist/cjs/machine/wire.js +280 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/shared/api.d.ts +9 -0
- package/dist/cjs/shared/api.js +5 -0
- package/dist/cjs/shared/chains.d.ts +243 -0
- package/dist/cjs/shared/chains.js +250 -0
- package/dist/cjs/shared/eip712.d.ts +44 -0
- package/dist/cjs/shared/eip712.js +64 -0
- package/dist/cjs/shared/index.d.ts +16 -0
- package/dist/cjs/shared/index.js +32 -0
- package/dist/cjs/shared/intentStream.d.ts +74 -0
- package/dist/cjs/shared/intentStream.js +59 -0
- package/dist/cjs/shared/intents.d.ts +88 -0
- package/dist/cjs/shared/intents.js +227 -0
- package/dist/cjs/shared/schemas.d.ts +928 -0
- package/dist/cjs/shared/schemas.js +524 -0
- package/dist/cjs/shared/shieldedPreview.d.ts +841 -0
- package/dist/cjs/shared/shieldedPreview.js +128 -0
- package/dist/cjs/shared/stealth.d.ts +53 -0
- package/dist/cjs/shared/stealth.js +68 -0
- package/dist/cjs/shared/trading.d.ts +153 -0
- package/dist/cjs/shared/trading.js +185 -0
- package/dist/cjs/shared/types.d.ts +37 -0
- package/dist/cjs/shared/types.js +7 -0
- package/dist/cjs/shared/validators.d.ts +30 -0
- package/dist/cjs/shared/validators.js +79 -0
- package/dist/cjs/transport/client.d.ts +234 -0
- package/dist/cjs/transport/client.js +1456 -0
- package/dist/esm/claim.d.ts +33 -0
- package/dist/esm/claim.js +41 -0
- package/dist/esm/fee.d.ts +247 -0
- package/dist/esm/fee.js +285 -0
- package/dist/esm/index.d.ts +18 -0
- package/dist/esm/index.js +17 -0
- package/dist/esm/machine/claimInfo.d.ts +29 -0
- package/dist/esm/machine/claimInfo.js +23 -0
- package/dist/esm/machine/decisions.d.ts +452 -0
- package/dist/esm/machine/decisions.js +640 -0
- package/dist/esm/machine/deposit.d.ts +113 -0
- package/dist/esm/machine/deposit.js +260 -0
- package/dist/esm/machine/index.d.ts +23 -0
- package/dist/esm/machine/index.js +23 -0
- package/dist/esm/machine/machine.d.ts +411 -0
- package/dist/esm/machine/machine.js +1621 -0
- package/dist/esm/machine/phase.d.ts +24 -0
- package/dist/esm/machine/phase.js +42 -0
- package/dist/esm/machine/quoteChecks.d.ts +41 -0
- package/dist/esm/machine/quoteChecks.js +78 -0
- package/dist/esm/machine/receipt.d.ts +151 -0
- package/dist/esm/machine/receipt.js +64 -0
- package/dist/esm/machine/recipientEcho.d.ts +16 -0
- package/dist/esm/machine/recipientEcho.js +24 -0
- package/dist/esm/machine/solverPin.d.ts +110 -0
- package/dist/esm/machine/solverPin.js +210 -0
- package/dist/esm/machine/state.d.ts +167 -0
- package/dist/esm/machine/state.js +28 -0
- package/dist/esm/machine/transition.d.ts +15 -0
- package/dist/esm/machine/transition.js +17 -0
- package/dist/esm/machine/validateRequest.d.ts +15 -0
- package/dist/esm/machine/validateRequest.js +123 -0
- package/dist/esm/machine/view.d.ts +3 -0
- package/dist/esm/machine/view.js +71 -0
- package/dist/esm/machine/wire.d.ts +527 -0
- package/dist/esm/machine/wire.js +266 -0
- package/dist/esm/shared/api.d.ts +9 -0
- package/dist/esm/shared/api.js +4 -0
- package/dist/esm/shared/chains.d.ts +243 -0
- package/dist/esm/shared/chains.js +236 -0
- package/dist/esm/shared/eip712.d.ts +44 -0
- package/dist/esm/shared/eip712.js +61 -0
- package/dist/esm/shared/index.d.ts +16 -0
- package/dist/esm/shared/index.js +16 -0
- package/dist/esm/shared/intentStream.d.ts +74 -0
- package/dist/esm/shared/intentStream.js +55 -0
- package/dist/esm/shared/intents.d.ts +88 -0
- package/dist/esm/shared/intents.js +213 -0
- package/dist/esm/shared/schemas.d.ts +928 -0
- package/dist/esm/shared/schemas.js +521 -0
- package/dist/esm/shared/shieldedPreview.d.ts +841 -0
- package/dist/esm/shared/shieldedPreview.js +124 -0
- package/dist/esm/shared/stealth.d.ts +53 -0
- package/dist/esm/shared/stealth.js +64 -0
- package/dist/esm/shared/trading.d.ts +153 -0
- package/dist/esm/shared/trading.js +182 -0
- package/dist/esm/shared/types.d.ts +37 -0
- package/dist/esm/shared/types.js +6 -0
- package/dist/esm/shared/validators.d.ts +30 -0
- package/dist/esm/shared/validators.js +74 -0
- package/dist/esm/transport/client.d.ts +234 -0
- package/dist/esm/transport/client.js +1447 -0
- package/package.json +61 -4
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Zod Schemas
|
|
4
|
+
*
|
|
5
|
+
* The Worker's quote and execute RESPONSE wire contract (`/api/intent/quote`,
|
|
6
|
+
* `/api/intent/execute`), with the primitives it closes over. Types are derived
|
|
7
|
+
* from schemas via z.infer -- single source of truth.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.QUOTED_EXPIRY_GRACE_SECS = exports.EXECUTE_RESEND_MARGIN_SECS = exports.MAX_EXECUTE_RESENDS = exports.ExecuteResponseSchema = exports.READ_TOKEN_HEADER = exports.READ_TOKEN_PATTERN = exports.READ_TOKEN_LENGTH = exports.QuoteResponseSchema = exports.FeeDenominationSchema = exports.FeeBreakdownSchema = exports.ClaimInfoSchema = exports.SettlementOptionSchema = exports.Eip712TypedDataSchema = exports.QuotePromoSchema = exports.PROMO_TIER_BETTER_NOTE = exports.PROMO_NOT_APPLIED_NOTE = exports.PROMO_CODE_REGEX = exports.QuoteReferralSchema = exports.REFERRAL_NEUTRAL_NOTE = exports.REFERRAL_NOT_APPLIED_NOTE = exports.REFERRAL_CODE_REGEX = exports.paymentMethod = exports.settlementType = exports.recipientAddress = exports.tronAddress = exports.ethAddress = void 0;
|
|
11
|
+
const zod_1 = require("zod");
|
|
12
|
+
const validators_js_1 = require("./validators.js");
|
|
13
|
+
const intents_js_1 = require("./intents.js");
|
|
14
|
+
// ============================================================
|
|
15
|
+
// Primitives
|
|
16
|
+
// ============================================================
|
|
17
|
+
/** Ethereum address: 0x-prefixed, 42 hex chars. Single source: validators.ts. */
|
|
18
|
+
exports.ethAddress = zod_1.z.string().regex(validators_js_1.ETH_ADDRESS_REGEX);
|
|
19
|
+
/**
|
|
20
|
+
* Tron base58check address: leading `T`, 34 chars, base58 alphabet (no
|
|
21
|
+
* 0/O/I/l). Structural check mirroring `TRON_ADDRESS_REGEX` in validators.ts;
|
|
22
|
+
* kept inline here so the schema module stays free of the viem import chain.
|
|
23
|
+
*/
|
|
24
|
+
exports.tronAddress = zod_1.z
|
|
25
|
+
.string()
|
|
26
|
+
.regex(/^T[1-9A-HJ-NP-Za-km-z]{33}$/, "Must be a Tron base58 address");
|
|
27
|
+
/**
|
|
28
|
+
* Recipient address for a non-EVM destination: the user's own address on the
|
|
29
|
+
* destination chain (EVM routes settle to the signing wallet, so it is
|
|
30
|
+
* optional). EVM hex20 or Tron base58.
|
|
31
|
+
*/
|
|
32
|
+
exports.recipientAddress = zod_1.z.union([exports.ethAddress, exports.tronAddress]);
|
|
33
|
+
/** Settlement type */
|
|
34
|
+
exports.settlementType = zod_1.z
|
|
35
|
+
.enum(["public", "shielded", "stealth"])
|
|
36
|
+
.describe("Where the payout lands. `public` settles to the wallet address. `stealth` settles to a one-time address on Ethereum L1 (ERC-5564). `shielded` (Aztec L2 notes) is planned and not yet live: it is gated by SHIELDED_SETTLEMENT_LIVE, served as unavailable in `settlement_options`, and refused as a `settlement_preference` while that is off. Read `settlement_options` for live availability rather than assuming this enum's members are all fillable.");
|
|
37
|
+
/**
|
|
38
|
+
* Pull+gas payment method the quote's eip712 authorizes.
|
|
39
|
+
*
|
|
40
|
+
* `deposit` is the odd one out and is not a pull at all: on a non-EVM origin
|
|
41
|
+
* (Tron) the user sends the tokens themselves to a served `deposit_address`,
|
|
42
|
+
* at their own network cost, with no wallet signature anywhere in the flow.
|
|
43
|
+
* It is in this enum because it is what belongs in this FIELD, which answers
|
|
44
|
+
* "how does the origin leg get funded".
|
|
45
|
+
*
|
|
46
|
+
* Without it the wire lies: a Tron-origin quote reports `payment_method:
|
|
47
|
+
* "permit2"` next to `gasless: false` and a `deposit_address`, describing a
|
|
48
|
+
* Permit2 pull on a chain that has no Permit2. The browser papers over it
|
|
49
|
+
* (`resolvePaymentMethod` overrides to `deposit` whenever a deposit address is
|
|
50
|
+
* present), so a human never sees the wrong label; every non-browser consumer
|
|
51
|
+
* does: the MCP server, the OpenAPI contract, and any agent branching on this
|
|
52
|
+
* field to decide whether to prepare a signature.
|
|
53
|
+
*/
|
|
54
|
+
exports.paymentMethod = zod_1.z.enum([
|
|
55
|
+
"erc3009",
|
|
56
|
+
"permit2",
|
|
57
|
+
"pimlico",
|
|
58
|
+
"x402",
|
|
59
|
+
"mpp",
|
|
60
|
+
"approval",
|
|
61
|
+
"deposit",
|
|
62
|
+
]);
|
|
63
|
+
// ============================================================
|
|
64
|
+
// Referral and promo outcomes (quote response siblings of fee_breakdown)
|
|
65
|
+
// ============================================================
|
|
66
|
+
/**
|
|
67
|
+
* A referral code as stored (xochi#583): 3-32 of `A-Z 0-9 _ -`, starting with a
|
|
68
|
+
* letter or digit. Stored and compared uppercase, so a link's `?ref=summer` and
|
|
69
|
+
* `SUMMER` are the same code. Migration 0019 enforces the same shape.
|
|
70
|
+
*/
|
|
71
|
+
exports.REFERRAL_CODE_REGEX = /^[A-Z0-9][A-Z0-9_-]{2,31}$/;
|
|
72
|
+
/**
|
|
73
|
+
* The note served when a `ref` was sent and is not applied. ONE text for every
|
|
74
|
+
* reason (unknown, expired, revoked, scoped to another key, a key that does not
|
|
75
|
+
* accept referrals, self-referral): a per-reason note would let a caller
|
|
76
|
+
* enumerate which codes exist.
|
|
77
|
+
*/
|
|
78
|
+
exports.REFERRAL_NOT_APPLIED_NOTE = "Referral code not applied.";
|
|
79
|
+
/**
|
|
80
|
+
* The neutral referral line (xochi#603): served on a quote whose `ref` was
|
|
81
|
+
* applied when the request is not signed in as the quoting wallet. One fixed
|
|
82
|
+
* text, identical whether or not the wallet has a referral binding.
|
|
83
|
+
*/
|
|
84
|
+
exports.REFERRAL_NEUTRAL_NOTE = "A referrer may earn part of the routing fee on this trade.";
|
|
85
|
+
exports.QuoteReferralSchema = zod_1.z
|
|
86
|
+
.union([
|
|
87
|
+
zod_1.z
|
|
88
|
+
.object({
|
|
89
|
+
code: zod_1.z
|
|
90
|
+
.string()
|
|
91
|
+
.regex(exports.REFERRAL_CODE_REGEX)
|
|
92
|
+
.describe("The code this trade credits, normalized uppercase."),
|
|
93
|
+
name: zod_1.z
|
|
94
|
+
.string()
|
|
95
|
+
.describe("Affiliate display name, rendered as `Referral: {name}` on the routing line."),
|
|
96
|
+
})
|
|
97
|
+
.strict()
|
|
98
|
+
.describe("Served only when the request carries a wallet-bound session (SIWE JWT or Mandate) for the quoting wallet: the code the trade will credit if executed now. The wallet's open referral binding wins over `ref`; otherwise it is the applied `ref`."),
|
|
99
|
+
zod_1.z
|
|
100
|
+
.object({
|
|
101
|
+
neutral: zod_1.z.literal(true),
|
|
102
|
+
note: zod_1.z.literal(exports.REFERRAL_NEUTRAL_NOTE),
|
|
103
|
+
})
|
|
104
|
+
.strict()
|
|
105
|
+
.describe("Served when `ref` was applied but the request is not signed in as the quoting wallet: a referrer may be credited, and who is not said. Identical whether or not the wallet has a referral binding."),
|
|
106
|
+
])
|
|
107
|
+
.describe("Who earns the routing fee layer (`fee_breakdown.routing_fee`) in place of the publishable key's routing recipient. Attribution only: no amount, rate or total differs from the same quote without `ref`.");
|
|
108
|
+
/**
|
|
109
|
+
* A promo code as stored (xochi#584): 3-32 of `A-Z 0-9 _ -`, starting with a
|
|
110
|
+
* letter or digit. Stored and compared uppercase, so `launch10` and `LAUNCH10`
|
|
111
|
+
* are the same code. Migration 0020 enforces the same shape.
|
|
112
|
+
*/
|
|
113
|
+
exports.PROMO_CODE_REGEX = /^[A-Z0-9][A-Z0-9_-]{2,31}$/;
|
|
114
|
+
/**
|
|
115
|
+
* The inline error served when a `promo` was sent and cannot be used. ONE text
|
|
116
|
+
* for every reason (unknown, malformed, expired, revoked, used up, used up by
|
|
117
|
+
* this wallet, not accepted under this publishable key, lookup ceiling, the
|
|
118
|
+
* feature off): a per-reason message would let a caller enumerate which codes
|
|
119
|
+
* exist.
|
|
120
|
+
*/
|
|
121
|
+
exports.PROMO_NOT_APPLIED_NOTE = "This code can't be applied.";
|
|
122
|
+
/**
|
|
123
|
+
* The note served when a valid code was sent but the caller's trust tier
|
|
124
|
+
* already discounts at least as much (discount = max(tier, capped promo), never
|
|
125
|
+
* both; an Institutional wallet always lands here). The code is
|
|
126
|
+
* not consumed.
|
|
127
|
+
*/
|
|
128
|
+
exports.PROMO_TIER_BETTER_NOTE = "Your trust tier already gives you a fee at least as low as this code.";
|
|
129
|
+
exports.QuotePromoSchema = zod_1.z
|
|
130
|
+
.object({
|
|
131
|
+
code: zod_1.z.string().regex(exports.PROMO_CODE_REGEX).describe("The applied code, normalized uppercase."),
|
|
132
|
+
discount_bps: zod_1.z
|
|
133
|
+
.number()
|
|
134
|
+
.int()
|
|
135
|
+
.positive()
|
|
136
|
+
.describe("Discount this code bought, in bps off the Standard venue + routing layers for the route's asset class. Already reflected in every amount of this quote and itemized as `fee_breakdown.discount`."),
|
|
137
|
+
})
|
|
138
|
+
.strict()
|
|
139
|
+
.describe("Present when the `promo` sent on the quote was applied: it beat the caller's trust-tier discount and priced this quote. One use of the code is counted when this quote is executed, not now; if the code is used up or withdrawn by then, execute refuses with `promo_unavailable` and the caller re-quotes.");
|
|
140
|
+
// ============================================================
|
|
141
|
+
// Response Schemas (worker -> client/agent wire contract)
|
|
142
|
+
// ============================================================
|
|
143
|
+
/**
|
|
144
|
+
* EIP-712 typed data the user signs. Internal keys follow the EIP-712
|
|
145
|
+
* standard (camelCase: domain.chainId, domain.verifyingContract, primaryType)
|
|
146
|
+
* and are part of the signed payload -- they are intentionally NOT snake_cased.
|
|
147
|
+
* Passed through verbatim from the solver. Mirrors the `Eip712TypedData`
|
|
148
|
+
* schema in public/openapi.yaml.
|
|
149
|
+
*/
|
|
150
|
+
exports.Eip712TypedDataSchema = zod_1.z.object({
|
|
151
|
+
// Two domain shapes coexist: legacy Xochi-owned structs and token-pull
|
|
152
|
+
// domains carry `verifyingContract`; the Riddler XochiIntent v3 domain carries
|
|
153
|
+
// `salt` (keccak256(version)) and NO verifyingContract. Both fields are
|
|
154
|
+
// optional so either shape validates and both survive the parse verbatim -- a
|
|
155
|
+
// stripped `salt` or an injected `verifyingContract` would change the domain
|
|
156
|
+
// separator and strand the signature.
|
|
157
|
+
domain: zod_1.z.object({
|
|
158
|
+
name: zod_1.z.string(),
|
|
159
|
+
version: zod_1.z.string(),
|
|
160
|
+
chainId: zod_1.z.number().int(),
|
|
161
|
+
verifyingContract: exports.ethAddress.optional(),
|
|
162
|
+
salt: zod_1.z.string().optional(),
|
|
163
|
+
}),
|
|
164
|
+
types: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.object({ name: zod_1.z.string(), type: zod_1.z.string() }))),
|
|
165
|
+
primaryType: zod_1.z.string(),
|
|
166
|
+
message: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()),
|
|
167
|
+
});
|
|
168
|
+
exports.SettlementOptionSchema = zod_1.z.object({
|
|
169
|
+
type: exports.settlementType,
|
|
170
|
+
available: zod_1.z.boolean(),
|
|
171
|
+
reason: zod_1.z.string().optional(),
|
|
172
|
+
});
|
|
173
|
+
exports.ClaimInfoSchema = zod_1.z.object({
|
|
174
|
+
factory_address: exports.ethAddress,
|
|
175
|
+
salt: zod_1.z.number().int(),
|
|
176
|
+
});
|
|
177
|
+
/**
|
|
178
|
+
* A fee layer AMOUNT in token base units: digits only, no sign, no decimal
|
|
179
|
+
* point, no exponent, no `0x`.
|
|
180
|
+
*
|
|
181
|
+
* A plain `z.string()` would admit `""`, `"0.5"`, `"1.2e3"` and `"-5"`. Every
|
|
182
|
+
* consumer immediately does `BigInt()` on these to reconcile the layers against
|
|
183
|
+
* `total_fee`, and `BigInt` throws on three of those four and silently returns
|
|
184
|
+
* `0n` on the empty string -- so the loose type turns an upstream formatting
|
|
185
|
+
* change into a 500 on every quote for the route (nothing wraps the
|
|
186
|
+
* reconciliation in a try/catch). The producer (Riddler `fee_policy.ex`,
|
|
187
|
+
* `calculate_layered_fee`) emits `Integer.to_string` of a non-negative integer
|
|
188
|
+
* for all five, so this narrowing describes what is actually served rather
|
|
189
|
+
* than restricting it.
|
|
190
|
+
*
|
|
191
|
+
* Deliberately NOT applied to `gas_floor.usd` or `total_usd`: those are dollar
|
|
192
|
+
* figures and are legitimately decimal ("0.022663").
|
|
193
|
+
*/
|
|
194
|
+
const feeLayerBaseUnits = zod_1.z
|
|
195
|
+
.string()
|
|
196
|
+
.regex(/^\d+$/)
|
|
197
|
+
.describe("Fee layer amount in token base units (integer, no decimal point). " +
|
|
198
|
+
"Denominated in the token named by the response's `fee_denomination`.");
|
|
199
|
+
/**
|
|
200
|
+
* Itemized fee breakdown (economics.md Part 1). snake_case wire shape the worker
|
|
201
|
+
* emits and the frontend renders against. Layers are three additive bps lines on
|
|
202
|
+
* a never-discounted cost floor (solver + gas), plus venue and routing.
|
|
203
|
+
*
|
|
204
|
+
* The layer AMOUNTS carry no denomination of their own. The token they are
|
|
205
|
+
* measured in is named by `fee_denomination` on the enclosing quote response.
|
|
206
|
+
*/
|
|
207
|
+
exports.FeeBreakdownSchema = zod_1.z.object({
|
|
208
|
+
// The SOLVER's view of the caller's tier, under which IT computed the layer
|
|
209
|
+
// amounts below. NOT the authority on the caller's tier: Xochi resolves that
|
|
210
|
+
// from D1 (`user_tiers`) and the client-supplied `trust_score` is never
|
|
211
|
+
// trusted. The two can legitimately disagree for a quote or two after an
|
|
212
|
+
// upgrade, because the solver's copy of the score lags D1's. Render the
|
|
213
|
+
// caller's tier from the Xochi-side value; read this one only as the label
|
|
214
|
+
// that explains these particular numbers.
|
|
215
|
+
tier: zod_1.z
|
|
216
|
+
.enum(["standard", "trusted", "verified", "premium", "institutional"])
|
|
217
|
+
.describe("The SOLVER's view of the tier, and the schedule it priced these layer " +
|
|
218
|
+
"amounts under. Not the authority on the caller's tier -- Xochi resolves " +
|
|
219
|
+
"that from its own records, and the solver's copy of the trust score can " +
|
|
220
|
+
"lag a fresh upgrade by a quote or two. Use it to explain these numbers, " +
|
|
221
|
+
"never to display the user's tier."),
|
|
222
|
+
asset_class: zod_1.z.enum(["stable", "volatile"]),
|
|
223
|
+
gas_floor: zod_1.z.object({ amount: zod_1.z.string(), usd: zod_1.z.string() }),
|
|
224
|
+
solver_fee_bps: zod_1.z.number(),
|
|
225
|
+
solver_fee: feeLayerBaseUnits,
|
|
226
|
+
price_impact_bps: zod_1.z.number(),
|
|
227
|
+
price_impact: feeLayerBaseUnits,
|
|
228
|
+
venue_fee_bps: zod_1.z.number(),
|
|
229
|
+
venue_fee: feeLayerBaseUnits,
|
|
230
|
+
routing_fee_bps: zod_1.z.number(),
|
|
231
|
+
routing_fee: feeLayerBaseUnits,
|
|
232
|
+
total_bps: zod_1.z.number(),
|
|
233
|
+
total_fee: feeLayerBaseUnits,
|
|
234
|
+
total_usd: zod_1.z.string(),
|
|
235
|
+
surplus_share_pct: zod_1.z.number(),
|
|
236
|
+
// Present when a promo code was in play on the quote (xochi#584): which
|
|
237
|
+
// discount priced it and how much it took off the Standard venue + routing
|
|
238
|
+
// layers. Emitted by the solver, which priced it; the worker reads `source`
|
|
239
|
+
// to decide whether the code was applied rather than assuming it was.
|
|
240
|
+
discount: zod_1.z
|
|
241
|
+
.object({
|
|
242
|
+
source: zod_1.z
|
|
243
|
+
.enum(["tier", "promo"])
|
|
244
|
+
.describe("Which discount priced this quote: the trust tier's or the promo code's. They never stack: discount = max(tier, min(promo, Institutional discount))."),
|
|
245
|
+
bps: zod_1.z
|
|
246
|
+
.number()
|
|
247
|
+
.int()
|
|
248
|
+
.nonnegative()
|
|
249
|
+
.describe("Basis points taken off the Standard venue + routing layers for this asset class. Never off the solver spread or the gas floor."),
|
|
250
|
+
amount: feeLayerBaseUnits,
|
|
251
|
+
})
|
|
252
|
+
.strict()
|
|
253
|
+
.optional()
|
|
254
|
+
.describe("The applied discount as a labelled line INSIDE `total_fee`: `venue_fee` and `routing_fee` are already net of it, so it is never subtracted again. Render it as e.g. `Promo LAUNCH10 -0.05%`. Absent when no promo code was sent."),
|
|
255
|
+
});
|
|
256
|
+
/**
|
|
257
|
+
* Which token the `fee_breakdown` layer amounts are measured in. See the
|
|
258
|
+
* `fee_denomination` field on {@link QuoteResponseSchema}.
|
|
259
|
+
*/
|
|
260
|
+
exports.FeeDenominationSchema = zod_1.z.enum(["origin", "destination"]);
|
|
261
|
+
/**
|
|
262
|
+
* Canonical quote response: the Xochi worker's `/api/intent/quote` output and
|
|
263
|
+
* the pinned agent contract. snake_case envelope; the `eip712` value is
|
|
264
|
+
* verbatim EIP-712 typed data. When `can_solve` is true, `to_amount`,
|
|
265
|
+
* `payment_method`, and `eip712` are present. Mirrors `QuoteResponse` in
|
|
266
|
+
* public/openapi.yaml -- keep all three in lockstep.
|
|
267
|
+
*/
|
|
268
|
+
exports.QuoteResponseSchema = zod_1.z
|
|
269
|
+
.object({
|
|
270
|
+
success: zod_1.z.boolean(),
|
|
271
|
+
intent_id: zod_1.z.string(),
|
|
272
|
+
quote_id: zod_1.z.string(),
|
|
273
|
+
can_solve: zod_1.z.boolean(),
|
|
274
|
+
/** Non-executable price exploration: never persisted and carries no signing material. */
|
|
275
|
+
preview: zod_1.z.boolean().optional(),
|
|
276
|
+
to_amount: zod_1.z.string().optional(),
|
|
277
|
+
min_to_amount: zod_1.z.string().optional(),
|
|
278
|
+
xochi_fee: zod_1.z.string().optional(),
|
|
279
|
+
xochi_fee_rate: zod_1.z.string().optional(),
|
|
280
|
+
cost_bps: zod_1.z
|
|
281
|
+
.number()
|
|
282
|
+
.nonnegative()
|
|
283
|
+
.optional()
|
|
284
|
+
.describe("All-in cost of this quote in basis points, from the same fixed-cost " +
|
|
285
|
+
"guard that refuses an uneconomical order, so the displayed price and " +
|
|
286
|
+
"the refusal threshold cannot disagree. Unlike xochi_fee_rate (a flat " +
|
|
287
|
+
"schedule rate) this moves with route and size: cost is " +
|
|
288
|
+
"max(gas floor of the destination chain, the fee rate), so it rises " +
|
|
289
|
+
"as the order shrinks. BOUNDED BY THE GUARD: an order whose cost " +
|
|
290
|
+
"exceeds the ceiling is declined with error_category " +
|
|
291
|
+
"'amount_too_small' and carries no cost_bps at all, so every served " +
|
|
292
|
+
"value sits at or below the ceiling (150 bps at MAX_COST_RATIO 0.015). " +
|
|
293
|
+
"Do not model a range above it. Omitted when the origin token has no " +
|
|
294
|
+
"dollar value on the worker and the guard could not run -- absent " +
|
|
295
|
+
"means unknown, never zero. Unrounded: below half a basis point the " +
|
|
296
|
+
"distinction between a free fill and a merely cheap one still matters, " +
|
|
297
|
+
"and rounding server-side destroys it."),
|
|
298
|
+
// Itemized layer split; optional until the worker/Riddler emit it.
|
|
299
|
+
fee_breakdown: exports.FeeBreakdownSchema.optional(),
|
|
300
|
+
// Which token `fee_breakdown`'s amounts are measured in. Carried on the wire
|
|
301
|
+
// rather than inferred from the MERE PRESENCE of fee_breakdown, which would
|
|
302
|
+
// rest on an undocumented convention in another repo (Riddler's
|
|
303
|
+
// fee_policy.ex computes every layer off `input_amount`): the xochi#410
|
|
304
|
+
// failure class, a cross-repo fact with no runtime evidence in this one.
|
|
305
|
+
fee_denomination: exports.FeeDenominationSchema.optional().describe("Which token the `fee_breakdown` layer AMOUNTS are denominated in. " +
|
|
306
|
+
"'origin' means `from_token` base units, 'destination' means `to_token` " +
|
|
307
|
+
"base units. Formatting a fee with the wrong token's decimals and symbol " +
|
|
308
|
+
"misstates it by the exchange rate, and on a USDC->USDC route (6 decimals " +
|
|
309
|
+
"both sides) the mistake is invisible -- which is why this is stated on " +
|
|
310
|
+
"the wire instead of inferred from the presence of `fee_breakdown`. " +
|
|
311
|
+
"PROVENANCE: the solver does not emit this field yet, so today Xochi " +
|
|
312
|
+
"asserts it from the solver's documented convention (every layer computed " +
|
|
313
|
+
"off the input amount, hence 'origin') rather than reporting a value the " +
|
|
314
|
+
"solver stated. It is forwarded verbatim once the solver does send one. " +
|
|
315
|
+
"Treat it as Xochi's best statement of the denomination, not as the fee " +
|
|
316
|
+
"computer's own declaration. ABSENT MEANS UNKNOWN, never a default: when " +
|
|
317
|
+
"there is no `fee_breakdown` the field is omitted entirely."),
|
|
318
|
+
// Referral attribution for the routing layer (xochi#583). Xochi-owned and
|
|
319
|
+
// a sibling of `fee_breakdown` rather than inside it: the breakdown is the
|
|
320
|
+
// solver's itemization and may be absent, while the routing line exists on
|
|
321
|
+
// every quote (the client derives one without a breakdown).
|
|
322
|
+
referral: exports.QuoteReferralSchema.optional(),
|
|
323
|
+
referral_note: zod_1.z
|
|
324
|
+
.literal(exports.REFERRAL_NOT_APPLIED_NOTE)
|
|
325
|
+
.optional()
|
|
326
|
+
.describe("Present when a `ref` was sent and not applied. Deliberately one fixed text for every reason (unknown, expired, revoked, not valid for this key, a key that does not accept referrals, self-referral), so the response does not reveal whether a code exists. The quote is otherwise identical to one sent without `ref`."),
|
|
327
|
+
// Promo code outcome (xochi#584). Xochi-owned siblings of `fee_breakdown`:
|
|
328
|
+
// the breakdown itemizes the discount (`fee_breakdown.discount`), these say
|
|
329
|
+
// whether the code the caller sent is the thing that priced it.
|
|
330
|
+
promo: exports.QuotePromoSchema.optional(),
|
|
331
|
+
promo_note: zod_1.z
|
|
332
|
+
.enum([exports.PROMO_NOT_APPLIED_NOTE, exports.PROMO_TIER_BETTER_NOTE])
|
|
333
|
+
.optional()
|
|
334
|
+
.describe("Inline message for a `promo` that did not price this quote. The quote is still served, priced as if no code had been sent. `" +
|
|
335
|
+
exports.PROMO_NOT_APPLIED_NOTE +
|
|
336
|
+
"` is deliberately one fixed text for every reason (unknown, expired, revoked, used up, this wallet's limit reached, not accepted under this publishable key), so the response does not reveal whether a code exists. `" +
|
|
337
|
+
exports.PROMO_TIER_BETTER_NOTE +
|
|
338
|
+
"` means the code is valid but the trust-tier discount is at least as large; it is not consumed."),
|
|
339
|
+
estimated_gas_cost: zod_1.z.string().optional(),
|
|
340
|
+
stream_topic: zod_1.z
|
|
341
|
+
.string()
|
|
342
|
+
.regex(/^[A-Za-z0-9_-]{16,64}$/)
|
|
343
|
+
.optional()
|
|
344
|
+
.describe("Subscribe capability for this intent's live status and fill-race " +
|
|
345
|
+
"stream, at `GET /api/stream/<stream_topic>` (WebSocket upgrade). " +
|
|
346
|
+
"TREAT IT AS A BEARER TOKEN: possession is the whole authorization, " +
|
|
347
|
+
"there is no second check, and the relay replays its recent event " +
|
|
348
|
+
"buffer to every new subscriber. It is served exactly once, in this " +
|
|
349
|
+
"response, and is never returned by a read endpoint. Keep it out of " +
|
|
350
|
+
"logs and out of URLs you share. It is deliberately NOT the " +
|
|
351
|
+
"`intent_id` beside it: that value is public by construction (it " +
|
|
352
|
+
"appears in status paths and in server logs), so a stream keyed on it " +
|
|
353
|
+
"would be readable by anyone who has ever seen one. Absent on an " +
|
|
354
|
+
"intent quoted before this field existed, which means no stream is " +
|
|
355
|
+
"available; fall back to polling `GET /api/intent/:id/status`, which " +
|
|
356
|
+
"is the durable path in every case."),
|
|
357
|
+
expiry: zod_1.z.string(),
|
|
358
|
+
gasless: zod_1.z.boolean().optional(),
|
|
359
|
+
gasless_fee: zod_1.z.string().optional(),
|
|
360
|
+
payment_method: exports.paymentMethod.optional(),
|
|
361
|
+
route: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
362
|
+
eip712: exports.Eip712TypedDataSchema.optional(),
|
|
363
|
+
// Origin-pull authorization: a second EIP-712 the user signs so the solver
|
|
364
|
+
// can collect origin funds before settling. ReceiveWithAuthorization (USDC
|
|
365
|
+
// domain) for erc3009, PermitWitnessTransferFrom for permit2. Absent for
|
|
366
|
+
// non-pulling methods and the mock path.
|
|
367
|
+
pull_authorization: exports.Eip712TypedDataSchema.optional(),
|
|
368
|
+
// Non-EVM origin (Tron): the per-intent address the user manually sends TRC-20
|
|
369
|
+
// to. Mutually exclusive with pull_authorization (a deposit route has no
|
|
370
|
+
// typed-data pull). Base58check -- never lowercased.
|
|
371
|
+
deposit_address: zod_1.z.string().optional(),
|
|
372
|
+
// Solver attestation binding the deposit_address to this intent. The client
|
|
373
|
+
// verifies the signature against a pinned signer BEFORE displaying the address
|
|
374
|
+
// (fail-closed); an absent/invalid attestation makes the route unavailable.
|
|
375
|
+
deposit_attestation: zod_1.z.string().optional(),
|
|
376
|
+
// Deposit-acceptance deadline (unix seconds) for a deposit route -- decoupled
|
|
377
|
+
// from the shorter quote `expiry` so a manual TRC-20 transfer has time to land.
|
|
378
|
+
// Drives the deposit countdown.
|
|
379
|
+
deposit_deadline: zod_1.z.number().int().optional(),
|
|
380
|
+
// Echo of the requested destination recipient (non-EVM/Tron routes). These
|
|
381
|
+
// routes carry no wallet signature, so recipient integrity rides on this
|
|
382
|
+
// authenticated channel: the client asserts the solvable quote echoes exactly
|
|
383
|
+
// the address it asked for and fail-closes otherwise (#113). Base58 is
|
|
384
|
+
// case-sensitive -- passed through verbatim, never lowercased.
|
|
385
|
+
recipient_address: exports.recipientAddress.optional(),
|
|
386
|
+
settlement_options: zod_1.z.array(exports.SettlementOptionSchema).optional(),
|
|
387
|
+
claim_info: exports.ClaimInfoSchema.optional(),
|
|
388
|
+
// Execute replay-dedup nonce the GET alias injects (absent from the POST
|
|
389
|
+
// envelope) so a third-party ACP agent need not do the pull-nonce bit math
|
|
390
|
+
// itself. Derived from the pull-authorization nonce (see deriveReplayNonce).
|
|
391
|
+
// Optional: the POST omits it, and the GET omits it too on a pull-less route.
|
|
392
|
+
nonce: zod_1.z.number().int().min(0).optional(),
|
|
393
|
+
error: zod_1.z.string().optional(),
|
|
394
|
+
// Present on the unsolvable-200 branch: `error_category` is a stable machine
|
|
395
|
+
// key for the reject class, `detail` the solver's raw reason. Absent on
|
|
396
|
+
// solvable quotes.
|
|
397
|
+
//
|
|
398
|
+
// Enumerated so the generated spec documents the vocabulary agents branch on,
|
|
399
|
+
// and so adding a category without declaring it fails the contract tests.
|
|
400
|
+
// Four are worker-side fail-closed guards; the rest come from
|
|
401
|
+
// `classifyDecline` (decline.ts), whose returns are a closed set of literals
|
|
402
|
+
// with `unknown` as the catch-all.
|
|
403
|
+
// Documented, deliberately NOT enumerated. The worker owns four of these,
|
|
404
|
+
// but the rest come from `classifyDecline` (decline.ts) over solver reasons,
|
|
405
|
+
// and the contract test below asserts an unlisted value still parses -- this
|
|
406
|
+
// side of the vocabulary is intentionally open, so a new upstream reason must
|
|
407
|
+
// not start failing every client that validates.
|
|
408
|
+
error_category: zod_1.z
|
|
409
|
+
.string()
|
|
410
|
+
.optional()
|
|
411
|
+
.describe("Stable machine key for a declined quote. Branch on this rather than on `error`, whose prose may change. Known values: gas_floor_unpriced (transient, retry), origin_pull_amount_mismatch, served_payout_mismatch, settlement_type_unavailable, stealth_factory_untrusted, refund_address_invalid, amount_too_large, amount_too_small, no_liquidity, route_unsupported, invalid_request (the request shape was rejected, not the route -- see `detail` for the offending field), auth, unknown. Treat an unrecognized value as a non-retryable decline."),
|
|
412
|
+
detail: zod_1.z.string().optional(),
|
|
413
|
+
// Present when `error_category` is `amount_too_small` because the route's
|
|
414
|
+
// FIXED settlement cost consumed too much of the order: the smallest order
|
|
415
|
+
// (USD) that clears the threshold at the current cost. Lets a caller retry
|
|
416
|
+
// with a workable amount instead of bisecting for one. Not a protocol
|
|
417
|
+
// constant -- it moves with the corridor's cost, so read it per quote.
|
|
418
|
+
minimum_amount_usd: zod_1.z
|
|
419
|
+
.number()
|
|
420
|
+
.positive()
|
|
421
|
+
.optional()
|
|
422
|
+
.describe("Smallest order in USD you should send on this route. THIS is the number to retry with; it already accounts for both the solver's floor and the fixed-cost threshold, so it is >= `min_fillable_usd`. Present on an `amount_too_small` decline. Varies with the corridor's current cost, so do not cache it."),
|
|
423
|
+
// The solver's own live bounds, passed through on a bounds decline. Both
|
|
424
|
+
// are served, so an undeclared one makes the strict schema reject a
|
|
425
|
+
// legitimate decline and leaves it out of the generated spec (#754).
|
|
426
|
+
min_fillable_usd: zod_1.z
|
|
427
|
+
.number()
|
|
428
|
+
.positive()
|
|
429
|
+
.optional()
|
|
430
|
+
.describe("The solver's live per-corridor floor: the smallest order it will fill at all, which is its own cost to fill. Reported for transparency. Do NOT retry with this figure -- an order at exactly the solver's cost delivers almost nothing. Use `minimum_amount_usd`."),
|
|
431
|
+
max_order_usd: zod_1.z
|
|
432
|
+
.number()
|
|
433
|
+
.positive()
|
|
434
|
+
.optional()
|
|
435
|
+
.describe("The solver's live per-trade ceiling in USD. Present on an `amount_too_large` decline."),
|
|
436
|
+
})
|
|
437
|
+
.strict();
|
|
438
|
+
/**
|
|
439
|
+
* Length of an intent read capability (`read_token` on the execute response,
|
|
440
|
+
* xochi#563): 32 random bytes as unpadded base64url.
|
|
441
|
+
*/
|
|
442
|
+
exports.READ_TOKEN_LENGTH = 43;
|
|
443
|
+
/** Shape of an intent read capability: exactly `READ_TOKEN_LENGTH` base64url characters. */
|
|
444
|
+
exports.READ_TOKEN_PATTERN = new RegExp(`^[A-Za-z0-9_-]{${exports.READ_TOKEN_LENGTH}}$`);
|
|
445
|
+
/**
|
|
446
|
+
* The request header that carries a `read_token` on `GET /api/intent/:id` and
|
|
447
|
+
* `GET /api/intent/:id/status`. A header and never a query parameter: the token
|
|
448
|
+
* must not land in URLs, logs or referrers.
|
|
449
|
+
*/
|
|
450
|
+
exports.READ_TOKEN_HEADER = "X-Xochi-Read-Token";
|
|
451
|
+
/** Mirrors `ExecuteResponse` in public/openapi.yaml. */
|
|
452
|
+
exports.ExecuteResponseSchema = zod_1.z
|
|
453
|
+
.object({
|
|
454
|
+
success: zod_1.z.boolean(),
|
|
455
|
+
intent_id: zod_1.z.string(),
|
|
456
|
+
status: zod_1.z.string(),
|
|
457
|
+
tx_hash: zod_1.z.string().optional(),
|
|
458
|
+
// Per-leg hashes for cross-chain: the origin/deposit leg is `tx_hash`, the
|
|
459
|
+
// destination fill is `receiving_tx_hash` (arrives later via status/WS).
|
|
460
|
+
bridge_tx_hash: zod_1.z.string().optional(),
|
|
461
|
+
receiving_tx_hash: zod_1.z.string().optional(),
|
|
462
|
+
// Public destination fill receipt (Riddler #632) + its chain, when a same-chain
|
|
463
|
+
// or fast fill resolves inline; usually arrives later via the status poll.
|
|
464
|
+
settlement_tx_hash: zod_1.z.string().optional(),
|
|
465
|
+
settlement_chain_id: zod_1.z.number().int().optional(),
|
|
466
|
+
// In-doubt: the solver may have moved funds but did not confirm. The intent
|
|
467
|
+
// stays non-terminal; surface this so the client shows "still confirming"
|
|
468
|
+
// rather than a success or a failure.
|
|
469
|
+
reconciling: zod_1.z.boolean().optional(),
|
|
470
|
+
note_commitment: zod_1.z.string().optional(),
|
|
471
|
+
stealth_address: zod_1.z.string().optional(),
|
|
472
|
+
ephemeral_pub_key: zod_1.z.string().optional(),
|
|
473
|
+
view_tag: zod_1.z.number().int().optional(),
|
|
474
|
+
error: zod_1.z.string().optional(),
|
|
475
|
+
substatus: zod_1.z
|
|
476
|
+
.literal(intents_js_1.NOT_EXECUTED_SUBSTATUS)
|
|
477
|
+
.optional()
|
|
478
|
+
.describe("`not_executed` on a `failed` where nothing moved, served by the execute 502 of the worker's own finishes (the resend budget's, a not-accepted refusal whose un-claim's write failed, no solver configured, a solver refusal carrying `details.funds_moved: false`) and echoed from the row by the lost-race 502 and the in-doubt 200: safe to re-quote. Absent on any other failure. Every source: docs/contracts/xochi-intent-api.md, \"The worker's not_executed marker\"."),
|
|
479
|
+
// Per-intent read capability (xochi#563): 32 random bytes, base64url, no
|
|
480
|
+
// padding. Served once, by the execute that admits the intent; present it as
|
|
481
|
+
// `X-Xochi-Read-Token` on `GET /api/intent/:id[/status]` to read the owner's
|
|
482
|
+
// view of a stealth intent; a shielded intent's token read gets the
|
|
483
|
+
// status-only view (SPEC.md §5.1). Older servers omit it. The one declared
|
|
484
|
+
// field that never fails the body: a malformed token is dropped, because this
|
|
485
|
+
// body reports an execute that may already have moved funds, and failing it
|
|
486
|
+
// would turn a settled swap into an in-doubt one over a privacy-only field.
|
|
487
|
+
// The describe is xochi's (xochi#565, `0317e84`) verbatim.
|
|
488
|
+
read_token: zod_1.z
|
|
489
|
+
.string()
|
|
490
|
+
.regex(exports.READ_TOKEN_PATTERN)
|
|
491
|
+
.optional()
|
|
492
|
+
.catch(undefined)
|
|
493
|
+
.describe("Per-intent read capability (xochi#563): 32 random bytes, base64url without padding. Returned to the request that claimed the intent for execution, on every answer where funds may have moved: the 200, the 502 of an unmarked solver refusal or failure this request wrote, a 502 whose status another writer decided, and the outcome unknown after the claim. Never on a `not_executed` answer, an un-claim back to `quoted` (the not-accepted 503, the in-doubt 200 with `status: \"quoted\"`) or a retried execute. Store it with the intent id and send it as the `X-Xochi-Read-Token` header on `GET /api/intent/{id}` and `/status` to get the owner's full view of a stealth intent without a wallet session. On a shielded intent it reads only the status-only view (legs_withheld), like an anonymous read; only a SIWE session for the intent's wallet reads that full record. A bearer secret: never log it or put it in a URL. The server stores only its SHA-256, so it cannot be re-issued; signing in with the intent's wallet (SIWE) reads the same view."),
|
|
494
|
+
})
|
|
495
|
+
.strict();
|
|
496
|
+
/**
|
|
497
|
+
* Automatic resends of one execute after its not-accepted answer. Mirrors
|
|
498
|
+
* `MAX_EXECUTE_RESENDS` in xochi's `@xochi/shared` `schemas.ts`, the budget
|
|
499
|
+
* the Worker enforces too: the refusal that follows this many un-claims
|
|
500
|
+
* finishes the intent `failed` instead.
|
|
501
|
+
*/
|
|
502
|
+
exports.MAX_EXECUTE_RESENDS = 3;
|
|
503
|
+
/**
|
|
504
|
+
* A resend must reach the Worker at least this many seconds before the
|
|
505
|
+
* earliest deadline the signed request carries. Mirrors
|
|
506
|
+
* `EXECUTE_RESEND_MARGIN_SECS` in xochi's `@xochi/shared` `schemas.ts`, the
|
|
507
|
+
* margin the Worker applies before it un-claims.
|
|
508
|
+
*/
|
|
509
|
+
exports.EXECUTE_RESEND_MARGIN_SECS = 5;
|
|
510
|
+
/**
|
|
511
|
+
* Seconds past an unexecuted intent's signed deadline before the Worker ends
|
|
512
|
+
* it: a `quoted` market row (never claimed, or every claim undone) that an
|
|
513
|
+
* intent read (`GET /api/intent/:id` or the status poll, never the stream)
|
|
514
|
+
* finds this far past the `deadline` the user signed is finished `failed`
|
|
515
|
+
* with `NOT_EXECUTED_SUBSTATUS`, and the read serves that. Slack for clock
|
|
516
|
+
* skew between the Worker, the database (whose clock the claim's deadline
|
|
517
|
+
* check uses), the solver and the client. Measured from the signed deadline;
|
|
518
|
+
* the quote sweep's own 60 s is measured from the Worker's 30 s quote expiry
|
|
519
|
+
* and usually ends a touched row first (README "Fund safety"). Mirrors
|
|
520
|
+
* `QUOTED_EXPIRY_GRACE_SECS` in xochi's `@xochi/shared` `schemas.ts`. The
|
|
521
|
+
* machine polls an execute at least this long past the quote's deadline
|
|
522
|
+
* (`unexecutedFinishAt`).
|
|
523
|
+
*/
|
|
524
|
+
exports.QUOTED_EXPIRY_GRACE_SECS = 60;
|