@xochi/client 0.0.0-stage → 0.1.1
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 +54 -0
- package/LICENSE +21 -0
- package/README.md +119 -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 +409 -0
- package/dist/cjs/machine/machine.js +1615 -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 +15 -0
- package/dist/cjs/shared/index.js +31 -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/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 +409 -0
- package/dist/esm/machine/machine.js +1609 -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 +15 -0
- package/dist/esm/shared/index.js +15 -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/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,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire shapes and decoders.
|
|
3
|
+
*
|
|
4
|
+
* The worker speaks the canonical snake_case contract (`QuoteResponseSchema` in
|
|
5
|
+
* `@xochi/shared`, `public/openapi.yaml`); everything past this module is
|
|
6
|
+
* camelCase. Decoding happens HERE, once, so the trade widget and `@xochi/sdk`
|
|
7
|
+
* read a quote, an execute result and an intent status through the same code
|
|
8
|
+
* rather than through two mappers that drift.
|
|
9
|
+
*/
|
|
10
|
+
import { ExecuteResponseSchema, QuoteResponseSchema, READ_TOKEN_PATTERN, isSettlementType, isIntentStatus, } from "../shared/index.js";
|
|
11
|
+
// ============================================================
|
|
12
|
+
// Wire adapters: snake_case wire (worker/openapi) -> camelCase app types
|
|
13
|
+
//
|
|
14
|
+
// The worker emits the canonical snake_case contract (see QuoteResponseSchema
|
|
15
|
+
// in @xochi/shared and public/openapi.yaml). The React app stays camelCase;
|
|
16
|
+
// intentAPI.ts maps at the client boundary via these helpers. The `eip712`
|
|
17
|
+
// value is verbatim EIP-712 typed data -- its internal keys are already the
|
|
18
|
+
// standard camelCase and pass through unchanged.
|
|
19
|
+
// ============================================================
|
|
20
|
+
/**
|
|
21
|
+
* The response contracts as the client applies them: every declared field is
|
|
22
|
+
* held to its schema (a `fee_breakdown` without `gas_floor`, a non-string
|
|
23
|
+
* `eip712`, a numeric `claim_info.factory_address` all fail), but an
|
|
24
|
+
* undeclared top-level key is let through rather than failing the whole
|
|
25
|
+
* quote, so an additive server field does not take every client down. The
|
|
26
|
+
* decoders read the original object, never the parse output, so the EIP-712
|
|
27
|
+
* payloads reach the wallet byte-for-byte as served.
|
|
28
|
+
*/
|
|
29
|
+
const QUOTE_RESPONSE = QuoteResponseSchema.passthrough();
|
|
30
|
+
const EXECUTE_RESPONSE = ExecuteResponseSchema.passthrough();
|
|
31
|
+
export function isQuoteResponseWire(value) {
|
|
32
|
+
return QUOTE_RESPONSE.safeParse(value).success;
|
|
33
|
+
}
|
|
34
|
+
export function isExecuteResponseWire(value) {
|
|
35
|
+
return EXECUTE_RESPONSE.safeParse(value).success;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read the resolved settlement type out of the solver's opaque `route` bag.
|
|
39
|
+
*
|
|
40
|
+
* `route` is `z.record(z.string(), z.unknown())` in the contract: a passthrough
|
|
41
|
+
* the worker does not model, so it arrives untyped. Narrow it to the closed
|
|
42
|
+
* settlement vocabulary here rather than casting, and return undefined for
|
|
43
|
+
* anything unrecognised. Callers that need it (the v2 deposit attestation) must
|
|
44
|
+
* treat undefined as a refusal: a wrong guess produces a message that fails to
|
|
45
|
+
* recover, which reads as a solver outage rather than as our bug.
|
|
46
|
+
*/
|
|
47
|
+
export function readResolvedSettlementType(route) {
|
|
48
|
+
const value = route?.settlement_type;
|
|
49
|
+
// Reuses the shared predicate rather than re-listing the vocabulary. A second
|
|
50
|
+
// copy of the closed set compiles fine and silently refuses every deposit
|
|
51
|
+
// quote on whichever settlement type it forgot.
|
|
52
|
+
return typeof value === "string" && isSettlementType(value) ? value : undefined;
|
|
53
|
+
}
|
|
54
|
+
export function mapQuoteResponse(w) {
|
|
55
|
+
return {
|
|
56
|
+
success: w.success,
|
|
57
|
+
intentId: w.intent_id,
|
|
58
|
+
quoteId: w.quote_id,
|
|
59
|
+
canSolve: w.can_solve,
|
|
60
|
+
preview: w.preview,
|
|
61
|
+
toAmount: w.to_amount ?? "0",
|
|
62
|
+
minToAmount: w.min_to_amount ?? "0",
|
|
63
|
+
xochiFee: w.xochi_fee ?? "0",
|
|
64
|
+
xochiFeeRate: w.xochi_fee_rate ?? "",
|
|
65
|
+
costBps: w.cost_bps,
|
|
66
|
+
feeBreakdown: w.fee_breakdown
|
|
67
|
+
? {
|
|
68
|
+
tier: w.fee_breakdown.tier,
|
|
69
|
+
assetClass: w.fee_breakdown.asset_class,
|
|
70
|
+
gasFloor: {
|
|
71
|
+
amount: w.fee_breakdown.gas_floor.amount,
|
|
72
|
+
usd: w.fee_breakdown.gas_floor.usd,
|
|
73
|
+
},
|
|
74
|
+
solverFeeBps: w.fee_breakdown.solver_fee_bps,
|
|
75
|
+
solverFee: w.fee_breakdown.solver_fee,
|
|
76
|
+
priceImpactBps: w.fee_breakdown.price_impact_bps,
|
|
77
|
+
priceImpact: w.fee_breakdown.price_impact,
|
|
78
|
+
venueFeeBps: w.fee_breakdown.venue_fee_bps,
|
|
79
|
+
venueFee: w.fee_breakdown.venue_fee,
|
|
80
|
+
routingFeeBps: w.fee_breakdown.routing_fee_bps,
|
|
81
|
+
routingFee: w.fee_breakdown.routing_fee,
|
|
82
|
+
totalBps: w.fee_breakdown.total_bps,
|
|
83
|
+
totalFee: w.fee_breakdown.total_fee,
|
|
84
|
+
totalUsd: w.fee_breakdown.total_usd,
|
|
85
|
+
surplusSharePct: w.fee_breakdown.surplus_share_pct,
|
|
86
|
+
}
|
|
87
|
+
: undefined,
|
|
88
|
+
// Passed through, never defaulted. An absent field means the server did not
|
|
89
|
+
// state the denomination, which the renderer has to be able to tell apart
|
|
90
|
+
// from a server that said "origin".
|
|
91
|
+
feeDenomination: w.fee_denomination,
|
|
92
|
+
estimatedGasCost: w.estimated_gas_cost ?? "",
|
|
93
|
+
// Passed through, never defaulted and never backfilled from intent_id.
|
|
94
|
+
streamTopic: w.stream_topic,
|
|
95
|
+
expiry: w.expiry,
|
|
96
|
+
gasless: w.gasless ?? false,
|
|
97
|
+
gaslessFee: w.gasless_fee,
|
|
98
|
+
paymentMethod: w.payment_method,
|
|
99
|
+
eip712Data: w.eip712,
|
|
100
|
+
pullAuthorization: w.pull_authorization,
|
|
101
|
+
depositAddress: w.deposit_address,
|
|
102
|
+
depositAttestation: w.deposit_attestation,
|
|
103
|
+
depositDeadline: w.deposit_deadline,
|
|
104
|
+
settlementType: readResolvedSettlementType(w.route),
|
|
105
|
+
recipientAddress: w.recipient_address,
|
|
106
|
+
settlementOptions: w.settlement_options ?? [],
|
|
107
|
+
claimInfo: w.claim_info
|
|
108
|
+
? { factoryAddress: w.claim_info.factory_address, salt: w.claim_info.salt }
|
|
109
|
+
: undefined,
|
|
110
|
+
error: w.error,
|
|
111
|
+
errorCategory: w.error_category,
|
|
112
|
+
minimumAmountUsd: w.minimum_amount_usd,
|
|
113
|
+
minFillableUsd: w.min_fillable_usd,
|
|
114
|
+
// Passed through untouched, never interpreted: the signer refuses it.
|
|
115
|
+
...("escrow" in w ? { escrow: w.escrow } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export function mapExecuteResponse(w) {
|
|
119
|
+
return {
|
|
120
|
+
success: w.success,
|
|
121
|
+
intentId: w.intent_id,
|
|
122
|
+
status: w.status,
|
|
123
|
+
txHash: w.tx_hash,
|
|
124
|
+
receivingTxHash: w.receiving_tx_hash,
|
|
125
|
+
settlementTxHash: w.settlement_tx_hash,
|
|
126
|
+
settlementChainId: w.settlement_chain_id,
|
|
127
|
+
bridgeTxHash: w.bridge_tx_hash,
|
|
128
|
+
reconciling: w.reconciling,
|
|
129
|
+
error: w.error,
|
|
130
|
+
...(typeof w.substatus === "string" ? { substatus: w.substatus } : {}),
|
|
131
|
+
noteCommitment: w.note_commitment,
|
|
132
|
+
stealthAddress: w.stealth_address,
|
|
133
|
+
ephemeralPubKey: w.ephemeral_pub_key,
|
|
134
|
+
viewTag: w.view_tag,
|
|
135
|
+
// The decoders read the raw body, not the parse output, so the schema's
|
|
136
|
+
// drop of a malformed token does not reach here: check it again.
|
|
137
|
+
readToken: typeof w.read_token === "string" && READ_TOKEN_PATTERN.test(w.read_token)
|
|
138
|
+
? w.read_token
|
|
139
|
+
: undefined,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
// ============================================================
|
|
143
|
+
// Race Stream Messages
|
|
144
|
+
// ============================================================
|
|
145
|
+
export const RACE_MESSAGE_TYPES = ["race_started", "solver_bid", "race_won"];
|
|
146
|
+
export function isRaceWebSocketMessage(value) {
|
|
147
|
+
if (typeof value !== "object" || value === null)
|
|
148
|
+
return false;
|
|
149
|
+
const obj = value;
|
|
150
|
+
return (typeof obj.type === "string" &&
|
|
151
|
+
RACE_MESSAGE_TYPES.includes(obj.type) &&
|
|
152
|
+
typeof obj.intentId === "string");
|
|
153
|
+
}
|
|
154
|
+
// ============================================================
|
|
155
|
+
// Intent status (GET /api/intent/:id/status)
|
|
156
|
+
// ============================================================
|
|
157
|
+
/**
|
|
158
|
+
* Granular origin-pull states the solver may emit that the `IntentStatus`
|
|
159
|
+
* union doesn't model, bucketed into the nearest known state. Unknown/granular
|
|
160
|
+
* strings otherwise reach `phaseFromStatus` (which drops them to the confirm
|
|
161
|
+
* screen) and fall outside the poll's in-flight set (freezing the row mid-flight
|
|
162
|
+
* -- the dead-spinner bug).
|
|
163
|
+
*/
|
|
164
|
+
export const GRANULAR_WIRE_STATUS = {
|
|
165
|
+
pulling: "executing",
|
|
166
|
+
pulled: "executing",
|
|
167
|
+
filling: "settling",
|
|
168
|
+
filled: "completed",
|
|
169
|
+
refunding: "settling",
|
|
170
|
+
pull_failed: "failed",
|
|
171
|
+
fill_failed: "failed",
|
|
172
|
+
refund_failed: "failed",
|
|
173
|
+
};
|
|
174
|
+
export function decodeWireStatus(raw) {
|
|
175
|
+
if (typeof raw !== "string")
|
|
176
|
+
return { kind: "absent", status: "executing" };
|
|
177
|
+
if (isIntentStatus(raw))
|
|
178
|
+
return { kind: "mapped", status: raw };
|
|
179
|
+
const bucket = GRANULAR_WIRE_STATUS[raw];
|
|
180
|
+
if (bucket)
|
|
181
|
+
return { kind: "mapped", status: bucket };
|
|
182
|
+
return { kind: "unmapped", status: "executing", raw };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Statuses that are settled: no further status polling is needed. Xochi's own
|
|
186
|
+
* lifecycle vocabulary (`IntentStatus`), and the fallback the status decoder
|
|
187
|
+
* uses when the worker omits `terminal`.
|
|
188
|
+
*/
|
|
189
|
+
export const TERMINAL_INTENT_STATUSES = {
|
|
190
|
+
completed: true,
|
|
191
|
+
failed: true,
|
|
192
|
+
expired: true,
|
|
193
|
+
refunded: true,
|
|
194
|
+
};
|
|
195
|
+
export function isTerminalIntentStatus(status) {
|
|
196
|
+
return TERMINAL_INTENT_STATUSES[status] === true;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Whether an anonymous read's `settlement_confirmed` (xochi#563) counts as a
|
|
200
|
+
* delivered fill. Only on a read that withheld the legs (the marker stands in
|
|
201
|
+
* for the withheld receipt), and only when `status` decodes (`decodeWireStatus`)
|
|
202
|
+
* to a status this build models that is not a terminal failure and not part of
|
|
203
|
+
* a refund: never on `refunded` / `failed` / `expired`, on a granular status
|
|
204
|
+
* bucketed to one (`fill_failed`, `refund_failed`), on any `refund*` status
|
|
205
|
+
* (`refunding`, `refund_pending`), or on an unmapped or absent status.
|
|
206
|
+
*/
|
|
207
|
+
export function withheldSettlementConfirmed(read) {
|
|
208
|
+
if (read.legsWithheld !== true || read.settlementConfirmed !== true)
|
|
209
|
+
return false;
|
|
210
|
+
const decoded = decodeWireStatus(read.status);
|
|
211
|
+
if (decoded.kind !== "mapped")
|
|
212
|
+
return false;
|
|
213
|
+
if (typeof read.status === "string" && read.status.startsWith("refund"))
|
|
214
|
+
return false;
|
|
215
|
+
return !(isTerminalIntentStatus(decoded.status) && decoded.status !== "completed");
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* One `GET /api/intent/:id` body: the status-only `WithheldIntentRecord` when
|
|
219
|
+
* the server says it withheld the legs, else the full record.
|
|
220
|
+
*/
|
|
221
|
+
export function mapIntentRead(w) {
|
|
222
|
+
if (w.legs_withheld !== true)
|
|
223
|
+
return mapIntentRecord(w);
|
|
224
|
+
const decoded = decodeWireStatus(w.status);
|
|
225
|
+
return {
|
|
226
|
+
legsWithheld: true,
|
|
227
|
+
id: w.id,
|
|
228
|
+
status: decoded.status,
|
|
229
|
+
...(decoded.kind === "unmapped" ? { unmapped: true } : {}),
|
|
230
|
+
fromChainId: w.from_chain_id,
|
|
231
|
+
toChainId: w.to_chain_id,
|
|
232
|
+
settlementType: w.settlement_type,
|
|
233
|
+
error: w.error,
|
|
234
|
+
...(typeof w.substatus === "string" ? { substatus: w.substatus } : {}),
|
|
235
|
+
settlementConfirmed: withheldSettlementConfirmed({
|
|
236
|
+
status: w.status,
|
|
237
|
+
legsWithheld: w.legs_withheld,
|
|
238
|
+
settlementConfirmed: w.settlement_confirmed,
|
|
239
|
+
}),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
export function mapIntentRecord(w) {
|
|
243
|
+
return {
|
|
244
|
+
id: w.id,
|
|
245
|
+
walletAddress: w.wallet_address ?? "",
|
|
246
|
+
status: w.status,
|
|
247
|
+
fromChainId: w.from_chain_id,
|
|
248
|
+
toChainId: w.to_chain_id,
|
|
249
|
+
fromToken: w.from_token ?? "",
|
|
250
|
+
toToken: w.to_token ?? "",
|
|
251
|
+
fromAmount: w.from_amount,
|
|
252
|
+
toAmount: w.to_amount,
|
|
253
|
+
quoteId: w.quote_id ?? "",
|
|
254
|
+
tier: w.tier ?? "",
|
|
255
|
+
feeRate: w.fee_rate ?? 0,
|
|
256
|
+
settlementType: w.settlement_type ?? "public",
|
|
257
|
+
aztecNoteHash: w.aztec_note_hash,
|
|
258
|
+
txHash: w.tx_hash,
|
|
259
|
+
settlementTxHash: w.settlement_tx_hash ?? undefined,
|
|
260
|
+
settlementChainId: w.settlement_chain_id ?? undefined,
|
|
261
|
+
createdAt: w.created_at,
|
|
262
|
+
updatedAt: w.updated_at,
|
|
263
|
+
error: w.error,
|
|
264
|
+
...(typeof w.substatus === "string" ? { substatus: w.substatus } : {}),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chain Definitions
|
|
3
|
+
*
|
|
4
|
+
* Supported chains and their configuration.
|
|
5
|
+
*
|
|
6
|
+
* `CHAIN_REGISTRY` is the ONE literal in this file: the `ChainId` union, every
|
|
7
|
+
* display/lookup map and `SUPPORTED_CHAIN_IDS` are derived from it. `SUPPORTED_CHAIN_IDS`
|
|
8
|
+
* is typed `readonly ChainId[]`, so a hand-maintained copy that omitted a chain compiled
|
|
9
|
+
* cleanly and silently excluded it from `isChainId`, `SETTLEABLE_CHAIN_IDS` and the
|
|
10
|
+
* capabilities fallback; the per-chain maps outside this file (`tokens.ts`) fail to
|
|
11
|
+
* compile until they carry a new registry entry.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* One supported EVM chain. `explorer` is an origin with no trailing path.
|
|
15
|
+
* `nativeCurrency` is the gas token symbol (defaults to "ETH" if omitted).
|
|
16
|
+
*/
|
|
17
|
+
export interface ChainDefinition {
|
|
18
|
+
readonly id: number;
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly shortName: string;
|
|
21
|
+
readonly explorer: string;
|
|
22
|
+
readonly nativeCurrency?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The single source of truth for supported EVM chains.
|
|
26
|
+
*
|
|
27
|
+
* Keyed by slug rather than by id for two reasons: the slugs are themselves
|
|
28
|
+
* public API (`CHAIN_IDS.base`), and string keys preserve insertion order, so
|
|
29
|
+
* `SUPPORTED_CHAIN_IDS` keeps its declared order. Integer-like keys would be
|
|
30
|
+
* re-sorted ascending by the runtime, which would silently reorder the
|
|
31
|
+
* `/api/capabilities` fallback payload built from that array.
|
|
32
|
+
*
|
|
33
|
+
* Non-EVM and virtual chains are deliberately NOT here: Tron (base58 addresses,
|
|
34
|
+
* TronWeb signing, hash-route explorer) and the Aztec virtual source chain (0)
|
|
35
|
+
* are display-only, and join the extended maps below through their own
|
|
36
|
+
* constants. EVM-only code (viem/wagmi/RPC) must never receive those ids.
|
|
37
|
+
*
|
|
38
|
+
* Short names are the compact UI labels; explorers are the origins the link
|
|
39
|
+
* helpers below build on (one map, so per-component explorer maps cannot
|
|
40
|
+
* drift).
|
|
41
|
+
*/
|
|
42
|
+
export declare const CHAIN_REGISTRY: {
|
|
43
|
+
readonly ethereum: {
|
|
44
|
+
readonly id: 1;
|
|
45
|
+
readonly name: "Ethereum";
|
|
46
|
+
readonly shortName: "ETH";
|
|
47
|
+
readonly explorer: "https://etherscan.io";
|
|
48
|
+
};
|
|
49
|
+
readonly optimism: {
|
|
50
|
+
readonly id: 10;
|
|
51
|
+
readonly name: "Optimism";
|
|
52
|
+
readonly shortName: "OP";
|
|
53
|
+
readonly explorer: "https://optimistic.etherscan.io";
|
|
54
|
+
};
|
|
55
|
+
readonly polygon: {
|
|
56
|
+
readonly id: 137;
|
|
57
|
+
readonly name: "Polygon";
|
|
58
|
+
readonly shortName: "POLY";
|
|
59
|
+
readonly explorer: "https://polygonscan.com";
|
|
60
|
+
readonly nativeCurrency: "POL";
|
|
61
|
+
};
|
|
62
|
+
readonly arbitrum: {
|
|
63
|
+
readonly id: 42161;
|
|
64
|
+
readonly name: "Arbitrum";
|
|
65
|
+
readonly shortName: "ARB";
|
|
66
|
+
readonly explorer: "https://arbiscan.io";
|
|
67
|
+
};
|
|
68
|
+
readonly base: {
|
|
69
|
+
readonly id: 8453;
|
|
70
|
+
readonly name: "Base";
|
|
71
|
+
readonly shortName: "BASE";
|
|
72
|
+
readonly explorer: "https://basescan.org";
|
|
73
|
+
};
|
|
74
|
+
readonly robinhood: {
|
|
75
|
+
readonly id: 4663;
|
|
76
|
+
readonly name: "Robinhood";
|
|
77
|
+
readonly shortName: "HOOD";
|
|
78
|
+
readonly explorer: "https://robinhoodchain.blockscout.com";
|
|
79
|
+
};
|
|
80
|
+
readonly arc: {
|
|
81
|
+
readonly id: 5042;
|
|
82
|
+
readonly name: "Arc";
|
|
83
|
+
readonly shortName: "ARC";
|
|
84
|
+
readonly explorer: "https://explorer.arc.io";
|
|
85
|
+
readonly nativeCurrency: "USDC";
|
|
86
|
+
};
|
|
87
|
+
readonly gnosis: {
|
|
88
|
+
readonly id: 100;
|
|
89
|
+
readonly name: "Gnosis";
|
|
90
|
+
readonly shortName: "GNO";
|
|
91
|
+
readonly explorer: "https://gnosisscan.io";
|
|
92
|
+
readonly nativeCurrency: "xDAI";
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
/** Slug of a supported chain: the `CHAIN_REGISTRY` keys. */
|
|
96
|
+
export type ChainSlug = keyof typeof CHAIN_REGISTRY;
|
|
97
|
+
/** Supported L1/L2 chain IDs, derived from `CHAIN_REGISTRY`. */
|
|
98
|
+
export type ChainId = (typeof CHAIN_REGISTRY)[ChainSlug]["id"];
|
|
99
|
+
/** Aztec virtual chain ID for shielded operations */
|
|
100
|
+
export type AztecChainId = 0;
|
|
101
|
+
/**
|
|
102
|
+
* Extended chain ID: every UI-renderable chain beyond the core EVM `ChainId`.
|
|
103
|
+
* Includes the Aztec virtual source chain (0) and non-EVM destinations (Tron).
|
|
104
|
+
* Used for `Token.chainId` and the display maps (`EXTENDED_CHAIN_NAMES`,
|
|
105
|
+
* `CHAIN_ICONS`). EVM-only code (viem/wagmi/RPC) must never receive these.
|
|
106
|
+
*/
|
|
107
|
+
export type ExtendedChainId = ChainId | AztecChainId | TronChainId;
|
|
108
|
+
/**
|
|
109
|
+
* Tron mainnet chain id. Tron is non-EVM (base58 addresses, TronWeb signing),
|
|
110
|
+
* so it is deliberately kept OUT of the EVM `ChainId` union -- EVM-only code
|
|
111
|
+
* (viem, wagmi, contract reads) must never receive it. UI-level chain selection
|
|
112
|
+
* uses `SelectableChainId` below.
|
|
113
|
+
*/
|
|
114
|
+
export declare const TRON_CHAIN_ID = 728126428;
|
|
115
|
+
export type TronChainId = typeof TRON_CHAIN_ID;
|
|
116
|
+
/** Tron display names (Tron is outside CHAIN_REGISTRY, being non-EVM). */
|
|
117
|
+
export declare const TRON_CHAIN_NAME = "Tron";
|
|
118
|
+
export declare const TRON_SHORT_NAME = "TRON";
|
|
119
|
+
/**
|
|
120
|
+
* Chains a user can pick in the trade UI: every EVM chain plus Tron. Components
|
|
121
|
+
* that only do chain *selection* (selectors, route display) accept this wider
|
|
122
|
+
* union; anything that touches an EVM RPC stays on `ChainId`.
|
|
123
|
+
*/
|
|
124
|
+
export type SelectableChainId = ChainId | TronChainId;
|
|
125
|
+
/** Type guard for the Tron mainnet chain id. */
|
|
126
|
+
export declare function isTronChainId(value: unknown): value is TronChainId;
|
|
127
|
+
/**
|
|
128
|
+
* Type guard for any chain the UI can render a link/name for (every EVM `ChainId`
|
|
129
|
+
* plus Tron). Guards raw `settlementChainId` values from Riddler -- which adds
|
|
130
|
+
* chains on its own cadence -- so an unknown id falls back to inference instead of
|
|
131
|
+
* producing a dead `href=""` or a `View undefined tx` label.
|
|
132
|
+
*/
|
|
133
|
+
export declare function isSelectableChainId(value: unknown): value is SelectableChainId;
|
|
134
|
+
/**
|
|
135
|
+
* Robinhood Chain (4663): an Arbitrum Orbit L2 whose native stablecoin is USDG
|
|
136
|
+
* (Global Dollar, Paxos). It has no USDC. The solver settles USDC<->USDG (and
|
|
137
|
+
* other stablecoin) cross-asset corridors to and from it. It is fully EVM (0x
|
|
138
|
+
* addresses, EIP-712 signing, Permit2 pull on USDG) and a first-class member
|
|
139
|
+
* of `ChainId`: the trade UI lists Robinhood + USDG, so the per-chain token
|
|
140
|
+
* maps carry it. USDG is a first-class token (see USDG_ADDRESSES in
|
|
141
|
+
* tokens.ts), never shoehorned into USDC_ADDRESSES (that map is Permit2-free
|
|
142
|
+
* ERC-3009 USDC only, and USDG supports neither ERC-3009 nor EIP-2612).
|
|
143
|
+
* `ROBINHOOD_CHAIN_ID` is kept for readable references and the settleable
|
|
144
|
+
* guards below.
|
|
145
|
+
*/
|
|
146
|
+
export declare const ROBINHOOD_CHAIN_ID = 4663;
|
|
147
|
+
export type RobinhoodChainId = typeof ROBINHOOD_CHAIN_ID;
|
|
148
|
+
/** Type guard for the Robinhood Chain id. */
|
|
149
|
+
export declare function isRobinhoodChainId(value: unknown): value is RobinhoodChainId;
|
|
150
|
+
/**
|
|
151
|
+
* Arc chain id. Arc is Circle's EVM-compatible L1 where USDC is the native
|
|
152
|
+
* gas token (18 dec protocol-level, 6 dec ERC-20). No WETH, no ETH --
|
|
153
|
+
* stablecoin-only chain. Feature-flagged behind ARC_LIVE.
|
|
154
|
+
*/
|
|
155
|
+
export declare const ARC_CHAIN_ID = 5042;
|
|
156
|
+
export type ArcChainId = typeof ARC_CHAIN_ID;
|
|
157
|
+
/** Type guard for the Arc chain id. */
|
|
158
|
+
export declare function isArcChainId(value: unknown): value is ArcChainId;
|
|
159
|
+
/**
|
|
160
|
+
* Gnosis Chain id. Native gas is xDAI. Xochi lists a single token here,
|
|
161
|
+
* Monerium EURe (18 dec, no ERC-3009, Permit2 pull); there is no WETH or USDC
|
|
162
|
+
* leg. Feature-flagged in the UI behind GNOSIS_LIVE.
|
|
163
|
+
*/
|
|
164
|
+
export declare const GNOSIS_CHAIN_ID = 100;
|
|
165
|
+
export type GnosisChainId = typeof GNOSIS_CHAIN_ID;
|
|
166
|
+
/** Type guard for the Gnosis Chain id. */
|
|
167
|
+
export declare function isGnosisChainId(value: unknown): value is GnosisChainId;
|
|
168
|
+
/**
|
|
169
|
+
* Slug -> chain id, derived from the registry, plus the Aztec virtual chain
|
|
170
|
+
* (0), which has no registry entry because it is not an EVM chain.
|
|
171
|
+
*/
|
|
172
|
+
type ChainIdsBySlug = {
|
|
173
|
+
readonly [K in ChainSlug]: (typeof CHAIN_REGISTRY)[K]["id"];
|
|
174
|
+
} & {
|
|
175
|
+
readonly aztec: AztecChainId;
|
|
176
|
+
};
|
|
177
|
+
/** All supported chain IDs */
|
|
178
|
+
export declare const CHAIN_IDS: ChainIdsBySlug;
|
|
179
|
+
/** Chain names for display */
|
|
180
|
+
export declare const CHAIN_NAMES: Record<ChainId, string>;
|
|
181
|
+
/**
|
|
182
|
+
* Short chain names for compact display. Keyed by `SelectableChainId` so the
|
|
183
|
+
* non-EVM Tron destination (base58) has a short name too -- the tx-link/name
|
|
184
|
+
* helpers in the trade result surfaces index this with a destination chain id
|
|
185
|
+
* that can be Tron.
|
|
186
|
+
*/
|
|
187
|
+
export declare const CHAIN_SHORT_NAMES: Record<SelectableChainId, string>;
|
|
188
|
+
/**
|
|
189
|
+
* Block explorer origins by chain (no trailing path). Single source of truth --
|
|
190
|
+
* build tx/address URLs through the helpers below so links always target the
|
|
191
|
+
* right chain (the old per-component maps drifted and one hardcoded Arbiscan).
|
|
192
|
+
*/
|
|
193
|
+
export declare const CHAIN_EXPLORERS: Record<ChainId, string>;
|
|
194
|
+
/**
|
|
195
|
+
* Native currency symbol per chain. Used for gas display (QuoteDetails) and
|
|
196
|
+
* balance labels (usePublicBalances). Most chains use ETH; Polygon uses POL,
|
|
197
|
+
* Arc uses USDC (native gas token on Circle's stablecoin L1).
|
|
198
|
+
* Derived from `CHAIN_REGISTRY.nativeCurrency`, defaulting to "ETH".
|
|
199
|
+
*/
|
|
200
|
+
export declare const CHAIN_NATIVE_CURRENCY: Record<ChainId, string>;
|
|
201
|
+
/**
|
|
202
|
+
* Tronscan uses a `#/` hash route (not the `/tx/` path shape of the EVM
|
|
203
|
+
* explorers), so Tron links are built separately from `CHAIN_EXPLORERS`.
|
|
204
|
+
*/
|
|
205
|
+
export declare const TRON_EXPLORER = "https://tronscan.org";
|
|
206
|
+
/**
|
|
207
|
+
* Build a transaction URL on the given chain's explorer (empty if unknown).
|
|
208
|
+
* Accepts a `SelectableChainId` so a Tron destination fill can be linked; Tron
|
|
209
|
+
* routes to tronscan's hash-route form.
|
|
210
|
+
*/
|
|
211
|
+
export declare function getExplorerTxUrl(chainId: SelectableChainId, txHash: string): string;
|
|
212
|
+
/** Build an address URL on the given chain's explorer (empty if unknown). */
|
|
213
|
+
export declare function getExplorerAddressUrl(chainId: SelectableChainId, address: string): string;
|
|
214
|
+
/**
|
|
215
|
+
* Extended chain names including the Aztec source + non-EVM destinations. The
|
|
216
|
+
* Aztec virtual chain is branded "Xochi" in the UI, which is why its display
|
|
217
|
+
* name is not simply a chain name.
|
|
218
|
+
*/
|
|
219
|
+
export declare const EXTENDED_CHAIN_NAMES: Record<ExtendedChainId, string>;
|
|
220
|
+
/** Supported chain IDs as array */
|
|
221
|
+
export declare const SUPPORTED_CHAIN_IDS: readonly ChainId[];
|
|
222
|
+
/** Type guard for ChainId */
|
|
223
|
+
export declare function isChainId(value: unknown): value is ChainId;
|
|
224
|
+
/**
|
|
225
|
+
* A chain id the solver can settle; equals `ChainId` (Robinhood is a first-class chain). The
|
|
226
|
+
* name is kept for the intent-API call sites (schema validation) that document the settleable
|
|
227
|
+
* surface explicitly, mirroring xochi's `@xochi/shared`.
|
|
228
|
+
*/
|
|
229
|
+
export type SettleableChainId = ChainId | RobinhoodChainId;
|
|
230
|
+
/**
|
|
231
|
+
* Chain ids the solver can settle to or from; exactly `SUPPORTED_CHAIN_IDS`. The intent API
|
|
232
|
+
* (`/api/intent/quote` and `/api/intent/execute`) validates request chain ids against this set.
|
|
233
|
+
* Token addresses ride in the request body, so the API needs no per-chain token config to
|
|
234
|
+
* accept a corridor; the solver decides feasibility.
|
|
235
|
+
*/
|
|
236
|
+
export declare const SETTLEABLE_CHAIN_IDS: readonly SettleableChainId[];
|
|
237
|
+
/** Type guard for a settleable chain id (any supported chain, including Robinhood). */
|
|
238
|
+
export declare function isSettleableChainId(value: unknown): value is SettleableChainId;
|
|
239
|
+
/** Type guard for ExtendedChainId (EVM chains + Aztec source + Tron destination). */
|
|
240
|
+
export declare function isExtendedChainId(value: unknown): value is ExtendedChainId;
|
|
241
|
+
/** Check if route between chains is supported */
|
|
242
|
+
export declare function isRouteSupported(from: ChainId, to: ChainId): boolean;
|
|
243
|
+
export {};
|