otto-intel-mcp 0.1.0
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/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/adapter/cdp-signer.d.ts +133 -0
- package/dist/adapter/cdp-signer.js +356 -0
- package/dist/adapter/cdp-signer.js.map +1 -0
- package/dist/adapter/chain.d.ts +36 -0
- package/dist/adapter/chain.js +65 -0
- package/dist/adapter/chain.js.map +1 -0
- package/dist/adapter/erc20.d.ts +39 -0
- package/dist/adapter/erc20.js +17 -0
- package/dist/adapter/erc20.js.map +1 -0
- package/dist/adapter/lifi-decode.d.ts +52 -0
- package/dist/adapter/lifi-decode.js +149 -0
- package/dist/adapter/lifi-decode.js.map +1 -0
- package/dist/adapter/refusal.d.ts +21 -0
- package/dist/adapter/refusal.js +55 -0
- package/dist/adapter/refusal.js.map +1 -0
- package/dist/adapter/sent-step.d.ts +15 -0
- package/dist/adapter/sent-step.js +6 -0
- package/dist/adapter/sent-step.js.map +1 -0
- package/dist/adapter/verify.d.ts +149 -0
- package/dist/adapter/verify.js +432 -0
- package/dist/adapter/verify.js.map +1 -0
- package/dist/adapter-cdp-index.d.ts +6 -0
- package/dist/adapter-cdp-index.js +7 -0
- package/dist/adapter-index.d.ts +15 -0
- package/dist/adapter-index.js +15 -0
- package/dist/artifact-id.d.ts +16 -0
- package/dist/artifact-id.js +60 -0
- package/dist/boot-redaction.d.ts +16 -0
- package/dist/boot-redaction.js +54 -0
- package/dist/data-source.d.ts +7 -0
- package/dist/data-source.js +2 -0
- package/dist/errors.d.ts +70 -0
- package/dist/errors.js +221 -0
- package/dist/execution-config.d.ts +232 -0
- package/dist/execution-config.js +443 -0
- package/dist/execution-delegated-definition.d.ts +165 -0
- package/dist/execution-delegated-definition.js +116 -0
- package/dist/execution-delegation-admin-definition.d.ts +208 -0
- package/dist/execution-delegation-admin-definition.js +170 -0
- package/dist/execution-delegation-admin.d.ts +74 -0
- package/dist/execution-delegation-admin.js +290 -0
- package/dist/execution-delegation-policy.d.ts +257 -0
- package/dist/execution-delegation-policy.js +279 -0
- package/dist/execution-delegation.d.ts +190 -0
- package/dist/execution-delegation.js +545 -0
- package/dist/execution-errors.d.ts +9 -0
- package/dist/execution-errors.js +128 -0
- package/dist/execution-index.d.ts +17 -0
- package/dist/execution-index.js +16 -0
- package/dist/execution-intent.d.ts +14 -0
- package/dist/execution-intent.js +36 -0
- package/dist/execution-registration.d.ts +113 -0
- package/dist/execution-registration.js +161 -0
- package/dist/execution-tool-definitions.d.ts +1103 -0
- package/dist/execution-tool-definitions.js +1051 -0
- package/dist/execution-tools.d.ts +85 -0
- package/dist/execution-tools.js +690 -0
- package/dist/execution-types.d.ts +274 -0
- package/dist/execution-types.js +157 -0
- package/dist/hyperliquid-info-client.d.ts +31 -0
- package/dist/hyperliquid-info-client.js +121 -0
- package/dist/hyperliquid-order-assertions.d.ts +89 -0
- package/dist/hyperliquid-order-assertions.js +325 -0
- package/dist/hyperliquid-order-builder.d.ts +47 -0
- package/dist/hyperliquid-order-builder.js +55 -0
- package/dist/lifi-bridge-assertions.d.ts +49 -0
- package/dist/lifi-bridge-assertions.js +303 -0
- package/dist/lifi-execution-client.d.ts +74 -0
- package/dist/lifi-execution-client.js +165 -0
- package/dist/lifi-fee-assertions.d.ts +32 -0
- package/dist/lifi-fee-assertions.js +103 -0
- package/dist/lifi-swap-assertions.d.ts +32 -0
- package/dist/lifi-swap-assertions.js +214 -0
- package/dist/local-config.d.ts +9 -0
- package/dist/local-config.js +45 -0
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +44 -0
- package/dist/polymarket-clob-client.d.ts +73 -0
- package/dist/polymarket-clob-client.js +186 -0
- package/dist/polymarket-order-assertions.d.ts +60 -0
- package/dist/polymarket-order-assertions.js +273 -0
- package/dist/polymarket-order-builder.d.ts +43 -0
- package/dist/polymarket-order-builder.js +74 -0
- package/dist/prepared-artifacts.d.ts +45 -0
- package/dist/prepared-artifacts.js +92 -0
- package/dist/stdio-main.d.ts +2 -0
- package/dist/stdio-main.js +20 -0
- package/dist/tool-definitions.d.ts +20 -0
- package/dist/tool-definitions.js +162 -0
- package/dist/x402-read-source.d.ts +11 -0
- package/dist/x402-read-source.js +77 -0
- package/package.json +162 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verify.ts — the adapter core: what the user's signer checks BEFORE it signs anything.
|
|
3
|
+
*
|
|
4
|
+
* Model B (docs/0808_model_b_scope_and_verdict.md, spec docs/0816_step1_execution_mcp_spec_draft.md):
|
|
5
|
+
* Otto constructs; the user's own custody signs and submits; Otto's fee is attribution baked into
|
|
6
|
+
* the constructed payload. The constructor is prepare-only and asserts what it built. This module is
|
|
7
|
+
* the OTHER half of spec §8 — "the consuming adapter re-checks digest + expected signer before
|
|
8
|
+
* signing" — extended to a full adapter-side policy, because an artifact's own digests prove
|
|
9
|
+
* integrity relative to the artifact's own claims and nothing more: an inconsistent artifact with
|
|
10
|
+
* correctly recomputed digests is still inconsistent. The checks run in three layers:
|
|
11
|
+
*
|
|
12
|
+
* 1. INTEGRITY — the constructor's own assertions, unchanged (schema, payload digest, whole-envelope
|
|
13
|
+
* commitment + freshness, account binding).
|
|
14
|
+
* 2. INTENT — the artifact is the trade the USER asked for: the approved token and amount ARE the
|
|
15
|
+
* intent's, the deposit legs pull exactly that amount, the output token IS the intent's, and the
|
|
16
|
+
* on-chain minimum output is at least the caller's own floor (> 0). The caller owns the floor;
|
|
17
|
+
* it never comes from the artifact. This is the fence for the swap legs themselves: LI.FI's
|
|
18
|
+
* GenericSwapV3 reverts unless `_receiver` gets at least `_minAmount` of the output token, so an
|
|
19
|
+
* internal DEX leg is bounded on-chain by the receiver + minimum-output binds rather than by an
|
|
20
|
+
* allowlist of every DEX contract (which this adapter deliberately does not attempt to hold).
|
|
21
|
+
* 3. POLICY — capability contract, chain, plan shape, approvals only to the reviewed spender
|
|
22
|
+
* that IS the call target, `_receiver` == this account, and the fee leg decoded with the
|
|
23
|
+
* adapter's own decoder and matched against the RULED attribution constants (10 bps to Otto's
|
|
24
|
+
* recipient, only the reviewed FeeForwarder deployments, nothing to strangers, aggregate under
|
|
25
|
+
* the cap).
|
|
26
|
+
*
|
|
27
|
+
* P0 LIVENESS binds here exactly as it binds the constructor (spec §7.0): a valid artifact for the
|
|
28
|
+
* stated intent MUST pass. The test suite's first test is that property; the mutation matrix proves
|
|
29
|
+
* every refusal reddens. Every check that passes is recorded by id so the sitting's evidence shows
|
|
30
|
+
* what was verified, not just that "verification passed".
|
|
31
|
+
*/
|
|
32
|
+
import { decodeFunctionData, getAddress, parseAbi } from 'viem';
|
|
33
|
+
import { DEFAULT_LIFI_AGGREGATE_FEE_CAP_RATE, DEFAULT_LIFI_FEE_BPS, DEFAULT_LIFI_FEE_CONFIG_VERSION, DEFAULT_LIFI_PLATFORM_FEE_RECIPIENT, DEFAULT_OTTO_LIFI_FEE_RECIPIENT, EXECUTION_ARTIFACT_SCHEMA, ExecutionRefusal, LIFI_DIAMOND_ADDRESS, LIFI_INTEGRATOR, assertArtifactAccountBinding, assertArtifactFresh, assertArtifactPayloadDigest, reviewedLiFiFeeForwarders, swapIntentDigestInput, executionDigest, } from '../execution-index.js';
|
|
34
|
+
import { COLLECT_TOKEN_FEES_SELECTOR, decodeCollectTokenFees, decodeGenericSwap, } from './lifi-decode.js';
|
|
35
|
+
import { isAdapterRefusalCode, refuse } from './refusal.js';
|
|
36
|
+
const ERC20_APPROVE_ABI = parseAbi([
|
|
37
|
+
'function approve(address spender, uint256 amount) returns (bool)',
|
|
38
|
+
]);
|
|
39
|
+
function bpsOfRate(rate) {
|
|
40
|
+
// '0.0025' → 25 bps. Exact decimal arithmetic on the ruled constant; no floats.
|
|
41
|
+
const match = /^0\.(\d{1,4})$/.exec(rate);
|
|
42
|
+
if (!match)
|
|
43
|
+
throw new Error(`aggregate fee cap rate ${rate} is not a 4-decimal fraction`);
|
|
44
|
+
return BigInt(match[1].padEnd(4, '0'));
|
|
45
|
+
}
|
|
46
|
+
/** The reviewed v1 policy for a same-chain LI.FI swap on `chainId`. */
|
|
47
|
+
export function defaultAdapterPolicy(chainId) {
|
|
48
|
+
const forwarders = reviewedLiFiFeeForwarders(chainId);
|
|
49
|
+
if (!forwarders || forwarders.length === 0)
|
|
50
|
+
throw new Error(`chain ${chainId} has no reviewed LI.FI FeeForwarder`);
|
|
51
|
+
return Object.freeze({
|
|
52
|
+
allowedCallTargets: Object.freeze([LIFI_DIAMOND_ADDRESS]),
|
|
53
|
+
allowedApproveSpenders: Object.freeze([LIFI_DIAMOND_ADDRESS]),
|
|
54
|
+
attribution: Object.freeze({
|
|
55
|
+
configVersion: DEFAULT_LIFI_FEE_CONFIG_VERSION,
|
|
56
|
+
feeBps: BigInt(DEFAULT_LIFI_FEE_BPS),
|
|
57
|
+
integrator: LIFI_INTEGRATOR,
|
|
58
|
+
ottoRecipient: DEFAULT_OTTO_LIFI_FEE_RECIPIENT,
|
|
59
|
+
otherAllowedRecipients: Object.freeze([DEFAULT_LIFI_PLATFORM_FEE_RECIPIENT]),
|
|
60
|
+
forwarders,
|
|
61
|
+
aggregateCapBps: bpsOfRate(DEFAULT_LIFI_AGGREGATE_FEE_CAP_RATE),
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** Run one of the constructor's own assertions; surface its refusal code unchanged. */
|
|
66
|
+
function integrity(fn) {
|
|
67
|
+
try {
|
|
68
|
+
return fn();
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error instanceof ExecutionRefusal && isAdapterRefusalCode(error.code)) {
|
|
72
|
+
refuse(error.code, error.message);
|
|
73
|
+
}
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function includesAddress(list, candidate) {
|
|
78
|
+
const target = getAddress(candidate);
|
|
79
|
+
return list.some((entry) => getAddress(entry) === target);
|
|
80
|
+
}
|
|
81
|
+
export function verifySwapEnvelope(raw, expected) {
|
|
82
|
+
const checks = [];
|
|
83
|
+
const pass = (id) => {
|
|
84
|
+
checks.push(Object.freeze({ id, result: 'passed' }));
|
|
85
|
+
};
|
|
86
|
+
const account = getAddress(expected.profile.address);
|
|
87
|
+
const policy = expected.policy;
|
|
88
|
+
const intent = Object.freeze({
|
|
89
|
+
fromToken: getAddress(expected.intent.fromToken),
|
|
90
|
+
toToken: getAddress(expected.intent.toToken),
|
|
91
|
+
fromAmount: expected.intent.fromAmount,
|
|
92
|
+
minAmountOut: expected.intent.minAmountOut,
|
|
93
|
+
});
|
|
94
|
+
if (intent.fromAmount <= 0n)
|
|
95
|
+
refuse('INTENT_MISMATCH', 'intent input amount must be positive');
|
|
96
|
+
if (intent.minAmountOut <= 0n) {
|
|
97
|
+
refuse('MIN_OUTPUT_TOO_LOW', 'the caller must supply a positive minimum output; a zero floor lets a swap deliver nothing');
|
|
98
|
+
}
|
|
99
|
+
if (intent.fromToken === intent.toToken)
|
|
100
|
+
refuse('INTENT_MISMATCH', 'intent input and output tokens are the same');
|
|
101
|
+
// 1. Schema — the constructor's own output contract, parsed strictly.
|
|
102
|
+
const parsed = EXECUTION_ARTIFACT_SCHEMA.safeParse(raw);
|
|
103
|
+
if (!parsed.success) {
|
|
104
|
+
refuse('SCHEMA_INVALID', `artifact does not satisfy the execution envelope schema: ${parsed.error.issues[0]?.message ?? 'unknown issue'}`);
|
|
105
|
+
}
|
|
106
|
+
if (parsed.data.rail !== 'lifi' || parsed.data.operation !== 'swap') {
|
|
107
|
+
refuse('UNSUPPORTED_OPERATION', `this adapter signs lifi/swap artifacts only, got ${parsed.data.rail}/${parsed.data.operation}`);
|
|
108
|
+
}
|
|
109
|
+
// A fresh tree: the verified plan is deep-frozen below, and that must never reach the caller's object.
|
|
110
|
+
const artifact = structuredClone(parsed.data);
|
|
111
|
+
pass('schema');
|
|
112
|
+
if (expected.prepareInput !== undefined) {
|
|
113
|
+
let normalized;
|
|
114
|
+
try {
|
|
115
|
+
normalized = swapIntentDigestInput(expected.prepareInput);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
refuse('INTENT_MISMATCH', `prepareInput is not a valid otto_prepare_swap request: ${error instanceof Error ? error.message.slice(0, 120) : String(error)}`);
|
|
119
|
+
}
|
|
120
|
+
const expectedDigest = executionDigest(normalized);
|
|
121
|
+
if (artifact.intent_digest !== expectedDigest) {
|
|
122
|
+
refuse('INTENT_MISMATCH', 'artifact intent_digest does not match the request the caller sent');
|
|
123
|
+
}
|
|
124
|
+
pass('intent_digest');
|
|
125
|
+
}
|
|
126
|
+
// 2–3. Payload digest, whole-envelope commitment, freshness — constructor assertions, unchanged.
|
|
127
|
+
integrity(() => assertArtifactPayloadDigest(artifact));
|
|
128
|
+
pass('payload_digest');
|
|
129
|
+
integrity(() => assertArtifactFresh(artifact, expected.nowMs ?? Date.now()));
|
|
130
|
+
pass('commitment');
|
|
131
|
+
pass('freshness');
|
|
132
|
+
// 4. Account binding: the artifact was built for THIS account, in every protocol role.
|
|
133
|
+
integrity(() => assertArtifactAccountBinding(artifact, expected.profile));
|
|
134
|
+
const roles = artifact.account_binding.protocol_roles.lifi;
|
|
135
|
+
if (getAddress(roles.sender) !== account || getAddress(roles.receiver) !== account) {
|
|
136
|
+
refuse('ACCOUNT_BINDING_MISMATCH', 'lifi sender/receiver role is not the signing account');
|
|
137
|
+
}
|
|
138
|
+
pass('account_binding');
|
|
139
|
+
// 5. Capability contract (spec §9): a missing capability refuses the WHOLE plan.
|
|
140
|
+
const missing = artifact.required_capabilities.filter((capability) => !expected.capabilities.includes(capability));
|
|
141
|
+
if (missing.length > 0) {
|
|
142
|
+
refuse('CAPABILITY_MISSING', `artifact requires ${missing.join(', ')}; this signer provides ${expected.capabilities.join(', ')}`);
|
|
143
|
+
}
|
|
144
|
+
pass('capabilities');
|
|
145
|
+
// 6. Chain: payload, submission and profile agree.
|
|
146
|
+
if (artifact.payload.chain_id !== expected.profile.chainId ||
|
|
147
|
+
artifact.submission.chain_id !== expected.profile.chainId) {
|
|
148
|
+
refuse('CHAIN_MISMATCH', 'artifact chain does not match the signing account chain');
|
|
149
|
+
}
|
|
150
|
+
pass('chain');
|
|
151
|
+
// 7. Plan shape: exactly approve_reset → approve → call; every step's native value parses to zero
|
|
152
|
+
// (the signer's own contract: it signs `value` verbatim, and an ERC20 swap moves no ETH).
|
|
153
|
+
const rawSteps = artifact.payload.steps;
|
|
154
|
+
if (rawSteps.length !== 3 ||
|
|
155
|
+
rawSteps[0]?.kind !== 'approve_reset' ||
|
|
156
|
+
rawSteps[1]?.kind !== 'approve' ||
|
|
157
|
+
rawSteps[2]?.kind !== 'call') {
|
|
158
|
+
refuse('PLAN_SHAPE_INVALID', 'swap plan must be exactly approve_reset, approve, call');
|
|
159
|
+
}
|
|
160
|
+
const steps = rawSteps.map((step) => {
|
|
161
|
+
if (!/^\d+$/.test(step.value) || BigInt(step.value) !== 0n) {
|
|
162
|
+
refuse('PLAN_SHAPE_INVALID', `step ${step.kind} must carry zero native value, got ${step.value}`);
|
|
163
|
+
}
|
|
164
|
+
return Object.freeze({ kind: step.kind, to: getAddress(step.to), value: 0n, data: step.data });
|
|
165
|
+
});
|
|
166
|
+
const [reset, approve, call] = steps;
|
|
167
|
+
pass('plan_shape');
|
|
168
|
+
// 8. Approvals: the INTENT's token, reset-to-zero then EXACTLY the intent amount, one reviewed
|
|
169
|
+
// spender that IS the call target (which must itself be a reviewed target).
|
|
170
|
+
const token = reset.to;
|
|
171
|
+
if (token !== intent.fromToken)
|
|
172
|
+
refuse('INTENT_MISMATCH', `plan approves ${token}, intent input token is ${intent.fromToken}`);
|
|
173
|
+
if (approve.to !== token)
|
|
174
|
+
refuse('APPROVAL_INVALID', 'approve_reset and approve target different tokens');
|
|
175
|
+
const decodeApprove = (data) => {
|
|
176
|
+
try {
|
|
177
|
+
const decoded = decodeFunctionData({ abi: ERC20_APPROVE_ABI, data });
|
|
178
|
+
return { spender: getAddress(decoded.args[0]), amount: decoded.args[1] };
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return refuse('APPROVAL_INVALID', 'approval step is not ERC20 approve(spender, amount)');
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
const decodedReset = decodeApprove(reset.data);
|
|
185
|
+
const decodedApprove = decodeApprove(approve.data);
|
|
186
|
+
if (decodedReset.amount !== 0n)
|
|
187
|
+
refuse('APPROVAL_INVALID', 'approve_reset must approve zero');
|
|
188
|
+
if (decodedApprove.amount !== intent.fromAmount) {
|
|
189
|
+
refuse('INTENT_MISMATCH', `plan approves ${decodedApprove.amount}, intent amount is ${intent.fromAmount}`);
|
|
190
|
+
}
|
|
191
|
+
if (decodedReset.spender !== decodedApprove.spender) {
|
|
192
|
+
refuse('APPROVAL_INVALID', 'approve_reset and approve name different spenders');
|
|
193
|
+
}
|
|
194
|
+
const spender = decodedApprove.spender;
|
|
195
|
+
if (spender !== call.to)
|
|
196
|
+
refuse('APPROVAL_INVALID', 'approval spender is not the call target');
|
|
197
|
+
if (!includesAddress(policy.allowedApproveSpenders, spender) || !includesAddress(policy.allowedCallTargets, call.to)) {
|
|
198
|
+
refuse('TARGET_NOT_ALLOWED', `${spender} is not a reviewed spender/target`);
|
|
199
|
+
}
|
|
200
|
+
const gross = decodedApprove.amount;
|
|
201
|
+
pass('approvals');
|
|
202
|
+
// 9. Swap calldata, decoded with the adapter's own decoder: the deposit legs pull EXACTLY the
|
|
203
|
+
// intent amount of the intent token, the last leg delivers the intent's output token, and the
|
|
204
|
+
// output lands in THIS account.
|
|
205
|
+
const swap = decodeGenericSwap(call.data);
|
|
206
|
+
if (swap.integrator !== policy.attribution.integrator) {
|
|
207
|
+
refuse('ATTRIBUTION_MISSING', `swap integrator is '${swap.integrator}', expected '${policy.attribution.integrator}'`);
|
|
208
|
+
}
|
|
209
|
+
if (swap.receiver !== account) {
|
|
210
|
+
refuse('RECEIVER_MISMATCH', `swap output would land at ${swap.receiver}, not the signing account`);
|
|
211
|
+
}
|
|
212
|
+
let deposited = 0n;
|
|
213
|
+
for (const leg of swap.legs) {
|
|
214
|
+
if (!leg.requiresDeposit)
|
|
215
|
+
continue;
|
|
216
|
+
if (leg.sendingAssetId !== token) {
|
|
217
|
+
refuse('SWAP_CALLDATA_INVALID', `deposit leg pulls ${leg.sendingAssetId}, not the intent token ${token}`);
|
|
218
|
+
}
|
|
219
|
+
deposited += leg.fromAmount;
|
|
220
|
+
}
|
|
221
|
+
if (deposited !== gross) {
|
|
222
|
+
refuse('SWAP_CALLDATA_INVALID', `deposit legs pull ${deposited} in total; the plan approves and the intent names ${gross}`);
|
|
223
|
+
}
|
|
224
|
+
const lastLeg = swap.legs[swap.legs.length - 1];
|
|
225
|
+
if (lastLeg.receivingAssetId !== intent.toToken) {
|
|
226
|
+
refuse('INTENT_MISMATCH', `swap delivers ${lastLeg.receivingAssetId}, intent output token is ${intent.toToken}`);
|
|
227
|
+
}
|
|
228
|
+
pass('swap_decode');
|
|
229
|
+
// 10. Minimum output: the on-chain floor the router enforces must be at least the caller's own.
|
|
230
|
+
if (swap.minAmount < intent.minAmountOut) {
|
|
231
|
+
refuse('MIN_OUTPUT_TOO_LOW', `swap minimum output ${swap.minAmount} is below the caller's floor ${intent.minAmountOut}`);
|
|
232
|
+
}
|
|
233
|
+
pass('min_output');
|
|
234
|
+
// 11. Attribution: the RULED mechanism + version, and the fee leg pays exactly the ruled amount to Otto.
|
|
235
|
+
if (artifact.fee_attribution.mechanism !== 'lifi_integrator_fee' ||
|
|
236
|
+
artifact.fee_attribution.config_version !== policy.attribution.configVersion) {
|
|
237
|
+
refuse('ATTRIBUTION_MISMATCH', `fee attribution is ${artifact.fee_attribution.mechanism}@${artifact.fee_attribution.config_version}, expected lifi_integrator_fee@${policy.attribution.configVersion}`);
|
|
238
|
+
}
|
|
239
|
+
const reviewedForwarders = policy.attribution.forwarders.map((entry) => getAddress(entry));
|
|
240
|
+
const isReviewed = (address) => reviewedForwarders.includes(getAddress(address));
|
|
241
|
+
const collectorLegs = swap.legs.filter((leg) => isReviewed(leg.callTo) || leg.callData.slice(0, 10).toLowerCase() === COLLECT_TOKEN_FEES_SELECTOR);
|
|
242
|
+
if (collectorLegs.length !== 1) {
|
|
243
|
+
refuse('ATTRIBUTION_MISSING', `expected exactly one fee-forwarding leg, found ${collectorLegs.length}`);
|
|
244
|
+
}
|
|
245
|
+
const feeLeg = collectorLegs[0];
|
|
246
|
+
// The drift alarm, user-side: an unreviewed collector is refused BY ADDRESS.
|
|
247
|
+
if (!isReviewed(feeLeg.callTo) || !isReviewed(feeLeg.approveTo) || feeLeg.callTo !== feeLeg.approveTo) {
|
|
248
|
+
refuse('ATTRIBUTION_MISSING', `fee leg calls ${feeLeg.callTo} (approveTo ${feeLeg.approveTo}), not a reviewed FeeForwarder deployment (reviewed: ${reviewedForwarders.join(', ')})`);
|
|
249
|
+
}
|
|
250
|
+
if (feeLeg.sendingAssetId !== token || feeLeg.receivingAssetId !== token) {
|
|
251
|
+
refuse('ATTRIBUTION_MISMATCH', 'fee leg is denominated in a different token');
|
|
252
|
+
}
|
|
253
|
+
const fees = decodeCollectTokenFees(feeLeg.callData);
|
|
254
|
+
if (fees.token !== token)
|
|
255
|
+
refuse('ATTRIBUTION_MISMATCH', 'fee calldata names a different token');
|
|
256
|
+
const expectedOttoFee = (gross * policy.attribution.feeBps) / 10000n;
|
|
257
|
+
const ottoRecipient = getAddress(policy.attribution.ottoRecipient);
|
|
258
|
+
const ottoDistributions = fees.distributions.filter((d) => d.recipient === ottoRecipient);
|
|
259
|
+
if (ottoDistributions.length !== 1) {
|
|
260
|
+
refuse('ATTRIBUTION_MISSING', `fee leg pays Otto's recipient ${ottoDistributions.length} times, expected exactly once`);
|
|
261
|
+
}
|
|
262
|
+
const ottoFee = ottoDistributions[0].amount;
|
|
263
|
+
if (ottoFee !== expectedOttoFee) {
|
|
264
|
+
refuse('ATTRIBUTION_MISMATCH', `Otto fee ${ottoFee} != ${expectedOttoFee} (${policy.attribution.feeBps} bps of ${gross})`);
|
|
265
|
+
}
|
|
266
|
+
let total = 0n;
|
|
267
|
+
for (const distribution of fees.distributions) {
|
|
268
|
+
total += distribution.amount;
|
|
269
|
+
if (distribution.recipient !== ottoRecipient &&
|
|
270
|
+
!includesAddress(policy.attribution.otherAllowedRecipients, distribution.recipient)) {
|
|
271
|
+
refuse('ATTRIBUTION_MISMATCH', `fee leg pays unrecognised recipient ${distribution.recipient}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (total * 10000n > gross * policy.attribution.aggregateCapBps) {
|
|
275
|
+
refuse('FEE_CAP_EXCEEDED', `aggregate fees ${total} exceed ${policy.attribution.aggregateCapBps} bps of ${gross}`);
|
|
276
|
+
}
|
|
277
|
+
pass('fee_attribution');
|
|
278
|
+
// The verified plan is DEEP-FROZEN, the artifact's nested objects included: a post-verification
|
|
279
|
+
// edit anywhere in it throws (ESM is strict mode) instead of reaching a signer, signers refuse any
|
|
280
|
+
// plan that is not frozen exactly this way (`assertVerifiedPlanIntact`), and what a signer binds to
|
|
281
|
+
// is the verifier's own identity record of the plan, never a field re-read from the plan object.
|
|
282
|
+
return deepFreezeVerified({
|
|
283
|
+
artifact,
|
|
284
|
+
steps: Object.freeze(steps.map((step) => Object.freeze({ ...step }))),
|
|
285
|
+
intent,
|
|
286
|
+
token,
|
|
287
|
+
outputToken: lastLeg.receivingAssetId,
|
|
288
|
+
spender,
|
|
289
|
+
gross,
|
|
290
|
+
receiver: swap.receiver,
|
|
291
|
+
minAmountOut: swap.minAmount,
|
|
292
|
+
ottoFee,
|
|
293
|
+
fees: Object.freeze({ token: fees.token, distributions: Object.freeze(fees.distributions.map((d) => Object.freeze({ ...d }))) }),
|
|
294
|
+
checks: Object.freeze(checks.map((check) => Object.freeze({ ...check }))),
|
|
295
|
+
}, account, expected.profile.chainId);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* PROVENANCE REGISTRY + IDENTITY RECORD: the only objects a signer will accept are the exact objects
|
|
299
|
+
* this module returned (identity, held in a WeakSet that copying, freezing or re-shaping cannot
|
|
300
|
+
* reproduce), and what a signer binds to is the verifier's OWN record of who the plan was verified
|
|
301
|
+
* for — captured at verification, held in a module-private WeakMap, never re-read from the plan. A
|
|
302
|
+
* foreign plan that passes every shape check is refused because it was never verified here; a plan
|
|
303
|
+
* whose nested artifact fields differ from its record (were the deep freeze ever bypassed) is refused
|
|
304
|
+
* because it no longer is what was verified.
|
|
305
|
+
*/
|
|
306
|
+
const VERIFIED_PLANS = new WeakSet();
|
|
307
|
+
const PLAN_IDENTITY = new WeakMap();
|
|
308
|
+
/** Recursively freeze a JSON-shaped value (plain objects and arrays); primitives and bigints pass through. */
|
|
309
|
+
function deepFreeze(value) {
|
|
310
|
+
if (typeof value !== 'object' || value === null)
|
|
311
|
+
return value;
|
|
312
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
313
|
+
deepFreeze(value[key]);
|
|
314
|
+
}
|
|
315
|
+
return Object.freeze(value);
|
|
316
|
+
}
|
|
317
|
+
/** True iff every object reachable from `value` is frozen. */
|
|
318
|
+
export function isDeepFrozen(value) {
|
|
319
|
+
if (typeof value !== 'object' || value === null)
|
|
320
|
+
return true;
|
|
321
|
+
if (!Object.isFrozen(value))
|
|
322
|
+
return false;
|
|
323
|
+
return Reflect.ownKeys(value).every((key) => isDeepFrozen(value[key]));
|
|
324
|
+
}
|
|
325
|
+
function deepFreezeVerified(plan, account, chainId) {
|
|
326
|
+
const frozen = deepFreeze(plan);
|
|
327
|
+
const identity = Object.freeze({
|
|
328
|
+
address: account,
|
|
329
|
+
chainId,
|
|
330
|
+
capabilities: Object.freeze([...frozen.artifact.required_capabilities]),
|
|
331
|
+
validUntilMs: Date.parse(frozen.artifact.valid_until),
|
|
332
|
+
artifactId: frozen.artifact.artifact_id,
|
|
333
|
+
artifactDigest: frozen.artifact.submission.artifact_digest,
|
|
334
|
+
stepTargets: Object.freeze([frozen.steps[0].to, frozen.steps[1].to, frozen.steps[2].to]),
|
|
335
|
+
});
|
|
336
|
+
VERIFIED_PLANS.add(frozen);
|
|
337
|
+
PLAN_IDENTITY.set(frozen, identity);
|
|
338
|
+
return frozen;
|
|
339
|
+
}
|
|
340
|
+
/** True iff `plan` is an object `verifySwapEnvelope` itself returned (identity, not shape). */
|
|
341
|
+
export function isVerifierOutput(plan) {
|
|
342
|
+
return typeof plan === 'object' && plan !== null && VERIFIED_PLANS.has(plan) && PLAN_IDENTITY.has(plan);
|
|
343
|
+
}
|
|
344
|
+
/** The verifier's own record of an intact verified plan (throws for anything else). */
|
|
345
|
+
export function verifiedPlanIdentity(plan) {
|
|
346
|
+
assertVerifiedPlanIntact(plan);
|
|
347
|
+
return PLAN_IDENTITY.get(plan);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* The FULL signer-side gate, called at signer entry and again immediately before every signature or
|
|
351
|
+
* submission: provenance + frozen shape (`assertVerifiedPlanIntact`), then — against the verifier's
|
|
352
|
+
* RECORD of the plan, not the plan's fields — the bound account IS this signer, the chain matches,
|
|
353
|
+
* every required capability is provided, and the plan is still fresh at `nowMs` (the record's
|
|
354
|
+
* deadline AND the artifact's own commitment-bound deadline). A plan verified for another account,
|
|
355
|
+
* another chain, or that has since expired is refused here even though it was genuinely verified.
|
|
356
|
+
*/
|
|
357
|
+
export function assertPlanSignableBy(plan, signer, nowMs = Date.now()) {
|
|
358
|
+
const identity = verifiedPlanIdentity(plan);
|
|
359
|
+
const signerAddress = getAddress(signer.address);
|
|
360
|
+
if (identity.address !== signerAddress) {
|
|
361
|
+
throw new Error(`refusing to sign: the plan is bound to ${identity.address}, this signer is ${signerAddress}`);
|
|
362
|
+
}
|
|
363
|
+
if (identity.chainId !== signer.chainId) {
|
|
364
|
+
throw new Error(`refusing to sign: the plan is for chain ${identity.chainId}, this signer is on ${signer.chainId}`);
|
|
365
|
+
}
|
|
366
|
+
const missing = identity.capabilities.filter((c) => !signer.capabilities.includes(c));
|
|
367
|
+
if (missing.length > 0)
|
|
368
|
+
throw new Error(`refusing to sign: the plan requires ${missing.join(', ')}`);
|
|
369
|
+
if (!(identity.validUntilMs > nowMs)) {
|
|
370
|
+
refuse('STALE_ARTIFACT', `execution artifact expired at ${plan.artifact.valid_until}`);
|
|
371
|
+
}
|
|
372
|
+
integrity(() => assertArtifactFresh(plan.artifact, nowMs));
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Signer-side gate: a plan may be signed only if it IS the object `verifySwapEnvelope` returned
|
|
376
|
+
* (provenance, checked by identity), it is still the deep-frozen shape it was returned as — the
|
|
377
|
+
* artifact's nested objects included — and it still matches the verifier's own record of it. A thawed
|
|
378
|
+
* copy, a JSON round trip, a mutated clone, a fully frozen look-alike that imitates the shape, or a
|
|
379
|
+
* re-pointed artifact with a recomputed commitment are all refused, so the only way to sign
|
|
380
|
+
* different bytes is to verify different bytes.
|
|
381
|
+
*/
|
|
382
|
+
export function assertVerifiedPlanIntact(plan) {
|
|
383
|
+
if (!isVerifierOutput(plan)) {
|
|
384
|
+
throw new Error('refusing to sign: the plan is not an object verifySwapEnvelope returned (provenance check failed)');
|
|
385
|
+
}
|
|
386
|
+
const frozen = Object.isFrozen(plan) &&
|
|
387
|
+
isDeepFrozen(plan.artifact) &&
|
|
388
|
+
Object.isFrozen(plan.steps) &&
|
|
389
|
+
plan.steps.length === 3 &&
|
|
390
|
+
plan.steps.every((step) => Object.isFrozen(step) && step.value === 0n && /^0x[0-9a-fA-F]*$/.test(step.data)) &&
|
|
391
|
+
Object.isFrozen(plan.intent) &&
|
|
392
|
+
Object.isFrozen(plan.fees) &&
|
|
393
|
+
Object.isFrozen(plan.fees.distributions) &&
|
|
394
|
+
Object.isFrozen(plan.checks) &&
|
|
395
|
+
plan.checks.length >= VERIFY_CHECK_IDS.length &&
|
|
396
|
+
plan.checks.every((check) => check.result === 'passed') &&
|
|
397
|
+
VERIFY_CHECK_IDS.every((id) => plan.checks.some((check) => check.id === id)) &&
|
|
398
|
+
plan.steps[2].to === plan.spender &&
|
|
399
|
+
plan.gross === plan.intent.fromAmount;
|
|
400
|
+
if (!frozen) {
|
|
401
|
+
throw new Error('refusing to sign: the plan is not the intact frozen result of verifySwapEnvelope');
|
|
402
|
+
}
|
|
403
|
+
const identity = PLAN_IDENTITY.get(plan);
|
|
404
|
+
const matchesRecord = getAddress(plan.artifact.account_binding.address) === identity.address &&
|
|
405
|
+
plan.artifact.account_binding.chainId === identity.chainId &&
|
|
406
|
+
plan.artifact.payload.chain_id === identity.chainId &&
|
|
407
|
+
plan.artifact.artifact_id === identity.artifactId &&
|
|
408
|
+
plan.artifact.submission.artifact_digest === identity.artifactDigest &&
|
|
409
|
+
Date.parse(plan.artifact.valid_until) === identity.validUntilMs &&
|
|
410
|
+
plan.artifact.required_capabilities.length === identity.capabilities.length &&
|
|
411
|
+
identity.capabilities.every((c) => plan.artifact.required_capabilities.includes(c)) &&
|
|
412
|
+
plan.steps.every((step, i) => step.to === identity.stepTargets[i]);
|
|
413
|
+
if (!matchesRecord) {
|
|
414
|
+
throw new Error("refusing to sign: the plan no longer matches the verifier's own record of it");
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/** The check ids `verifySwapEnvelope` records on a passing artifact, in order. Tests pin this list. */
|
|
418
|
+
export const VERIFY_CHECK_IDS = Object.freeze([
|
|
419
|
+
'schema',
|
|
420
|
+
'payload_digest',
|
|
421
|
+
'commitment',
|
|
422
|
+
'freshness',
|
|
423
|
+
'account_binding',
|
|
424
|
+
'capabilities',
|
|
425
|
+
'chain',
|
|
426
|
+
'plan_shape',
|
|
427
|
+
'approvals',
|
|
428
|
+
'swap_decode',
|
|
429
|
+
'min_output',
|
|
430
|
+
'fee_attribution',
|
|
431
|
+
]);
|
|
432
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/adapter/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapter-cdp-index.ts — the CDP-delegated signer on its OWN entry (`otto-intel-mcp/adapter-cdp`).
|
|
3
|
+
* `@coinbase/cdp-sdk` is an optional dependency loaded lazily inside `openCdpRig`; importing this
|
|
4
|
+
* entry never resolves the SDK, and a rig built from raw operations (`createCdpRig`) never needs it.
|
|
5
|
+
*/
|
|
6
|
+
export { CDP_CAPABILITIES, CHAIN_ID, CdpDelegatedSigner, DelegatedPlanHaltedError, NETWORK, RevocationRequestedError, createCdpRig, haltCauseOf, openCdpRig, revocationRequested, revokeDelegationByUserId, type CdpDeveloperCredentials, type DelegatedHaltCause, type CdpEndUser, type CdpRawOperations, type CdpRig, } from './adapter/cdp-signer.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapter-cdp-index.ts — the CDP-delegated signer on its OWN entry (`otto-intel-mcp/adapter-cdp`).
|
|
3
|
+
* `@coinbase/cdp-sdk` is an optional dependency loaded lazily inside `openCdpRig`; importing this
|
|
4
|
+
* entry never resolves the SDK, and a rig built from raw operations (`createCdpRig`) never needs it.
|
|
5
|
+
*/
|
|
6
|
+
export { CDP_CAPABILITIES, CHAIN_ID, CdpDelegatedSigner, DelegatedPlanHaltedError, NETWORK, RevocationRequestedError, createCdpRig, haltCauseOf, openCdpRig, revocationRequested, revokeDelegationByUserId, } from './adapter/cdp-signer.js';
|
|
7
|
+
//# sourceMappingURL=adapter-cdp-index.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapter-index.ts — the ADAPTER entry (`otto-intel-mcp/adapter`): the user-side verifier and the two
|
|
3
|
+
* chain helpers every signer of a prepare-only artifact needs — integrity → intent → policy →
|
|
4
|
+
* attribution checks, the LI.FI calldata decoder, the refusal taxonomy, the sealed-receipt rule and
|
|
5
|
+
* the ERC-20 allowance read-back. Moved here from `services/otto-execute` (2026-08-29) because the
|
|
6
|
+
* hosted MCP's delegated-send path runs the same verifier server-side and Railway builds this service
|
|
7
|
+
* in isolation; `otto-execute` re-exports these names unchanged. Nothing here constructs, signs or
|
|
8
|
+
* needs `@coinbase/cdp-sdk` — the delegated signer lives on `otto-intel-mcp/adapter-cdp`.
|
|
9
|
+
*/
|
|
10
|
+
export { ADAPTER_REFUSAL_CODES, AdapterRefusal, isAdapterRefusal, isAdapterRefusalCode, refuse, type AdapterRefusalCode, } from './adapter/refusal.js';
|
|
11
|
+
export { COLLECT_TOKEN_FEES_SELECTOR, GENERIC_SWAP_SELECTORS, decodeCollectTokenFees, decodeGenericSwap, encodeCollectTokenFees, encodeGenericSwap, type DecodedCollectTokenFees, type DecodedGenericSwap, type FeeDistribution, type SwapLeg, } from './adapter/lifi-decode.js';
|
|
12
|
+
export { VERIFY_CHECK_IDS, assertPlanSignableBy, assertVerifiedPlanIntact, defaultAdapterPolicy, isDeepFrozen, isVerifierOutput, verifiedPlanIdentity, verifySwapEnvelope, type AdapterPolicy, type AttributionPolicy, type PlanIdentity, type SignerIdentity, type SigningCapability, type SwapIntent, type VerifiedCheck, type VerifiedStep, type VerifiedSwap, type VerifyExpectations, } from './adapter/verify.js';
|
|
13
|
+
export { waitForSealedReceipt, type SealedClient, type SealedWaitOptions, } from './adapter/chain.js';
|
|
14
|
+
export { ERC20_ALLOWANCE_ABI, ERC20_APPROVE_ABI, readAllowance } from './adapter/erc20.js';
|
|
15
|
+
export type { SentStep } from './adapter/sent-step.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapter-index.ts — the ADAPTER entry (`otto-intel-mcp/adapter`): the user-side verifier and the two
|
|
3
|
+
* chain helpers every signer of a prepare-only artifact needs — integrity → intent → policy →
|
|
4
|
+
* attribution checks, the LI.FI calldata decoder, the refusal taxonomy, the sealed-receipt rule and
|
|
5
|
+
* the ERC-20 allowance read-back. Moved here from `services/otto-execute` (2026-08-29) because the
|
|
6
|
+
* hosted MCP's delegated-send path runs the same verifier server-side and Railway builds this service
|
|
7
|
+
* in isolation; `otto-execute` re-exports these names unchanged. Nothing here constructs, signs or
|
|
8
|
+
* needs `@coinbase/cdp-sdk` — the delegated signer lives on `otto-intel-mcp/adapter-cdp`.
|
|
9
|
+
*/
|
|
10
|
+
export { ADAPTER_REFUSAL_CODES, AdapterRefusal, isAdapterRefusal, isAdapterRefusalCode, refuse, } from './adapter/refusal.js';
|
|
11
|
+
export { COLLECT_TOKEN_FEES_SELECTOR, GENERIC_SWAP_SELECTORS, decodeCollectTokenFees, decodeGenericSwap, encodeCollectTokenFees, encodeGenericSwap, } from './adapter/lifi-decode.js';
|
|
12
|
+
export { VERIFY_CHECK_IDS, assertPlanSignableBy, assertVerifiedPlanIntact, defaultAdapterPolicy, isDeepFrozen, isVerifierOutput, verifiedPlanIdentity, verifySwapEnvelope, } from './adapter/verify.js';
|
|
13
|
+
export { waitForSealedReceipt, } from './adapter/chain.js';
|
|
14
|
+
export { ERC20_ALLOWANCE_ABI, ERC20_APPROVE_ABI, readAllowance } from './adapter/erc20.js';
|
|
15
|
+
//# sourceMappingURL=adapter-index.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hard ceiling on constructor-owned artifact freshness, measured from the construction
|
|
3
|
+
* timestamp embedded in the artifact id. The configured TTL must stay at or below it, and
|
|
4
|
+
* adapter-side freshness is clamped to it, so a rewritten `valid_until` cannot widen the
|
|
5
|
+
* window past this bound.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MAX_ARTIFACT_TTL_MS = 600000;
|
|
8
|
+
export declare function createArtifactId(nowMs?: number, entropy?: Uint8Array): string;
|
|
9
|
+
/** Construction timestamp embedded in the artifact id, or undefined when the id is malformed. */
|
|
10
|
+
export declare function artifactIdTimestampMs(artifactId: string): number | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Freshness bound owned by the constructor: `valid_until` must sit strictly after the
|
|
13
|
+
* construction timestamp and within the ceiling. Any adapter can re-derive it from the
|
|
14
|
+
* artifact alone, so an edited expiry is detectable without external state.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isArtifactFreshnessWellFormed(artifactId: string, validUntil: string): boolean;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
const CROCKFORD_BASE32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
|
3
|
+
const ARTIFACT_ID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
|
|
4
|
+
const MAX_ULID_TIMESTAMP_MS = 281_474_976_710_655;
|
|
5
|
+
/**
|
|
6
|
+
* Hard ceiling on constructor-owned artifact freshness, measured from the construction
|
|
7
|
+
* timestamp embedded in the artifact id. The configured TTL must stay at or below it, and
|
|
8
|
+
* adapter-side freshness is clamped to it, so a rewritten `valid_until` cannot widen the
|
|
9
|
+
* window past this bound.
|
|
10
|
+
*/
|
|
11
|
+
export const MAX_ARTIFACT_TTL_MS = 600_000;
|
|
12
|
+
function encodeBase32(value, length) {
|
|
13
|
+
let remaining = value;
|
|
14
|
+
let encoded = '';
|
|
15
|
+
for (let index = 0; index < length; index += 1) {
|
|
16
|
+
encoded = CROCKFORD_BASE32[Number(remaining & 31n)] + encoded;
|
|
17
|
+
remaining >>= 5n;
|
|
18
|
+
}
|
|
19
|
+
if (remaining !== 0n)
|
|
20
|
+
throw new Error('value does not fit in requested base32 length');
|
|
21
|
+
return encoded;
|
|
22
|
+
}
|
|
23
|
+
export function createArtifactId(nowMs = Date.now(), entropy = randomBytes(10)) {
|
|
24
|
+
if (!Number.isSafeInteger(nowMs) || nowMs < 0 || nowMs > MAX_ULID_TIMESTAMP_MS) {
|
|
25
|
+
throw new Error('ULID timestamp is outside the 48-bit range');
|
|
26
|
+
}
|
|
27
|
+
if (entropy.length !== 10)
|
|
28
|
+
throw new Error('ULID entropy must contain exactly 10 bytes');
|
|
29
|
+
let randomness = 0n;
|
|
30
|
+
for (const byte of entropy)
|
|
31
|
+
randomness = (randomness << 8n) | BigInt(byte);
|
|
32
|
+
return `${encodeBase32(BigInt(nowMs), 10)}${encodeBase32(randomness, 16)}`;
|
|
33
|
+
}
|
|
34
|
+
/** Construction timestamp embedded in the artifact id, or undefined when the id is malformed. */
|
|
35
|
+
export function artifactIdTimestampMs(artifactId) {
|
|
36
|
+
if (!ARTIFACT_ID_PATTERN.test(artifactId))
|
|
37
|
+
return undefined;
|
|
38
|
+
let timestamp = 0n;
|
|
39
|
+
for (const character of artifactId.slice(0, 10)) {
|
|
40
|
+
const digit = CROCKFORD_BASE32.indexOf(character);
|
|
41
|
+
if (digit < 0)
|
|
42
|
+
return undefined;
|
|
43
|
+
timestamp = (timestamp << 5n) | BigInt(digit);
|
|
44
|
+
}
|
|
45
|
+
const value = Number(timestamp);
|
|
46
|
+
return Number.isSafeInteger(value) ? value : undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Freshness bound owned by the constructor: `valid_until` must sit strictly after the
|
|
50
|
+
* construction timestamp and within the ceiling. Any adapter can re-derive it from the
|
|
51
|
+
* artifact alone, so an edited expiry is detectable without external state.
|
|
52
|
+
*/
|
|
53
|
+
export function isArtifactFreshnessWellFormed(artifactId, validUntil) {
|
|
54
|
+
const constructedMs = artifactIdTimestampMs(artifactId);
|
|
55
|
+
const validUntilMs = Date.parse(validUntil);
|
|
56
|
+
if (constructedMs === undefined || !Number.isFinite(validUntilMs))
|
|
57
|
+
return false;
|
|
58
|
+
return validUntilMs > constructedMs && validUntilMs - constructedMs <= MAX_ARTIFACT_TTL_MS;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=artifact-id.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* boot-redaction.ts — what the hosted boot may print about a configuration failure.
|
|
3
|
+
*
|
|
4
|
+
* viem embeds the RPC URL in its error messages (`URL: https://…/v2/<key>`) and strips only basic-auth
|
|
5
|
+
* credentials from it — not a key carried in the path or the query. A boot-time RPC failure printed
|
|
6
|
+
* verbatim would therefore write `BASE_RPC_URL` (an API key) to the Railway log. Every configuration or
|
|
7
|
+
* boot error goes through `redactBootMessage` before stderr: URLs become `<url>`, and any configured
|
|
8
|
+
* secret VALUE that survives is replaced by its NAME. The chain-id check itself throws a fixed message
|
|
9
|
+
* (`execution-delegation.ts`); this is the second layer, for every other path that could carry a value.
|
|
10
|
+
*/
|
|
11
|
+
/** The hosted service's secret-bearing variables. Values are never printed; names are. */
|
|
12
|
+
export declare const REDACTED_ENV_NAMES: readonly ["CDP_API_KEY_ID", "CDP_API_KEY_SECRET", "CDP_WALLET_SECRET", "OTTO_DELEGATION_SERVER_SECRET", "BASE_RPC_URL", "INTERNAL_API_KEY", "DATA_ENDPOINTS_INTERNAL_URL", "LIFI_API_KEY", "LIFI_BASE_URL", "POLYMARKET_CLOB_URL", "HYPERLIQUID_API_URL", "X402_PRIVATE_KEY", "X402_BASE_URL", "X402_RPC_URL"];
|
|
13
|
+
/** Redact URLs and every configured secret value from a message that is about to be printed. */
|
|
14
|
+
export declare function redactBootMessage(message: string, env?: NodeJS.ProcessEnv, names?: readonly string[]): string;
|
|
15
|
+
/** The one line the boot prints for a failure; never the raw error. */
|
|
16
|
+
export declare function bootFailureLine(prefix: string, error: unknown, env?: NodeJS.ProcessEnv): string;
|