@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.
Files changed (129) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +21 -0
  3. package/README.md +120 -2
  4. package/dist/cjs/claim.d.ts +33 -0
  5. package/dist/cjs/claim.js +44 -0
  6. package/dist/cjs/fee.d.ts +247 -0
  7. package/dist/cjs/fee.js +295 -0
  8. package/dist/cjs/index.d.ts +18 -0
  9. package/dist/cjs/index.js +33 -0
  10. package/dist/cjs/machine/claimInfo.d.ts +29 -0
  11. package/dist/cjs/machine/claimInfo.js +26 -0
  12. package/dist/cjs/machine/decisions.d.ts +452 -0
  13. package/dist/cjs/machine/decisions.js +665 -0
  14. package/dist/cjs/machine/deposit.d.ts +113 -0
  15. package/dist/cjs/machine/deposit.js +265 -0
  16. package/dist/cjs/machine/index.d.ts +23 -0
  17. package/dist/cjs/machine/index.js +53 -0
  18. package/dist/cjs/machine/machine.d.ts +411 -0
  19. package/dist/cjs/machine/machine.js +1627 -0
  20. package/dist/cjs/machine/phase.d.ts +24 -0
  21. package/dist/cjs/machine/phase.js +45 -0
  22. package/dist/cjs/machine/quoteChecks.d.ts +41 -0
  23. package/dist/cjs/machine/quoteChecks.js +82 -0
  24. package/dist/cjs/machine/receipt.d.ts +151 -0
  25. package/dist/cjs/machine/receipt.js +69 -0
  26. package/dist/cjs/machine/recipientEcho.d.ts +16 -0
  27. package/dist/cjs/machine/recipientEcho.js +27 -0
  28. package/dist/cjs/machine/solverPin.d.ts +110 -0
  29. package/dist/cjs/machine/solverPin.js +214 -0
  30. package/dist/cjs/machine/state.d.ts +167 -0
  31. package/dist/cjs/machine/state.js +31 -0
  32. package/dist/cjs/machine/transition.d.ts +15 -0
  33. package/dist/cjs/machine/transition.js +20 -0
  34. package/dist/cjs/machine/validateRequest.d.ts +15 -0
  35. package/dist/cjs/machine/validateRequest.js +127 -0
  36. package/dist/cjs/machine/view.d.ts +3 -0
  37. package/dist/cjs/machine/view.js +74 -0
  38. package/dist/cjs/machine/wire.d.ts +527 -0
  39. package/dist/cjs/machine/wire.js +280 -0
  40. package/dist/cjs/package.json +1 -0
  41. package/dist/cjs/shared/api.d.ts +9 -0
  42. package/dist/cjs/shared/api.js +5 -0
  43. package/dist/cjs/shared/chains.d.ts +243 -0
  44. package/dist/cjs/shared/chains.js +250 -0
  45. package/dist/cjs/shared/eip712.d.ts +44 -0
  46. package/dist/cjs/shared/eip712.js +64 -0
  47. package/dist/cjs/shared/index.d.ts +16 -0
  48. package/dist/cjs/shared/index.js +32 -0
  49. package/dist/cjs/shared/intentStream.d.ts +74 -0
  50. package/dist/cjs/shared/intentStream.js +59 -0
  51. package/dist/cjs/shared/intents.d.ts +88 -0
  52. package/dist/cjs/shared/intents.js +227 -0
  53. package/dist/cjs/shared/schemas.d.ts +928 -0
  54. package/dist/cjs/shared/schemas.js +524 -0
  55. package/dist/cjs/shared/shieldedPreview.d.ts +841 -0
  56. package/dist/cjs/shared/shieldedPreview.js +128 -0
  57. package/dist/cjs/shared/stealth.d.ts +53 -0
  58. package/dist/cjs/shared/stealth.js +68 -0
  59. package/dist/cjs/shared/trading.d.ts +153 -0
  60. package/dist/cjs/shared/trading.js +185 -0
  61. package/dist/cjs/shared/types.d.ts +37 -0
  62. package/dist/cjs/shared/types.js +7 -0
  63. package/dist/cjs/shared/validators.d.ts +30 -0
  64. package/dist/cjs/shared/validators.js +79 -0
  65. package/dist/cjs/transport/client.d.ts +234 -0
  66. package/dist/cjs/transport/client.js +1456 -0
  67. package/dist/esm/claim.d.ts +33 -0
  68. package/dist/esm/claim.js +41 -0
  69. package/dist/esm/fee.d.ts +247 -0
  70. package/dist/esm/fee.js +285 -0
  71. package/dist/esm/index.d.ts +18 -0
  72. package/dist/esm/index.js +17 -0
  73. package/dist/esm/machine/claimInfo.d.ts +29 -0
  74. package/dist/esm/machine/claimInfo.js +23 -0
  75. package/dist/esm/machine/decisions.d.ts +452 -0
  76. package/dist/esm/machine/decisions.js +640 -0
  77. package/dist/esm/machine/deposit.d.ts +113 -0
  78. package/dist/esm/machine/deposit.js +260 -0
  79. package/dist/esm/machine/index.d.ts +23 -0
  80. package/dist/esm/machine/index.js +23 -0
  81. package/dist/esm/machine/machine.d.ts +411 -0
  82. package/dist/esm/machine/machine.js +1621 -0
  83. package/dist/esm/machine/phase.d.ts +24 -0
  84. package/dist/esm/machine/phase.js +42 -0
  85. package/dist/esm/machine/quoteChecks.d.ts +41 -0
  86. package/dist/esm/machine/quoteChecks.js +78 -0
  87. package/dist/esm/machine/receipt.d.ts +151 -0
  88. package/dist/esm/machine/receipt.js +64 -0
  89. package/dist/esm/machine/recipientEcho.d.ts +16 -0
  90. package/dist/esm/machine/recipientEcho.js +24 -0
  91. package/dist/esm/machine/solverPin.d.ts +110 -0
  92. package/dist/esm/machine/solverPin.js +210 -0
  93. package/dist/esm/machine/state.d.ts +167 -0
  94. package/dist/esm/machine/state.js +28 -0
  95. package/dist/esm/machine/transition.d.ts +15 -0
  96. package/dist/esm/machine/transition.js +17 -0
  97. package/dist/esm/machine/validateRequest.d.ts +15 -0
  98. package/dist/esm/machine/validateRequest.js +123 -0
  99. package/dist/esm/machine/view.d.ts +3 -0
  100. package/dist/esm/machine/view.js +71 -0
  101. package/dist/esm/machine/wire.d.ts +527 -0
  102. package/dist/esm/machine/wire.js +266 -0
  103. package/dist/esm/shared/api.d.ts +9 -0
  104. package/dist/esm/shared/api.js +4 -0
  105. package/dist/esm/shared/chains.d.ts +243 -0
  106. package/dist/esm/shared/chains.js +236 -0
  107. package/dist/esm/shared/eip712.d.ts +44 -0
  108. package/dist/esm/shared/eip712.js +61 -0
  109. package/dist/esm/shared/index.d.ts +16 -0
  110. package/dist/esm/shared/index.js +16 -0
  111. package/dist/esm/shared/intentStream.d.ts +74 -0
  112. package/dist/esm/shared/intentStream.js +55 -0
  113. package/dist/esm/shared/intents.d.ts +88 -0
  114. package/dist/esm/shared/intents.js +213 -0
  115. package/dist/esm/shared/schemas.d.ts +928 -0
  116. package/dist/esm/shared/schemas.js +521 -0
  117. package/dist/esm/shared/shieldedPreview.d.ts +841 -0
  118. package/dist/esm/shared/shieldedPreview.js +124 -0
  119. package/dist/esm/shared/stealth.d.ts +53 -0
  120. package/dist/esm/shared/stealth.js +64 -0
  121. package/dist/esm/shared/trading.d.ts +153 -0
  122. package/dist/esm/shared/trading.js +182 -0
  123. package/dist/esm/shared/types.d.ts +37 -0
  124. package/dist/esm/shared/types.js +6 -0
  125. package/dist/esm/shared/validators.d.ts +30 -0
  126. package/dist/esm/shared/validators.js +74 -0
  127. package/dist/esm/transport/client.d.ts +234 -0
  128. package/dist/esm/transport/client.js +1447 -0
  129. package/package.json +61 -4
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * Riddler's shielded terms preview wire (`POST /xochi/shielded/preview`,
4
+ * Riddler#1296): the typed request and response shapes and a decoder.
5
+ *
6
+ * There is NO transport method: the preview needs a `quote`-scoped Riddler
7
+ * credential, so only the Xochi Worker calls it, and the Worker proxy route is
8
+ * a follow-up owned by xochi#635. These shapes exist so that proxy and its
9
+ * consumers decode the solver's answer with the same schema the solver
10
+ * publishes (`packages/spec/wire/openapi.json`, `ShieldedPreviewResponse`).
11
+ *
12
+ * The preview is NOT a signing payload: `preview_digest` is the EIP-712 digest
13
+ * of `ShieldedPreviewV1` under the `Xochi Shielded Preview` domain, which no
14
+ * `ShieldedTermsV1` signature verifies against (`@xochi/spec`
15
+ * `conformance/shielded-terms.json`, PROPOSED). `executable` is always false.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.ShieldedPreviewResponseSchema = exports.ShieldedPreviewSchema = exports.ShieldedFeeBreakdownSchema = exports.ShieldedCorridorSchema = exports.ShieldedNetworkIdentitySchema = exports.aztecValue = void 0;
19
+ exports.decodeShieldedPreview = decodeShieldedPreview;
20
+ const zod_1 = require("zod");
21
+ const schemas_js_1 = require("./schemas.js");
22
+ /** 32-byte hex (`0x` + 64). */
23
+ const bytes32 = zod_1.z.string().regex(/^0x[0-9a-fA-F]{64}$/);
24
+ /**
25
+ * A 32-byte Aztec address or field element. The decoder checks the shape only;
26
+ * the solver has already refused zero and values at or above the BN254 modulus.
27
+ * A 20-byte EVM address never fits.
28
+ */
29
+ exports.aztecValue = bytes32;
30
+ /** Canonical unsigned decimal string (uint256 members). */
31
+ const uintString = zod_1.z.string().regex(/^(0|[1-9][0-9]*)$/);
32
+ /** Canonical positive base-unit amount. */
33
+ const baseUnits = zod_1.z.string().regex(/^[1-9][0-9]*$/);
34
+ exports.ShieldedNetworkIdentitySchema = zod_1.z.object({
35
+ aztec_chain_id: zod_1.z.string().regex(/^[1-9][0-9]*$/),
36
+ protocol_version: zod_1.z.string().regex(/^[1-9][0-9]*$/),
37
+ rollup_address: schemas_js_1.ethAddress,
38
+ l1_chain_id: zod_1.z.string().regex(/^[1-9][0-9]*$/),
39
+ genesis_or_network_digest: bytes32,
40
+ });
41
+ exports.ShieldedCorridorSchema = zod_1.z.object({
42
+ corridor_id: bytes32,
43
+ revision: zod_1.z.number().int().min(1).max(4294967295),
44
+ manifest_digest: bytes32,
45
+ });
46
+ exports.ShieldedFeeBreakdownSchema = zod_1.z.object({
47
+ gas_floor: uintString,
48
+ solver_fee_bps: zod_1.z.number().int().min(0).max(10000),
49
+ solver_fee: uintString,
50
+ price_impact_bps: zod_1.z.number().int().min(0).max(10000),
51
+ price_impact: uintString,
52
+ venue_fee_bps: zod_1.z.number().int().min(0).max(10000),
53
+ venue_fee: uintString,
54
+ routing_fee_bps: zod_1.z.number().int().min(0).max(10000),
55
+ routing_fee: uintString,
56
+ total_bps: zod_1.z.number().int().min(0).max(40000),
57
+ total_fee: uintString,
58
+ });
59
+ /** `ShieldedPreviewV1` in snake_case: the terms minus the per-execution members. */
60
+ exports.ShieldedPreviewSchema = zod_1.z.object({
61
+ protocol_version: zod_1.z.literal("xochi.shielded.preview.v1"),
62
+ environment: zod_1.z.string().min(1),
63
+ source: zod_1.z.object({
64
+ chain_id: zod_1.z.number().int().positive(),
65
+ token_address: schemas_js_1.ethAddress,
66
+ wallet: schemas_js_1.ethAddress,
67
+ collector: schemas_js_1.ethAddress,
68
+ amount: baseUnits,
69
+ }),
70
+ destination: zod_1.z.object({
71
+ settlement_type: zod_1.z.literal("shielded"),
72
+ privacy_level: zod_1.z.literal("private"),
73
+ disclosure_profile: zod_1.z.literal("recipient-private-public-amount-v1"),
74
+ rail: zod_1.z.literal("a_tracer"),
75
+ network: exports.ShieldedNetworkIdentitySchema,
76
+ corridor: exports.ShieldedCorridorSchema,
77
+ asset_id: zod_1.z.string().min(1),
78
+ token_address: exports.aztecValue,
79
+ recipient_binding_id: zod_1.z.string().min(1),
80
+ recipient_descriptor_digest: bytes32,
81
+ recipient_address: exports.aztecValue,
82
+ amount: baseUnits,
83
+ fee_payer: exports.aztecValue,
84
+ }),
85
+ fee_breakdown: exports.ShieldedFeeBreakdownSchema,
86
+ refund_address: schemas_js_1.ethAddress,
87
+ refund_amount: baseUnits,
88
+ collect_before: zod_1.z.number().int().nonnegative(),
89
+ settlement_service_deadline: zod_1.z.number().int().nonnegative(),
90
+ recipient_binding_digest: bytes32,
91
+ policy_revision: zod_1.z.number().int().nonnegative(),
92
+ });
93
+ exports.ShieldedPreviewResponseSchema = zod_1.z.object({
94
+ executable: zod_1.z.literal(false),
95
+ preview_version: zod_1.z.literal("xochi.shielded.preview.v1"),
96
+ preview_digest: bytes32,
97
+ recipient_binding_digest: bytes32,
98
+ domain: zod_1.z.object({
99
+ name: zod_1.z.literal("Xochi Shielded Preview"),
100
+ version: zod_1.z.literal("1"),
101
+ chain_id: zod_1.z.number().int().positive(),
102
+ salt: bytes32,
103
+ }),
104
+ preview: exports.ShieldedPreviewSchema,
105
+ manifest: zod_1.z.object({
106
+ manifest_digest: bytes32,
107
+ aztec_sdk_version: zod_1.z.string().min(1),
108
+ recipient_account_class_id: exports.aztecValue,
109
+ }),
110
+ });
111
+ /**
112
+ * Decode a preview response. Throws (zod) on any shape the solver does not
113
+ * publish, including an executable or terms-shaped answer, and on a fee
114
+ * breakdown whose totals disagree: `total_fee` must be `max(gas_floor, layers)` (the gas
115
+ * floor is a minimum fee, as in FeePolicy) and `total_bps` the four rates.
116
+ */
117
+ function decodeShieldedPreview(body) {
118
+ const decoded = exports.ShieldedPreviewResponseSchema.parse(body);
119
+ const f = decoded.preview.fee_breakdown;
120
+ const layers = BigInt(f.solver_fee) + BigInt(f.price_impact) + BigInt(f.venue_fee) + BigInt(f.routing_fee);
121
+ const gasFloor = BigInt(f.gas_floor);
122
+ const totalFee = layers > gasFloor ? layers : gasFloor;
123
+ const totalBps = f.solver_fee_bps + f.price_impact_bps + f.venue_fee_bps + f.routing_fee_bps;
124
+ if (BigInt(f.total_fee) !== totalFee || f.total_bps !== totalBps) {
125
+ throw new Error("shielded preview fee_breakdown totals disagree with max(gas_floor, layers)");
126
+ }
127
+ return decoded;
128
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Stealth settlement constants: the SimpleAccount factory + salt a stealth
3
+ * payout is derived against, and the per-chain allowlist that bounds which
4
+ * advertised factory may be followed.
5
+ *
6
+ * @see https://eips.ethereum.org/EIPS/eip-5564
7
+ * @see https://eips.ethereum.org/EIPS/eip-6538
8
+ */
9
+ /**
10
+ * SimpleAccountFactory (v0.8) for the counterfactual stealth destination.
11
+ * Salt is 0: one account per stealth EOA.
12
+ *
13
+ * Canonical home for a security-critical address the client and the worker
14
+ * both need; never declare a second copy.
15
+ */
16
+ export declare const SIMPLE_ACCOUNT_FACTORY = "0x13E9ed32155810FDbd067D4522C492D6f68E5944";
17
+ /** CREATE2 salt for the stealth SimpleAccount. One account per stealth EOA. */
18
+ export declare const STEALTH_ACCOUNT_SALT = 0;
19
+ /**
20
+ * Factories a stealth payout may be derived against, PER DESTINATION CHAIN.
21
+ *
22
+ * The client deliberately FOLLOWS the solver-advertised factory rather than its
23
+ * own pin (#302): if the two disagree they derive different counterfactual
24
+ * addresses and the recipient cannot find their money. That is the right call
25
+ * for parity, and it is why this list exists rather than a hard pin.
26
+ *
27
+ * Nothing else constrains WHAT may be advertised. The factory decides who owns
28
+ * the account the payout lands in, so an advertised factory that deploys an
29
+ * account owned by someone else is theft, and both sides would have agreed on
30
+ * it. This is the trust anchor the advertised value is checked against: follow
31
+ * the solver, but only into a factory this build already knows.
32
+ *
33
+ * Keyed by chain rather than a flat list because the capability matrix is
34
+ * expected to go per-chain, and a flat list would then accept a factory
35
+ * legitimate on one chain as authorization for another. Stealth is pinned to
36
+ * Ethereum L1 today (STEALTH_SETTLEMENT_CHAIN_ID), so there is exactly one
37
+ * entry; an unlisted chain has no trusted factory and fails closed.
38
+ *
39
+ * ROTATION: ship a build listing both the old and new factory for that chain,
40
+ * let the solver flip, then drop the old one. Staging it that way keeps the
41
+ * allowlist fail-closed without a window where stealth is refused.
42
+ */
43
+ export declare const TRUSTED_STEALTH_FACTORIES: Readonly<Record<number, readonly string[]>>;
44
+ /**
45
+ * Whether a factory may be used to derive a stealth payout ON THIS CHAIN.
46
+ *
47
+ * Case-insensitive: EVM addresses compare case-insensitively and the capability
48
+ * matrix preserves whatever casing the solver sent. Absent address, absent
49
+ * chain, or a chain with no listed factory all return false, because "we have
50
+ * no entry for this" and "this is fine" must not be the same answer about who
51
+ * receives someone's money.
52
+ */
53
+ export declare function isTrustedStealthFactory(address: string | undefined | null, chainId: number | undefined | null): boolean;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * Stealth settlement constants: the SimpleAccount factory + salt a stealth
4
+ * payout is derived against, and the per-chain allowlist that bounds which
5
+ * advertised factory may be followed.
6
+ *
7
+ * @see https://eips.ethereum.org/EIPS/eip-5564
8
+ * @see https://eips.ethereum.org/EIPS/eip-6538
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.TRUSTED_STEALTH_FACTORIES = exports.STEALTH_ACCOUNT_SALT = exports.SIMPLE_ACCOUNT_FACTORY = void 0;
12
+ exports.isTrustedStealthFactory = isTrustedStealthFactory;
13
+ const chains_js_1 = require("./chains.js");
14
+ /**
15
+ * SimpleAccountFactory (v0.8) for the counterfactual stealth destination.
16
+ * Salt is 0: one account per stealth EOA.
17
+ *
18
+ * Canonical home for a security-critical address the client and the worker
19
+ * both need; never declare a second copy.
20
+ */
21
+ exports.SIMPLE_ACCOUNT_FACTORY = "0x13E9ed32155810FDbd067D4522C492D6f68E5944";
22
+ /** CREATE2 salt for the stealth SimpleAccount. One account per stealth EOA. */
23
+ exports.STEALTH_ACCOUNT_SALT = 0;
24
+ /**
25
+ * Factories a stealth payout may be derived against, PER DESTINATION CHAIN.
26
+ *
27
+ * The client deliberately FOLLOWS the solver-advertised factory rather than its
28
+ * own pin (#302): if the two disagree they derive different counterfactual
29
+ * addresses and the recipient cannot find their money. That is the right call
30
+ * for parity, and it is why this list exists rather than a hard pin.
31
+ *
32
+ * Nothing else constrains WHAT may be advertised. The factory decides who owns
33
+ * the account the payout lands in, so an advertised factory that deploys an
34
+ * account owned by someone else is theft, and both sides would have agreed on
35
+ * it. This is the trust anchor the advertised value is checked against: follow
36
+ * the solver, but only into a factory this build already knows.
37
+ *
38
+ * Keyed by chain rather than a flat list because the capability matrix is
39
+ * expected to go per-chain, and a flat list would then accept a factory
40
+ * legitimate on one chain as authorization for another. Stealth is pinned to
41
+ * Ethereum L1 today (STEALTH_SETTLEMENT_CHAIN_ID), so there is exactly one
42
+ * entry; an unlisted chain has no trusted factory and fails closed.
43
+ *
44
+ * ROTATION: ship a build listing both the old and new factory for that chain,
45
+ * let the solver flip, then drop the old one. Staging it that way keeps the
46
+ * allowlist fail-closed without a window where stealth is refused.
47
+ */
48
+ exports.TRUSTED_STEALTH_FACTORIES = {
49
+ [chains_js_1.CHAIN_IDS.ethereum]: [exports.SIMPLE_ACCOUNT_FACTORY],
50
+ };
51
+ /**
52
+ * Whether a factory may be used to derive a stealth payout ON THIS CHAIN.
53
+ *
54
+ * Case-insensitive: EVM addresses compare case-insensitively and the capability
55
+ * matrix preserves whatever casing the solver sent. Absent address, absent
56
+ * chain, or a chain with no listed factory all return false, because "we have
57
+ * no entry for this" and "this is fine" must not be the same answer about who
58
+ * receives someone's money.
59
+ */
60
+ function isTrustedStealthFactory(address, chainId) {
61
+ if (!address || chainId === undefined || chainId === null)
62
+ return false;
63
+ const allowed = exports.TRUSTED_STEALTH_FACTORIES[chainId];
64
+ if (!allowed)
65
+ return false;
66
+ const normalized = address.toLowerCase();
67
+ return allowed.some((f) => f.toLowerCase() === normalized);
68
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Trading Constants
3
+ *
4
+ * Shared trading configuration for slippage, quotes, and limits.
5
+ */
6
+ /** Default slippage in basis points (0.5%) */
7
+ export declare const DEFAULT_SLIPPAGE_BPS = 50;
8
+ /** Available slippage options in basis points */
9
+ export declare const SLIPPAGE_OPTIONS: readonly [10, 50, 100, 200];
10
+ export type SlippageOption = (typeof SLIPPAGE_OPTIONS)[number];
11
+ /** Slippage presets with labels */
12
+ export declare const SLIPPAGE_PRESETS: {
13
+ readonly low: 10;
14
+ readonly medium: 50;
15
+ readonly high: 100;
16
+ };
17
+ /** Quote expiry in seconds */
18
+ export declare const QUOTE_EXPIRY_SEC = 30;
19
+ /** Maximum retries for failed quotes */
20
+ export declare const MAX_QUOTE_RETRIES = 3;
21
+ /** Maximum decimal places for token amounts */
22
+ export declare const MAX_TOKEN_DECIMALS = 18;
23
+ /** Minimum trade amount in USD */
24
+ export declare const MIN_TRADE_USD = 1;
25
+ /** Maximum slippage percentage */
26
+ export declare const MAX_SLIPPAGE_PERCENT = 50;
27
+ /** Minimum slippage percentage */
28
+ export declare const MIN_SLIPPAGE_PERCENT = 0.01;
29
+ /**
30
+ * Hard ceiling on how far below market a fill may be before execution is
31
+ * refused. This is all-in value loss (fees + fixed network minimum + market
32
+ * slippage) versus the live market rate, not the user's configurable slippage
33
+ * tolerance. Protects users from executing trades -- typically dust orders --
34
+ * where costs eat more than half the notional.
35
+ */
36
+ export declare const MAX_PRICE_IMPACT_PERCENT = 50;
37
+ /**
38
+ * Minimum trust score for Aztec L2 shielded settlement (the Private privacy
39
+ * level). L1 stealth (ERC-5564) is NOT trust-gated: privacy is free by default,
40
+ * so any wallet can settle to a one-time stealth address and simply pays its own
41
+ * trust-tier fee. Only the deeper Aztec-shielded tiers -- Private (50+) and
42
+ * Sovereign (75+), both reached via pxe-bridge -- require trust.
43
+ */
44
+ export declare const SHIELDED_MIN_SCORE = 50;
45
+ /** Trust score thresholds by tier */
46
+ export declare const TRUST_THRESHOLDS: {
47
+ readonly trusted: 25;
48
+ readonly verified: 50;
49
+ readonly premium: 75;
50
+ readonly institutional: 100;
51
+ };
52
+ /** Basis points denominator (1 bps = 0.01%) */
53
+ export declare const BPS_DENOMINATOR = 10000;
54
+ /** Maximum deadline offset in seconds (1 hour) */
55
+ export declare const MAX_DEADLINE_OFFSET_SEC = 3600;
56
+ /**
57
+ * Server-side ceiling on a quote request's `deadline`, in seconds from now.
58
+ *
59
+ * Deliberately looser than MAX_DEADLINE_OFFSET_SEC, which is the CLIENT's rule
60
+ * (src/utils/validators.ts) and the value the frontend actually sends. This is
61
+ * the outer bound the API will accept from anyone, sized so an agent or a device
62
+ * with a skewed clock is not rejected for asking a reasonable question.
63
+ *
64
+ * The reason there is a ceiling at all: `deadline` is what the user signs into
65
+ * the intent EIP-712, so an unbounded value is an authorization with no
66
+ * expiry -- and `decideReconcile` treats "past the signed deadline" as the point
67
+ * where an in-doubt intent can no longer move funds.
68
+ */
69
+ export declare const MAX_QUOTE_DEADLINE_FUTURE_SEC: number;
70
+ /**
71
+ * How far in the PAST a quote `deadline` may be and still be accepted, in
72
+ * seconds. Pure clock-skew tolerance: no correct client asks for a deadline that
73
+ * has already passed, but one whose clock is behind might.
74
+ *
75
+ * A past-dated deadline is not inert data: the abandoned-deposit sweep reads
76
+ * `deadline` to decide when a deposit route has died, so `deadline: 1` would
77
+ * mean "terminalise this live route on the next cron tick". The sweep gates on
78
+ * the server-set `created_at` too; this is the other half: the value never
79
+ * gets stored in the first place.
80
+ */
81
+ export declare const MAX_QUOTE_DEADLINE_PAST_SEC = 3600;
82
+ /**
83
+ * Sanity bound on the SOLVER-supplied `deposit_deadline`, in seconds from the
84
+ * quote, applied only when Xochi persists it.
85
+ *
86
+ * The client treats this field as untrusted: `POLL_CEILING_MAX_MS` exists
87
+ * because a malformed or hostile `deposit_deadline` far in the future must not
88
+ * turn the poll into a permanent background request loop. Persisting it makes
89
+ * the server trust it more than the browser does, and it is the PRIMARY gate
90
+ * on the abandoned-deposit sweep (`COALESCE(deposit_deadline, deadline)`), so
91
+ * one bad value -- a misparsed `XOCHI_DEPOSIT_DEADLINE_SECONDS`, a solver bug
92
+ * -- would make every deposit row permanently and silently unsweepable.
93
+ *
94
+ * Riddler's default is 1800s, so this is two orders of magnitude of headroom: it
95
+ * rejects nothing a correctly configured solver can mint. A value outside it is
96
+ * stored as NULL, which degrades the sweep to the quote `deadline` rather than
97
+ * to no gate at all. The value SERVED to the client is never altered -- it is
98
+ * covered by the deposit attestation signature, so changing it would break
99
+ * client-side verification.
100
+ */
101
+ export declare const MAX_DEPOSIT_WINDOW_SEC: number;
102
+ /** Maximum proof age in seconds (1 hour) -- proofs older than this are rejected server-side */
103
+ export declare const MAX_PROOF_AGE_SEC = 3600;
104
+ /** Grace period for rotated provider sets in seconds (24 hours) */
105
+ export declare const PROVIDER_SET_GRACE_PERIOD_SEC = 86400;
106
+ /** Ethereum address length including 0x prefix */
107
+ export declare const ETH_ADDRESS_LENGTH = 42;
108
+ /** Max slippage in basis points */
109
+ export declare const MAX_SLIPPAGE_BPS: number;
110
+ /** Multipliers for attestation scoring within a category */
111
+ export declare const ATTESTATION_MULTIPLIERS: {
112
+ readonly first: 1;
113
+ readonly second: 0.25;
114
+ readonly subsequent: 0.1;
115
+ };
116
+ /** Default query limit for paginated API responses */
117
+ export declare const DEFAULT_QUERY_LIMIT = 50;
118
+ /** Maximum query limit for announcement scanning */
119
+ export declare const MAX_ANNOUNCEMENT_LIMIT = 1000;
120
+ /** Default announcement query limit */
121
+ export declare const DEFAULT_ANNOUNCEMENT_LIMIT = 100;
122
+ /** Rate limit window in milliseconds (15 minutes) */
123
+ export declare const RATE_LIMIT_WINDOW_MS: number;
124
+ /** Maximum rate-limited attempts per window */
125
+ export declare const RATE_LIMIT_MAX_ATTEMPTS = 5;
126
+ /** JWT token expiry in hours */
127
+ export declare const JWT_EXPIRY_HOURS = 4;
128
+ /** Tier proof expiry in milliseconds (7 days) */
129
+ export declare const TIER_PROOF_EXPIRY_MS: number;
130
+ /** Max rate limit store size before cleanup */
131
+ export declare const RATE_LIMIT_STORE_MAX = 10000;
132
+ /** Base delay for failed auth in milliseconds */
133
+ export declare const AUTH_FAILURE_BASE_DELAY_MS = 500;
134
+ /** Max delay for failed auth in milliseconds */
135
+ export declare const AUTH_FAILURE_MAX_DELAY_MS = 5000;
136
+ /** Max demo code length */
137
+ export declare const MAX_DEMO_CODE_LENGTH = 100;
138
+ /** Mock spread factor (1% spread = 99/100) */
139
+ export declare const MOCK_SPREAD_NUMERATOR = 99n;
140
+ export declare const MOCK_SPREAD_DENOMINATOR = 100n;
141
+ /** All trading constants in one object */
142
+ export declare const TRADING: {
143
+ readonly defaultSlippageBps: 50;
144
+ readonly slippageOptions: readonly [10, 50, 100, 200];
145
+ readonly quoteExpirySec: 30;
146
+ readonly maxQuoteRetries: 3;
147
+ readonly maxTokenDecimals: 18;
148
+ readonly minTradeUsd: 1;
149
+ readonly maxSlippagePercent: 50;
150
+ readonly minSlippagePercent: 0.01;
151
+ readonly maxPriceImpactPercent: 50;
152
+ readonly shieldedMinScore: 50;
153
+ };
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /**
3
+ * Trading Constants
4
+ *
5
+ * Shared trading configuration for slippage, quotes, and limits.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.TRADING = exports.MOCK_SPREAD_DENOMINATOR = exports.MOCK_SPREAD_NUMERATOR = exports.MAX_DEMO_CODE_LENGTH = exports.AUTH_FAILURE_MAX_DELAY_MS = exports.AUTH_FAILURE_BASE_DELAY_MS = exports.RATE_LIMIT_STORE_MAX = exports.TIER_PROOF_EXPIRY_MS = exports.JWT_EXPIRY_HOURS = exports.RATE_LIMIT_MAX_ATTEMPTS = exports.RATE_LIMIT_WINDOW_MS = exports.DEFAULT_ANNOUNCEMENT_LIMIT = exports.MAX_ANNOUNCEMENT_LIMIT = exports.DEFAULT_QUERY_LIMIT = exports.ATTESTATION_MULTIPLIERS = exports.MAX_SLIPPAGE_BPS = exports.ETH_ADDRESS_LENGTH = exports.PROVIDER_SET_GRACE_PERIOD_SEC = exports.MAX_PROOF_AGE_SEC = exports.MAX_DEPOSIT_WINDOW_SEC = exports.MAX_QUOTE_DEADLINE_PAST_SEC = exports.MAX_QUOTE_DEADLINE_FUTURE_SEC = exports.MAX_DEADLINE_OFFSET_SEC = exports.BPS_DENOMINATOR = exports.TRUST_THRESHOLDS = exports.SHIELDED_MIN_SCORE = exports.MAX_PRICE_IMPACT_PERCENT = exports.MIN_SLIPPAGE_PERCENT = exports.MAX_SLIPPAGE_PERCENT = exports.MIN_TRADE_USD = exports.MAX_TOKEN_DECIMALS = exports.MAX_QUOTE_RETRIES = exports.QUOTE_EXPIRY_SEC = exports.SLIPPAGE_PRESETS = exports.SLIPPAGE_OPTIONS = exports.DEFAULT_SLIPPAGE_BPS = void 0;
9
+ // ============================================================
10
+ // Slippage Configuration
11
+ // ============================================================
12
+ /** Default slippage in basis points (0.5%) */
13
+ exports.DEFAULT_SLIPPAGE_BPS = 50;
14
+ /** Available slippage options in basis points */
15
+ exports.SLIPPAGE_OPTIONS = [10, 50, 100, 200];
16
+ /** Slippage presets with labels */
17
+ exports.SLIPPAGE_PRESETS = {
18
+ low: 10, // 0.1%
19
+ medium: 50, // 0.5%
20
+ high: 100, // 1%
21
+ };
22
+ // ============================================================
23
+ // Quote Configuration
24
+ // ============================================================
25
+ /** Quote expiry in seconds */
26
+ exports.QUOTE_EXPIRY_SEC = 30;
27
+ /** Maximum retries for failed quotes */
28
+ exports.MAX_QUOTE_RETRIES = 3;
29
+ // ============================================================
30
+ // Trading Limits
31
+ // ============================================================
32
+ /** Maximum decimal places for token amounts */
33
+ exports.MAX_TOKEN_DECIMALS = 18;
34
+ /** Minimum trade amount in USD */
35
+ exports.MIN_TRADE_USD = 1;
36
+ /** Maximum slippage percentage */
37
+ exports.MAX_SLIPPAGE_PERCENT = 50;
38
+ /** Minimum slippage percentage */
39
+ exports.MIN_SLIPPAGE_PERCENT = 0.01;
40
+ /**
41
+ * Hard ceiling on how far below market a fill may be before execution is
42
+ * refused. This is all-in value loss (fees + fixed network minimum + market
43
+ * slippage) versus the live market rate, not the user's configurable slippage
44
+ * tolerance. Protects users from executing trades -- typically dust orders --
45
+ * where costs eat more than half the notional.
46
+ */
47
+ exports.MAX_PRICE_IMPACT_PERCENT = 50;
48
+ // ============================================================
49
+ // Trust Thresholds
50
+ // ============================================================
51
+ /**
52
+ * Minimum trust score for Aztec L2 shielded settlement (the Private privacy
53
+ * level). L1 stealth (ERC-5564) is NOT trust-gated: privacy is free by default,
54
+ * so any wallet can settle to a one-time stealth address and simply pays its own
55
+ * trust-tier fee. Only the deeper Aztec-shielded tiers -- Private (50+) and
56
+ * Sovereign (75+), both reached via pxe-bridge -- require trust.
57
+ */
58
+ exports.SHIELDED_MIN_SCORE = 50;
59
+ /** Trust score thresholds by tier */
60
+ exports.TRUST_THRESHOLDS = {
61
+ trusted: 25,
62
+ verified: 50,
63
+ premium: 75,
64
+ institutional: 100,
65
+ };
66
+ // ============================================================
67
+ // Numeric Constants
68
+ // ============================================================
69
+ /** Basis points denominator (1 bps = 0.01%) */
70
+ exports.BPS_DENOMINATOR = 10_000;
71
+ /** Maximum deadline offset in seconds (1 hour) */
72
+ exports.MAX_DEADLINE_OFFSET_SEC = 3600;
73
+ /**
74
+ * Server-side ceiling on a quote request's `deadline`, in seconds from now.
75
+ *
76
+ * Deliberately looser than MAX_DEADLINE_OFFSET_SEC, which is the CLIENT's rule
77
+ * (src/utils/validators.ts) and the value the frontend actually sends. This is
78
+ * the outer bound the API will accept from anyone, sized so an agent or a device
79
+ * with a skewed clock is not rejected for asking a reasonable question.
80
+ *
81
+ * The reason there is a ceiling at all: `deadline` is what the user signs into
82
+ * the intent EIP-712, so an unbounded value is an authorization with no
83
+ * expiry -- and `decideReconcile` treats "past the signed deadline" as the point
84
+ * where an in-doubt intent can no longer move funds.
85
+ */
86
+ exports.MAX_QUOTE_DEADLINE_FUTURE_SEC = 24 * 3600;
87
+ /**
88
+ * How far in the PAST a quote `deadline` may be and still be accepted, in
89
+ * seconds. Pure clock-skew tolerance: no correct client asks for a deadline that
90
+ * has already passed, but one whose clock is behind might.
91
+ *
92
+ * A past-dated deadline is not inert data: the abandoned-deposit sweep reads
93
+ * `deadline` to decide when a deposit route has died, so `deadline: 1` would
94
+ * mean "terminalise this live route on the next cron tick". The sweep gates on
95
+ * the server-set `created_at` too; this is the other half: the value never
96
+ * gets stored in the first place.
97
+ */
98
+ exports.MAX_QUOTE_DEADLINE_PAST_SEC = 3600;
99
+ /**
100
+ * Sanity bound on the SOLVER-supplied `deposit_deadline`, in seconds from the
101
+ * quote, applied only when Xochi persists it.
102
+ *
103
+ * The client treats this field as untrusted: `POLL_CEILING_MAX_MS` exists
104
+ * because a malformed or hostile `deposit_deadline` far in the future must not
105
+ * turn the poll into a permanent background request loop. Persisting it makes
106
+ * the server trust it more than the browser does, and it is the PRIMARY gate
107
+ * on the abandoned-deposit sweep (`COALESCE(deposit_deadline, deadline)`), so
108
+ * one bad value -- a misparsed `XOCHI_DEPOSIT_DEADLINE_SECONDS`, a solver bug
109
+ * -- would make every deposit row permanently and silently unsweepable.
110
+ *
111
+ * Riddler's default is 1800s, so this is two orders of magnitude of headroom: it
112
+ * rejects nothing a correctly configured solver can mint. A value outside it is
113
+ * stored as NULL, which degrades the sweep to the quote `deadline` rather than
114
+ * to no gate at all. The value SERVED to the client is never altered -- it is
115
+ * covered by the deposit attestation signature, so changing it would break
116
+ * client-side verification.
117
+ */
118
+ exports.MAX_DEPOSIT_WINDOW_SEC = 24 * 3600;
119
+ /** Maximum proof age in seconds (1 hour) -- proofs older than this are rejected server-side */
120
+ exports.MAX_PROOF_AGE_SEC = 3600;
121
+ /** Grace period for rotated provider sets in seconds (24 hours) */
122
+ exports.PROVIDER_SET_GRACE_PERIOD_SEC = 86400;
123
+ /** Ethereum address length including 0x prefix */
124
+ exports.ETH_ADDRESS_LENGTH = 42;
125
+ /** Max slippage in basis points */
126
+ exports.MAX_SLIPPAGE_BPS = exports.MAX_SLIPPAGE_PERCENT * 100;
127
+ // ============================================================
128
+ // Attestation Diminishing Returns
129
+ // ============================================================
130
+ /** Multipliers for attestation scoring within a category */
131
+ exports.ATTESTATION_MULTIPLIERS = {
132
+ first: 1.0,
133
+ second: 0.25,
134
+ subsequent: 0.1,
135
+ };
136
+ // ============================================================
137
+ // Query Limits
138
+ // ============================================================
139
+ /** Default query limit for paginated API responses */
140
+ exports.DEFAULT_QUERY_LIMIT = 50;
141
+ /** Maximum query limit for announcement scanning */
142
+ exports.MAX_ANNOUNCEMENT_LIMIT = 1000;
143
+ /** Default announcement query limit */
144
+ exports.DEFAULT_ANNOUNCEMENT_LIMIT = 100;
145
+ // ============================================================
146
+ // Auth & Rate Limiting
147
+ // ============================================================
148
+ /** Rate limit window in milliseconds (15 minutes) */
149
+ exports.RATE_LIMIT_WINDOW_MS = 15 * 60 * 1000;
150
+ /** Maximum rate-limited attempts per window */
151
+ exports.RATE_LIMIT_MAX_ATTEMPTS = 5;
152
+ /** JWT token expiry in hours */
153
+ exports.JWT_EXPIRY_HOURS = 4;
154
+ /** Tier proof expiry in milliseconds (7 days) */
155
+ exports.TIER_PROOF_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000;
156
+ /** Max rate limit store size before cleanup */
157
+ exports.RATE_LIMIT_STORE_MAX = 10_000;
158
+ /** Base delay for failed auth in milliseconds */
159
+ exports.AUTH_FAILURE_BASE_DELAY_MS = 500;
160
+ /** Max delay for failed auth in milliseconds */
161
+ exports.AUTH_FAILURE_MAX_DELAY_MS = 5000;
162
+ /** Max demo code length */
163
+ exports.MAX_DEMO_CODE_LENGTH = 100;
164
+ // ============================================================
165
+ // Mock Spread
166
+ // ============================================================
167
+ /** Mock spread factor (1% spread = 99/100) */
168
+ exports.MOCK_SPREAD_NUMERATOR = 99n;
169
+ exports.MOCK_SPREAD_DENOMINATOR = 100n;
170
+ // ============================================================
171
+ // Bundled Trading Config
172
+ // ============================================================
173
+ /** All trading constants in one object */
174
+ exports.TRADING = {
175
+ defaultSlippageBps: exports.DEFAULT_SLIPPAGE_BPS,
176
+ slippageOptions: exports.SLIPPAGE_OPTIONS,
177
+ quoteExpirySec: exports.QUOTE_EXPIRY_SEC,
178
+ maxQuoteRetries: exports.MAX_QUOTE_RETRIES,
179
+ maxTokenDecimals: exports.MAX_TOKEN_DECIMALS,
180
+ minTradeUsd: exports.MIN_TRADE_USD,
181
+ maxSlippagePercent: exports.MAX_SLIPPAGE_PERCENT,
182
+ minSlippagePercent: exports.MIN_SLIPPAGE_PERCENT,
183
+ maxPriceImpactPercent: exports.MAX_PRICE_IMPACT_PERCENT,
184
+ shieldedMinScore: exports.SHIELDED_MIN_SCORE,
185
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared Types
3
+ *
4
+ * Type definitions used across frontend and backend.
5
+ */
6
+ /** Lowercase tier id used on the wire + in the fee breakdown. */
7
+ export type Tier = "standard" | "trusted" | "verified" | "premium" | "institutional";
8
+ /**
9
+ * Itemized fee breakdown for a quote (economics.md Part 1, "Frontend display spec").
10
+ * Three additive layers on a never-discounted cost floor: solver spread + gas
11
+ * floor (Riddler), venue fee (Xochi), routing fee (Raxol -> buyback). Amounts are
12
+ * destination-token smallest units (strings); rates are bps. Discounts carve
13
+ * venue + routing only, never the solver floor.
14
+ */
15
+ export interface FeeBreakdown {
16
+ tier: Tier;
17
+ assetClass: "stable" | "volatile";
18
+ /** Guaranteed minimum, never discounted (Riddler). */
19
+ gasFloor: {
20
+ amount: string;
21
+ usd: string;
22
+ };
23
+ solverFeeBps: number;
24
+ solverFee: string;
25
+ /** > 0 only on large-vs-depth fills; render as a warning, not a fee. */
26
+ priceImpactBps: number;
27
+ priceImpact: string;
28
+ venueFeeBps: number;
29
+ venueFee: string;
30
+ routingFeeBps: number;
31
+ routingFee: string;
32
+ totalBps: number;
33
+ totalFee: string;
34
+ totalUsd: string;
35
+ /** 15 -- solver upside on positive slippage; the user keeps the other 85%. */
36
+ surplusSharePct: number;
37
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * Shared Types
4
+ *
5
+ * Type definitions used across frontend and backend.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Validators
3
+ *
4
+ * Address validators: EVM hex20 and checksummed Tron base58.
5
+ */
6
+ /** Ethereum address regex pattern */
7
+ export declare const ETH_ADDRESS_REGEX: RegExp;
8
+ /** Zero address constant */
9
+ export declare const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
10
+ /** Check if string is a valid Ethereum address */
11
+ export declare function isValidAddress(address: string): boolean;
12
+ /**
13
+ * Tron mainnet address shape: 34 chars, leading "T", base58 alphabet (no
14
+ * 0/O/I/l). Shape alone is NOT sufficient to accept an address -- see
15
+ * `isValidTronAddress`, which also verifies the base58check checksum.
16
+ */
17
+ export declare const TRON_ADDRESS_REGEX: RegExp;
18
+ /**
19
+ * Validate a Tron address, checksum included.
20
+ *
21
+ * The shape regex alone accepts any typo that keeps the length and alphabet,
22
+ * which is nearly all of them. Base58check carries a 4-byte checksum, so a
23
+ * mistyped address survives it with probability about 1 in 4 billion. That
24
+ * matters here specifically because a Tron payout recipient is typed by hand on
25
+ * an EVM-origin swap: there is no Tron wallet in the flow to catch it later, and
26
+ * a TRC-20 transfer to a well-formed but unowned address is irrecoverable.
27
+ *
28
+ * Checksum = first 4 bytes of sha256(sha256(0x41 || address_bytes)).
29
+ */
30
+ export declare function isValidTronAddress(address: string): boolean;