@x1id/resolve 0.23.0 → 0.43.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/README.md +71 -0
- package/dist/accounts.d.ts +6 -0
- package/dist/accounts.js +6 -1
- package/dist/agent.d.ts +184 -8
- package/dist/agent.js +351 -3
- package/dist/agentPolicy.d.ts +238 -0
- package/dist/agentPolicy.js +364 -0
- package/dist/attestation.d.ts +25 -0
- package/dist/attestation.js +25 -0
- package/dist/checkout.d.ts +216 -0
- package/dist/checkout.js +327 -0
- package/dist/credentials.d.ts +466 -0
- package/dist/credentials.js +669 -0
- package/dist/gate.d.ts +94 -4
- package/dist/gate.js +206 -7
- package/dist/gateClient.d.ts +12 -0
- package/dist/giftLink.d.ts +272 -0
- package/dist/giftLink.js +452 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +32 -0
- package/dist/loginButton.d.ts +69 -0
- package/dist/loginButton.js +207 -0
- package/dist/logo.d.ts +1 -0
- package/dist/logo.js +8 -0
- package/dist/messaging.d.ts +439 -0
- package/dist/messaging.js +979 -0
- package/dist/nsGate.d.ts +98 -0
- package/dist/nsGate.js +172 -0
- package/dist/oidc.d.ts +272 -0
- package/dist/oidc.js +641 -0
- package/dist/pay.d.ts +513 -0
- package/dist/pay.js +756 -0
- package/dist/payment.d.ts +9 -2
- package/dist/payment.js +9 -2
- package/dist/recovery.d.ts +186 -0
- package/dist/recovery.js +276 -0
- package/dist/recoveryNft.d.ts +183 -0
- package/dist/recoveryNft.js +291 -0
- package/dist/shield.d.ts +373 -0
- package/dist/shield.js +695 -0
- package/dist/subscription.d.ts +439 -0
- package/dist/subscription.js +642 -0
- package/dist/textRecords.d.ts +52 -6
- package/dist/textRecords.js +80 -8
- package/dist/types.d.ts +3 -0
- package/dist/x402Receipt.d.ts +246 -0
- package/dist/x402Receipt.js +374 -0
- package/package.json +18 -1
- package/schema/agent-manifest.json +91 -8
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x402 payment receipts on chain (#8324, wired by [Pay 10] / WP 8833):
|
|
3
|
+
* `record_x402_receipt`, its `X402ReceiptRecorded` event and the per-handle
|
|
4
|
+
* `AgentPaymentStats` tally.
|
|
5
|
+
*
|
|
6
|
+
* Hand-rolled like the rest of this package — no Anchor client, no
|
|
7
|
+
* `@solana/web3.js`. The builder returns the transport-neutral
|
|
8
|
+
* {@link BuiltInstruction} (`delegate.ts` has the two-line web3.js adapter).
|
|
9
|
+
*
|
|
10
|
+
* # What a receipt proves (and what it does not)
|
|
11
|
+
*
|
|
12
|
+
* The instruction is ATTESTOR-SIGNED (`has_one = attestor` on
|
|
13
|
+
* `["attestor_config"]`). The program does NOT look at the payment itself: it
|
|
14
|
+
* does not introspect the Instructions sysvar, does not read `settled_sig`'s
|
|
15
|
+
* transaction, and does not check `amount` / `mint` against anything. A
|
|
16
|
+
* receipt therefore proves exactly one thing — "the configured attestor key
|
|
17
|
+
* stamped this payment" — and the attestor is trusted to have confirmed the
|
|
18
|
+
* settlement off chain first (`docs/x402-receipts.md`, option B). The
|
|
19
|
+
* `settled_sig` it carries is the audit anchor: anyone can fetch that
|
|
20
|
+
* transaction and check it. Never present a receipt as more than that.
|
|
21
|
+
*
|
|
22
|
+
* Both handles must be live `Handle` accounts and must differ
|
|
23
|
+
* (`X402SelfReceipt`, 6085). A scoped handle (`["handle", [len], label,
|
|
24
|
+
* name]`) needs its `["namespace", label]` account in `namespaces` (the
|
|
25
|
+
* program scans remaining accounts for it); a bare handle needs nothing.
|
|
26
|
+
*
|
|
27
|
+
* Cost: the attestor is the fee payer and pays the rent of each
|
|
28
|
+
* `AgentPaymentStats` account the first time a handle is touched (81 bytes
|
|
29
|
+
* each). On X1 the fee is 10 lamports per REQUESTED compute unit plus 1,500
|
|
30
|
+
* per signature, so always set a compute-unit limit: simulate and use
|
|
31
|
+
* {@link x402ReceiptCuLimitFromSimulation}, or fall back to
|
|
32
|
+
* {@link RECORD_X402_RECEIPT_CU_LIMIT}.
|
|
33
|
+
*/
|
|
34
|
+
import { decodeBase58, decodeBase58_32, encodeBase58 } from "./base58.js";
|
|
35
|
+
import { sha256 } from "./sha256.js";
|
|
36
|
+
/** Anchor instruction discriminator: `sha256("global:record_x402_receipt")[0..8]`. */
|
|
37
|
+
export const RECORD_X402_RECEIPT_DISCRIMINATOR = Uint8Array.from([196, 39, 64, 88, 117, 200, 163, 232]);
|
|
38
|
+
/** Anchor event discriminator: `sha256("event:X402ReceiptRecorded")[0..8]`.
|
|
39
|
+
* An emitted event is logged as `Program data: <base64(disc ‖ borsh)>`. */
|
|
40
|
+
export const X402_RECEIPT_RECORDED_EVENT_DISCRIMINATOR = Uint8Array.from([37, 68, 174, 195, 241, 126, 162, 223]);
|
|
41
|
+
/** Anchor account discriminator: `sha256("account:AgentPaymentStats")[0..8]`. */
|
|
42
|
+
export const AGENT_PAYMENT_STATS_DISCRIMINATOR = Uint8Array.from([80, 161, 55, 40, 220, 73, 56, 65]);
|
|
43
|
+
/** Seed prefix of the stats PDA: `["x402_stats", handleAccount]`. */
|
|
44
|
+
export const AGENT_PAYMENT_STATS_SEED = "x402_stats";
|
|
45
|
+
/** `AgentPaymentStats` size: disc(8) + handle(32) + 4×u64(32) + last_at(8) + bump(1). */
|
|
46
|
+
export const AGENT_PAYMENT_STATS_LEN = 81;
|
|
47
|
+
/** `X402ReceiptRecorded` encoded size: disc(8) + payer(32) + payee(32) +
|
|
48
|
+
* amount(8) + mint(32) + resource_hash(32) + settled_sig(64) + timestamp(8). */
|
|
49
|
+
export const X402_RECEIPT_EVENT_LEN = 216;
|
|
50
|
+
/** `record_x402_receipt` instruction data size: disc + amount + mint +
|
|
51
|
+
* resource_hash + settled_sig. */
|
|
52
|
+
export const RECORD_X402_RECEIPT_DATA_LEN = 8 + 8 + 32 + 32 + 64;
|
|
53
|
+
/** `X402SelfReceipt`: payer and payee handle are the same account. */
|
|
54
|
+
export const X402_SELF_RECEIPT_ERROR = 6085;
|
|
55
|
+
/**
|
|
56
|
+
* Static compute-unit CEILING for a transaction carrying ONE
|
|
57
|
+
* `record_x402_receipt` (plus its compute-budget instruction), for callers
|
|
58
|
+
* that cannot simulate first. Measured in litesvm against the built program
|
|
59
|
+
* (programs/x1-handles/tests/x402_receipt_sdk_e2e.rs): ~40.0k CU when both
|
|
60
|
+
* stats accounts are created (first touch), ~28.7k CU after, with stats-PDA
|
|
61
|
+
* bumps 255/254. The cost is not constant: Anchor re-derives each stats PDA
|
|
62
|
+
* with `find_program_address` on every call, so a handle whose stats bump is
|
|
63
|
+
* lower costs ~1.5k CU more per step below 255. X1 charges the REQUESTED
|
|
64
|
+
* limit (10 lamports per CU), so the better practice — what dev-api does — is
|
|
65
|
+
* to simulate and request {@link x402ReceiptCuLimitFromSimulation} of the
|
|
66
|
+
* measured units, using this only as the fallback / upper bound.
|
|
67
|
+
*/
|
|
68
|
+
export const RECORD_X402_RECEIPT_CU_LIMIT = 60_000;
|
|
69
|
+
/** A tight limit from a simulation's `unitsConsumed`: +10% (at least 2,000
|
|
70
|
+
* CU), rounded up to a thousand, never above `ceiling`. */
|
|
71
|
+
export function x402ReceiptCuLimitFromSimulation(unitsConsumed, ceiling = RECORD_X402_RECEIPT_CU_LIMIT * 2) {
|
|
72
|
+
if (!Number.isFinite(unitsConsumed) || unitsConsumed <= 0)
|
|
73
|
+
return Math.min(RECORD_X402_RECEIPT_CU_LIMIT, ceiling);
|
|
74
|
+
const padded = unitsConsumed + Math.max(2_000, Math.ceil(unitsConsumed * 0.1));
|
|
75
|
+
return Math.min(Math.ceil(padded / 1_000) * 1_000, ceiling);
|
|
76
|
+
}
|
|
77
|
+
const SYSTEM_PROGRAM = "11111111111111111111111111111111";
|
|
78
|
+
const TE = new TextEncoder();
|
|
79
|
+
const U64_MAX = 0xffffffffffffffffn;
|
|
80
|
+
function toBytes32(v, what) {
|
|
81
|
+
if (typeof v === "string") {
|
|
82
|
+
const b = decodeBase58_32(v);
|
|
83
|
+
if (!b)
|
|
84
|
+
throw new Error(`${what} is not a valid base58 address`);
|
|
85
|
+
return b;
|
|
86
|
+
}
|
|
87
|
+
if (v.length !== 32)
|
|
88
|
+
throw new Error(`${what} must be exactly 32 bytes`);
|
|
89
|
+
return v;
|
|
90
|
+
}
|
|
91
|
+
function toBase58(v, what) {
|
|
92
|
+
return encodeBase58(toBytes32(v, what));
|
|
93
|
+
}
|
|
94
|
+
/** A transaction signature as its 64 raw bytes (from base58 or bytes). */
|
|
95
|
+
export function x402SettledSigBytes(sig) {
|
|
96
|
+
if (typeof sig !== "string") {
|
|
97
|
+
if (sig.length !== 64)
|
|
98
|
+
throw new Error("settledSig must be exactly 64 bytes");
|
|
99
|
+
return sig;
|
|
100
|
+
}
|
|
101
|
+
const b = decodeSig(sig);
|
|
102
|
+
if (!b)
|
|
103
|
+
throw new Error("settledSig is not a base58 64-byte transaction signature");
|
|
104
|
+
return b;
|
|
105
|
+
}
|
|
106
|
+
function decodeSig(s) {
|
|
107
|
+
if (!/^[1-9A-HJ-NP-Za-km-z]{64,88}$/.test(s))
|
|
108
|
+
return null;
|
|
109
|
+
return decodeBase58(s, 64);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The `resource_hash` x1id stamps: sha256 of the x402 `resource` URL the
|
|
113
|
+
* payment bought, e.g. `https://api.x1id.io/v1/resolve`. The ENDPOINT, not the
|
|
114
|
+
* exact request — a receipt is public forever, and hashing the full request
|
|
115
|
+
* (`/v1/resolve/alice`) would let anyone brute-force what an agent looked up.
|
|
116
|
+
*/
|
|
117
|
+
export function x402ResourceHash(resource) {
|
|
118
|
+
if (typeof resource !== "string" || resource.length === 0)
|
|
119
|
+
throw new Error("resource must be a non-empty string");
|
|
120
|
+
return sha256(TE.encode(resource));
|
|
121
|
+
}
|
|
122
|
+
/** `["x402_stats", handleAccount]` — the second seed is the 32 raw bytes of
|
|
123
|
+
* the Handle ACCOUNT address. */
|
|
124
|
+
export async function findAgentPaymentStatsPda(find, programId, handle) {
|
|
125
|
+
return find([TE.encode(AGENT_PAYMENT_STATS_SEED), toBytes32(handle, "handle")], toBase58(programId, "programId"));
|
|
126
|
+
}
|
|
127
|
+
/** Borsh args after the discriminator: amount u64, mint, resource_hash[32], settled_sig[64]. */
|
|
128
|
+
export function encodeRecordX402ReceiptArgs(a) {
|
|
129
|
+
if (typeof a.amount !== "bigint" || a.amount < 0n || a.amount > U64_MAX)
|
|
130
|
+
throw new Error("amount must be a u64 bigint");
|
|
131
|
+
if (!(a.resourceHash instanceof Uint8Array) || a.resourceHash.length !== 32)
|
|
132
|
+
throw new Error("resourceHash must be exactly 32 bytes");
|
|
133
|
+
const out = new Uint8Array(RECORD_X402_RECEIPT_DATA_LEN);
|
|
134
|
+
out.set(RECORD_X402_RECEIPT_DISCRIMINATOR, 0);
|
|
135
|
+
new DataView(out.buffer).setBigUint64(8, a.amount, true);
|
|
136
|
+
out.set(toBytes32(a.mint, "mint"), 16);
|
|
137
|
+
out.set(a.resourceHash, 48);
|
|
138
|
+
out.set(x402SettledSigBytes(a.settledSig), 80);
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Build `record_x402_receipt`. Accounts in `RecordX402Receipt` order:
|
|
143
|
+
* attestor(s,w) attestor_config payer_handle payee_handle payer_stats(w)
|
|
144
|
+
* payee_stats(w) system_program, then any namespace accounts.
|
|
145
|
+
*/
|
|
146
|
+
export function buildRecordX402ReceiptIx(p) {
|
|
147
|
+
const payerHandle = toBase58(p.payerHandle, "payerHandle");
|
|
148
|
+
const payeeHandle = toBase58(p.payeeHandle, "payeeHandle");
|
|
149
|
+
if (payerHandle === payeeHandle)
|
|
150
|
+
throw new Error("payerHandle and payeeHandle must differ (X402SelfReceipt)");
|
|
151
|
+
const data = encodeRecordX402ReceiptArgs(p);
|
|
152
|
+
const keys = [
|
|
153
|
+
{ pubkey: toBase58(p.attestor, "attestor"), isSigner: true, isWritable: true },
|
|
154
|
+
{ pubkey: toBase58(p.attestorConfig, "attestorConfig"), isSigner: false, isWritable: false },
|
|
155
|
+
{ pubkey: payerHandle, isSigner: false, isWritable: false },
|
|
156
|
+
{ pubkey: payeeHandle, isSigner: false, isWritable: false },
|
|
157
|
+
{ pubkey: toBase58(p.payerStats, "payerStats"), isSigner: false, isWritable: true },
|
|
158
|
+
{ pubkey: toBase58(p.payeeStats, "payeeStats"), isSigner: false, isWritable: true },
|
|
159
|
+
{ pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
|
|
160
|
+
];
|
|
161
|
+
for (const [i, ns] of (p.namespaces ?? []).entries()) {
|
|
162
|
+
keys.push({ pubkey: toBase58(ns, `namespaces[${i}]`), isSigner: false, isWritable: false });
|
|
163
|
+
}
|
|
164
|
+
return { programId: toBase58(p.programId, "programId"), keys, data };
|
|
165
|
+
}
|
|
166
|
+
function eq(a, b) {
|
|
167
|
+
if (a.length !== b.length)
|
|
168
|
+
return false;
|
|
169
|
+
for (let i = 0; i < a.length; i++)
|
|
170
|
+
if (a[i] !== b[i])
|
|
171
|
+
return false;
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
/** Decode one event payload (the bytes of a `Program data:` line). null when
|
|
175
|
+
* it is not an `X402ReceiptRecorded`. */
|
|
176
|
+
export function decodeX402ReceiptRecorded(raw) {
|
|
177
|
+
if (raw.length !== X402_RECEIPT_EVENT_LEN || !eq(raw.subarray(0, 8), X402_RECEIPT_RECORDED_EVENT_DISCRIMINATOR))
|
|
178
|
+
return null;
|
|
179
|
+
const dv = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
180
|
+
return {
|
|
181
|
+
payerHandle: encodeBase58(raw.slice(8, 40)),
|
|
182
|
+
payeeHandle: encodeBase58(raw.slice(40, 72)),
|
|
183
|
+
amount: dv.getBigUint64(72, true),
|
|
184
|
+
mint: encodeBase58(raw.slice(80, 112)),
|
|
185
|
+
resourceHash: raw.slice(112, 144),
|
|
186
|
+
settledSig: encodeBase58(raw.slice(144, 208)),
|
|
187
|
+
timestamp: dv.getBigInt64(208, true),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/** Encode an event the way the program emits it (tests / fixtures). */
|
|
191
|
+
export function encodeX402ReceiptRecorded(e) {
|
|
192
|
+
const out = new Uint8Array(X402_RECEIPT_EVENT_LEN);
|
|
193
|
+
const dv = new DataView(out.buffer);
|
|
194
|
+
out.set(X402_RECEIPT_RECORDED_EVENT_DISCRIMINATOR, 0);
|
|
195
|
+
out.set(toBytes32(e.payerHandle, "payerHandle"), 8);
|
|
196
|
+
out.set(toBytes32(e.payeeHandle, "payeeHandle"), 40);
|
|
197
|
+
dv.setBigUint64(72, e.amount, true);
|
|
198
|
+
out.set(toBytes32(e.mint, "mint"), 80);
|
|
199
|
+
if (e.resourceHash.length !== 32)
|
|
200
|
+
throw new Error("resourceHash must be exactly 32 bytes");
|
|
201
|
+
out.set(e.resourceHash, 112);
|
|
202
|
+
out.set(x402SettledSigBytes(e.settledSig), 144);
|
|
203
|
+
dv.setBigInt64(208, e.timestamp, true);
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
function b64decode(s) {
|
|
207
|
+
try {
|
|
208
|
+
return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Every `X402ReceiptRecorded` among a transaction's log lines, attributed to
|
|
216
|
+
* `programId`'s OWN invoke frames only (a look-alike `Program data:` line
|
|
217
|
+
* printed by another program is ignored).
|
|
218
|
+
*/
|
|
219
|
+
export function parseX402ReceiptEvents(logs, programId) {
|
|
220
|
+
const pid = toBase58(programId, "programId");
|
|
221
|
+
const stack = [];
|
|
222
|
+
const out = [];
|
|
223
|
+
for (const line of logs) {
|
|
224
|
+
const inv = /^Program (\S+) invoke \[\d+\]$/.exec(line);
|
|
225
|
+
if (inv) {
|
|
226
|
+
stack.push(inv[1]);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (/^Program \S+ (success|failed: )/.test(line)) {
|
|
230
|
+
stack.pop();
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (line.startsWith("Program data: ") && stack[stack.length - 1] === pid) {
|
|
234
|
+
const raw = b64decode(line.slice("Program data: ".length).trim());
|
|
235
|
+
const ev = raw ? decodeX402ReceiptRecorded(raw) : null;
|
|
236
|
+
if (ev)
|
|
237
|
+
out.push(ev);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return out;
|
|
241
|
+
}
|
|
242
|
+
/** Decode an `AgentPaymentStats` account (discriminator + exact length
|
|
243
|
+
* checked); null for anything else. */
|
|
244
|
+
export function decodeAgentPaymentStats(raw) {
|
|
245
|
+
if (raw.length !== AGENT_PAYMENT_STATS_LEN || !eq(raw.subarray(0, 8), AGENT_PAYMENT_STATS_DISCRIMINATOR))
|
|
246
|
+
return null;
|
|
247
|
+
const dv = new DataView(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
248
|
+
return {
|
|
249
|
+
handle: encodeBase58(raw.slice(8, 40)),
|
|
250
|
+
paymentsMadeCount: dv.getBigUint64(40, true),
|
|
251
|
+
totalPaid: dv.getBigUint64(48, true),
|
|
252
|
+
paymentsReceivedCount: dv.getBigUint64(56, true),
|
|
253
|
+
totalReceived: dv.getBigUint64(64, true),
|
|
254
|
+
lastAt: dv.getBigInt64(72, true),
|
|
255
|
+
bump: raw[80],
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/** Encode an `AgentPaymentStats` account (tests / fixtures). */
|
|
259
|
+
export function encodeAgentPaymentStats(s) {
|
|
260
|
+
const out = new Uint8Array(AGENT_PAYMENT_STATS_LEN);
|
|
261
|
+
const dv = new DataView(out.buffer);
|
|
262
|
+
out.set(AGENT_PAYMENT_STATS_DISCRIMINATOR, 0);
|
|
263
|
+
out.set(toBytes32(s.handle, "handle"), 8);
|
|
264
|
+
dv.setBigUint64(40, s.paymentsMadeCount, true);
|
|
265
|
+
dv.setBigUint64(48, s.totalPaid, true);
|
|
266
|
+
dv.setBigUint64(56, s.paymentsReceivedCount, true);
|
|
267
|
+
dv.setBigUint64(64, s.totalReceived, true);
|
|
268
|
+
dv.setBigInt64(72, s.lastAt, true);
|
|
269
|
+
out[80] = s.bump;
|
|
270
|
+
return out;
|
|
271
|
+
}
|
|
272
|
+
/** Fee (lamports) of one receipt transaction on X1: 10 lamports per
|
|
273
|
+
* requested CU + 1,500 per signature (rent of new stats accounts excluded). */
|
|
274
|
+
export function x402ReceiptTxFeeLamports(cuLimit = RECORD_X402_RECEIPT_CU_LIMIT, signatures = 1) {
|
|
275
|
+
return cuLimit * 10 + 1_500 * signatures;
|
|
276
|
+
}
|
|
277
|
+
// ===================== dedicated receipt signer (upgrade 5) =====================
|
|
278
|
+
//
|
|
279
|
+
// `record_x402_receipt_v2` (signed by the dedicated receipt signer OR the
|
|
280
|
+
// attestor) and the admin's `set_receipt_signer`. Both are routed through the
|
|
281
|
+
// program's `fallback` (`extra_ix`) and need the registry upgrade that adds
|
|
282
|
+
// them (until then: `InstructionFallbackNotFound`, 101). v1
|
|
283
|
+
// ({@link buildRecordX402ReceiptIx}) is unchanged and stays attestor-only.
|
|
284
|
+
// docs/x402-receipts.md "Dedicated receipt signer (upgrade 5)".
|
|
285
|
+
/** Seed of the receipt-signer singleton: `["receipt_signer_config"]`. */
|
|
286
|
+
export const RECEIPT_SIGNER_CONFIG_SEED = "receipt_signer_config";
|
|
287
|
+
/** Seed of a handle's O(1) payment tally (alias of {@link AGENT_PAYMENT_STATS_SEED}). */
|
|
288
|
+
export const X402_STATS_SEED = AGENT_PAYMENT_STATS_SEED;
|
|
289
|
+
/** `sha256("global:record_x402_receipt_v2")[..8]`. */
|
|
290
|
+
export const RECORD_X402_RECEIPT_V2_DISCRIMINATOR = Uint8Array.from([99, 60, 56, 81, 186, 214, 82, 72]);
|
|
291
|
+
/** `sha256("global:set_receipt_signer")[..8]`. */
|
|
292
|
+
export const SET_RECEIPT_SIGNER_DISCRIMINATOR = Uint8Array.from([40, 114, 51, 32, 33, 184, 70, 80]);
|
|
293
|
+
/** `sha256("account:ReceiptSignerConfig")[..8]`. */
|
|
294
|
+
export const RECEIPT_SIGNER_CONFIG_DISCRIMINATOR = Uint8Array.from([154, 248, 63, 199, 144, 203, 157, 2]);
|
|
295
|
+
/** `RegistryError::NotReceiptSigner`: v2 signed by neither role. */
|
|
296
|
+
export const X402_NOT_RECEIPT_SIGNER_ERROR = 6157;
|
|
297
|
+
/** `RegistryError::X402ReceiptPayerIsSigner`: v2 with `payer == signer`
|
|
298
|
+
* (Sentinel run-011: the X1ID receipt key never pays). */
|
|
299
|
+
export const X402_RECEIPT_PAYER_IS_SIGNER_ERROR = 6232;
|
|
300
|
+
/** `["receipt_signer_config"]`. */
|
|
301
|
+
export async function findReceiptSignerConfigPda(find, programId) {
|
|
302
|
+
return find([TE.encode(RECEIPT_SIGNER_CONFIG_SEED)], toBase58(programId, "programId"));
|
|
303
|
+
}
|
|
304
|
+
/** `["x402_stats", handle]` (alias of {@link findAgentPaymentStatsPda}). */
|
|
305
|
+
export async function findX402StatsPda(find, programId, handle) {
|
|
306
|
+
return findAgentPaymentStatsPda(find, programId, handle);
|
|
307
|
+
}
|
|
308
|
+
/** Decode a `ReceiptSignerConfig` (`signer` base58; the default key
|
|
309
|
+
* `11111111111111111111111111111111` means the role is disabled). */
|
|
310
|
+
export function decodeReceiptSignerConfig(data) {
|
|
311
|
+
if (data.length < 8 + 32 + 1)
|
|
312
|
+
return null;
|
|
313
|
+
for (let i = 0; i < 8; i++)
|
|
314
|
+
if (data[i] !== RECEIPT_SIGNER_CONFIG_DISCRIMINATOR[i])
|
|
315
|
+
return null;
|
|
316
|
+
return { signer: encodeBase58(data.slice(8, 40)), bump: data[40] };
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Build `record_x402_receipt_v2`: the same args as v1. Accounts
|
|
320
|
+
* (`RecordX402ReceiptV2`): signer(s), payer(s,w), attestor_config,
|
|
321
|
+
* receipt_signer_config, payer_handle, payee_handle, payer_stats(w),
|
|
322
|
+
* payee_stats(w), system_program, then any namespaces. Make `payer` the
|
|
323
|
+
* transaction fee payer so the receipt signer's key holds no XNT. Throws when
|
|
324
|
+
* `payer` is missing or equals `signer`.
|
|
325
|
+
*/
|
|
326
|
+
export function buildRecordX402ReceiptV2Ix(p) {
|
|
327
|
+
if (p.receiptSignerConfig === undefined)
|
|
328
|
+
throw new Error("receiptSignerConfig is required for v2");
|
|
329
|
+
if (p.payer === undefined || p.payer === null) {
|
|
330
|
+
throw new Error("payer is required for v2 (the receipt signer only co-signs; X1ID pays nothing)");
|
|
331
|
+
}
|
|
332
|
+
const signer = toBase58(p.signer, "signer");
|
|
333
|
+
const payer = toBase58(p.payer, "payer");
|
|
334
|
+
if (payer === signer)
|
|
335
|
+
throw new Error("payer must differ from signer (X402ReceiptPayerIsSigner)");
|
|
336
|
+
const payerHandle = toBase58(p.payerHandle, "payerHandle");
|
|
337
|
+
const payeeHandle = toBase58(p.payeeHandle, "payeeHandle");
|
|
338
|
+
if (payerHandle === payeeHandle)
|
|
339
|
+
throw new Error("payerHandle and payeeHandle must differ (X402SelfReceipt)");
|
|
340
|
+
const data = encodeRecordX402ReceiptArgs(p);
|
|
341
|
+
data.set(RECORD_X402_RECEIPT_V2_DISCRIMINATOR, 0);
|
|
342
|
+
const keys = [
|
|
343
|
+
{ pubkey: signer, isSigner: true, isWritable: false },
|
|
344
|
+
{ pubkey: payer, isSigner: true, isWritable: true },
|
|
345
|
+
{ pubkey: toBase58(p.attestorConfig, "attestorConfig"), isSigner: false, isWritable: false },
|
|
346
|
+
{ pubkey: toBase58(p.receiptSignerConfig, "receiptSignerConfig"), isSigner: false, isWritable: false },
|
|
347
|
+
{ pubkey: payerHandle, isSigner: false, isWritable: false },
|
|
348
|
+
{ pubkey: payeeHandle, isSigner: false, isWritable: false },
|
|
349
|
+
{ pubkey: toBase58(p.payerStats, "payerStats"), isSigner: false, isWritable: true },
|
|
350
|
+
{ pubkey: toBase58(p.payeeStats, "payeeStats"), isSigner: false, isWritable: true },
|
|
351
|
+
{ pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
|
|
352
|
+
];
|
|
353
|
+
for (const [i, ns] of (p.namespaces ?? []).entries()) {
|
|
354
|
+
keys.push({ pubkey: toBase58(ns, `namespaces[${i}]`), isSigner: false, isWritable: false });
|
|
355
|
+
}
|
|
356
|
+
return { programId: toBase58(p.programId, "programId"), keys, data };
|
|
357
|
+
}
|
|
358
|
+
/** `set_receipt_signer(signer)`. Accounts (`SetReceiptSigner`): admin(s,w),
|
|
359
|
+
* config, receipt_signer_config(w), system_program. */
|
|
360
|
+
export function buildSetReceiptSignerIx(p) {
|
|
361
|
+
const data = new Uint8Array(40);
|
|
362
|
+
data.set(SET_RECEIPT_SIGNER_DISCRIMINATOR, 0);
|
|
363
|
+
data.set(toBytes32(p.signer, "signer"), 8);
|
|
364
|
+
return {
|
|
365
|
+
programId: toBase58(p.programId, "programId"),
|
|
366
|
+
keys: [
|
|
367
|
+
{ pubkey: toBase58(p.admin, "admin"), isSigner: true, isWritable: true },
|
|
368
|
+
{ pubkey: toBase58(p.config, "config"), isSigner: false, isWritable: false },
|
|
369
|
+
{ pubkey: toBase58(p.receiptSignerConfig, "receiptSignerConfig"), isSigner: false, isWritable: true },
|
|
370
|
+
{ pubkey: SYSTEM_PROGRAM, isSigner: false, isWritable: false },
|
|
371
|
+
],
|
|
372
|
+
data,
|
|
373
|
+
};
|
|
374
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x1id/resolve",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "Resolve @handles and X1NS names on X1. Never silently picks between namespaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -10,6 +10,18 @@
|
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"default": "./dist/index.js"
|
|
12
12
|
},
|
|
13
|
+
"./messaging": {
|
|
14
|
+
"types": "./dist/messaging.d.ts",
|
|
15
|
+
"default": "./dist/messaging.js"
|
|
16
|
+
},
|
|
17
|
+
"./oidc": {
|
|
18
|
+
"types": "./dist/oidc.d.ts",
|
|
19
|
+
"default": "./dist/oidc.js"
|
|
20
|
+
},
|
|
21
|
+
"./login-button": {
|
|
22
|
+
"types": "./dist/loginButton.d.ts",
|
|
23
|
+
"default": "./dist/loginButton.js"
|
|
24
|
+
},
|
|
13
25
|
"./wasm/x1_resolve_wasm.wasm": "./wasm/x1_resolve_wasm.wasm",
|
|
14
26
|
"./wasm/*": "./wasm/*",
|
|
15
27
|
"./package.json": "./package.json"
|
|
@@ -47,6 +59,11 @@
|
|
|
47
59
|
"engines": {
|
|
48
60
|
"node": ">=18"
|
|
49
61
|
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@noble/ciphers": "^1.3.0",
|
|
64
|
+
"@noble/curves": "^1.8.0",
|
|
65
|
+
"@noble/hashes": "^1.4.0"
|
|
66
|
+
},
|
|
50
67
|
"devDependencies": {
|
|
51
68
|
"typescript": "^5.6.0",
|
|
52
69
|
"@types/node": "^22.0.0",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://x1id.io/schema/agent-manifest.json",
|
|
4
4
|
"title": "x1id agent manifest",
|
|
5
|
-
"description": "A machine-readable description of an @handle operating as an AI agent — what it does, how to reach it, and how to pay it. Nothing here is a payment fact: payment.addresses[] is a mirror of the on-chain address Record(s) and payment.x402 is a pre-flight hint; the chain and the live 402 response always win. See docs/agent-records.md.",
|
|
5
|
+
"description": "A machine-readable description of an @handle operating as an AI agent — what it does, how to reach it, and how to pay it. Versions 1 and 2 are both valid; v2 adds per-capability / per-endpoint pricing, accepted payment methods and a name-bound payTo. Nothing here is a payment fact: v2's payment.payTo names an @name whose address is resolved from the chain (the W16 / name_pays_to rule), payment.addresses[] is a mirror of the on-chain address Record(s), and payment.x402 is a pre-flight hint; the chain and the live 402 response always win. See docs/agent-records.md. The SDK's validateManifest additionally checks cross-references (endpoint/capability ids), that x1id.name/handle/network match the handle being read, that payTo.name equals x1id.name, and that every priced mint is settled by a listed method.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": ["x1id"],
|
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
"additionalProperties": false,
|
|
13
13
|
"required": ["version", "name", "network", "handle"],
|
|
14
14
|
"properties": {
|
|
15
|
-
"version": { "
|
|
15
|
+
"version": { "enum": [1, 2] },
|
|
16
16
|
"name": { "type": "string", "minLength": 1, "maxLength": 260, "description": "The canonical @handle name, without the @." },
|
|
17
17
|
"network": { "type": "string", "enum": ["testnet", "mainnet"] },
|
|
18
|
-
"handle": { "
|
|
18
|
+
"handle": { "$ref": "#/$defs/Base58", "description": "The Handle PDA account address, base58 — the uniqueness anchor. A manifest served for the wrong name/handle is discarded on read." }
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"identity": {
|
|
22
22
|
"type": "object",
|
|
23
23
|
"additionalProperties": false,
|
|
24
24
|
"properties": {
|
|
25
|
-
"attestation": { "
|
|
25
|
+
"attestation": { "$ref": "#/$defs/Base58", "description": "Base58 address of a non-stale Attestation account on this handle (kind DNS or SOCIAL). See docs/agent-records.md §4 for what L1 does and does not prove." },
|
|
26
26
|
"description": { "type": "string", "maxLength": 2048 }
|
|
27
27
|
}
|
|
28
28
|
},
|
|
@@ -34,24 +34,69 @@
|
|
|
34
34
|
"additionalProperties": false,
|
|
35
35
|
"required": ["type", "url"],
|
|
36
36
|
"properties": {
|
|
37
|
+
"id": { "$ref": "#/$defs/Id", "description": "v2: stable id referenced from capabilities.items[].endpoint." },
|
|
37
38
|
"type": { "type": "string", "enum": ["x402", "mcp", "a2a", "http"] },
|
|
38
|
-
"url": { "$ref": "#/$defs/HttpsUrl" }
|
|
39
|
+
"url": { "$ref": "#/$defs/HttpsUrl" },
|
|
40
|
+
"capabilities": { "type": "array", "maxItems": 64, "items": { "$ref": "#/$defs/Id" }, "description": "v2: capability ids served here." },
|
|
41
|
+
"pricing": { "$ref": "#/$defs/Pricing", "description": "v2: this endpoint's own price(s)." }
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
},
|
|
42
45
|
"capabilities": {
|
|
43
46
|
"type": "object",
|
|
44
47
|
"additionalProperties": false,
|
|
45
|
-
"required": ["url"],
|
|
46
48
|
"properties": {
|
|
47
49
|
"schema": { "$ref": "#/$defs/HttpsUrl" },
|
|
48
|
-
"url": { "$ref": "#/$defs/HttpsUrl" }
|
|
50
|
+
"url": { "$ref": "#/$defs/HttpsUrl" },
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"maxItems": 64,
|
|
54
|
+
"description": "v2: the searchable capability list (the directory indexes items[].id).",
|
|
55
|
+
"items": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"additionalProperties": false,
|
|
58
|
+
"required": ["id"],
|
|
59
|
+
"properties": {
|
|
60
|
+
"id": { "$ref": "#/$defs/Id" },
|
|
61
|
+
"description": { "type": "string", "maxLength": 512 },
|
|
62
|
+
"endpoint": { "$ref": "#/$defs/Id" },
|
|
63
|
+
"pricing": { "$ref": "#/$defs/Pricing" }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
49
67
|
}
|
|
50
68
|
},
|
|
51
69
|
"payment": {
|
|
52
70
|
"type": "object",
|
|
53
71
|
"additionalProperties": false,
|
|
54
72
|
"properties": {
|
|
73
|
+
"payTo": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"required": ["name"],
|
|
77
|
+
"description": "v2: WHO gets paid — an @name (must equal x1id.name), never an address. Readers resolve the address from the chain under the W16 rule (live verified record for the method's chain, else the name's current authority on SVM chains). Required whenever anything is priced.",
|
|
78
|
+
"properties": { "name": { "type": "string", "minLength": 1, "maxLength": 261 } }
|
|
79
|
+
},
|
|
80
|
+
"methods": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"minItems": 1,
|
|
83
|
+
"maxItems": 8,
|
|
84
|
+
"description": "v2: accepted payment methods. Required whenever anything is priced.",
|
|
85
|
+
"items": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"additionalProperties": false,
|
|
88
|
+
"required": ["type", "rail", "network"],
|
|
89
|
+
"properties": {
|
|
90
|
+
"id": { "$ref": "#/$defs/Id" },
|
|
91
|
+
"type": { "const": "x402" },
|
|
92
|
+
"rail": { "enum": ["x1", "cdp"], "description": "x1 = the X1-native x402 rail (self-sponsored, settled on X1); cdp = the Coinbase CDP facilitator rail." },
|
|
93
|
+
"network": { "type": "string", "pattern": "^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,64}$", "description": "CAIP-2. For rail x1 it must be the X1 network id of x1id.network (solana:C7ucgdDEhxLTpXHhWSZxavSVmaNTUJWw testnet, solana:4SvBP3omtvcCVWdxq1zBY5cDp4wndjsT mainnet)." },
|
|
94
|
+
"scheme": { "const": "exact" },
|
|
95
|
+
"facilitator": { "$ref": "#/$defs/HttpsUrl" },
|
|
96
|
+
"assets": { "type": "array", "minItems": 1, "maxItems": 16, "items": { "$ref": "#/$defs/Mint" } }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
55
100
|
"addresses": {
|
|
56
101
|
"type": "array",
|
|
57
102
|
"maxItems": 16,
|
|
@@ -81,11 +126,49 @@
|
|
|
81
126
|
}
|
|
82
127
|
}
|
|
83
128
|
},
|
|
129
|
+
"allOf": [
|
|
130
|
+
{
|
|
131
|
+
"if": { "properties": { "x1id": { "properties": { "version": { "const": 1 } } } } },
|
|
132
|
+
"then": {
|
|
133
|
+
"properties": {
|
|
134
|
+
"capabilities": { "required": ["url"], "properties": { "items": false } },
|
|
135
|
+
"endpoints": { "items": { "properties": { "id": false, "capabilities": false, "pricing": false } } },
|
|
136
|
+
"payment": { "properties": { "payTo": false, "methods": false } }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"if": { "properties": { "x1id": { "properties": { "version": { "const": 2 } } } } },
|
|
142
|
+
"then": {
|
|
143
|
+
"properties": {
|
|
144
|
+
"capabilities": { "anyOf": [{ "required": ["url"] }, { "required": ["items"] }] }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
],
|
|
84
149
|
"$defs": {
|
|
85
150
|
"HttpsUrl": {
|
|
86
151
|
"type": "string",
|
|
87
152
|
"pattern": "^https://[^\\s@/]+(/[^\\s]*)?$",
|
|
88
153
|
"maxLength": 2048
|
|
89
|
-
}
|
|
154
|
+
},
|
|
155
|
+
"Base58": { "type": "string", "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$" },
|
|
156
|
+
"Id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]{0,63}$" },
|
|
157
|
+
"Mint": {
|
|
158
|
+
"type": "string",
|
|
159
|
+
"description": "\"XNT\" (native), an SPL mint (base58), or — CDP rail only — an EVM token address.",
|
|
160
|
+
"anyOf": [{ "const": "XNT" }, { "$ref": "#/$defs/Base58" }, { "pattern": "^0x[0-9a-fA-F]{40}$" }]
|
|
161
|
+
},
|
|
162
|
+
"Price": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"additionalProperties": false,
|
|
165
|
+
"required": ["amount", "mint", "unit"],
|
|
166
|
+
"properties": {
|
|
167
|
+
"amount": { "type": "string", "pattern": "^(0|[1-9][0-9]{0,19})$", "description": "Decimal string of the asset's smallest units (u64)." },
|
|
168
|
+
"mint": { "$ref": "#/$defs/Mint" },
|
|
169
|
+
"unit": { "enum": ["per-call", "per-1k-tokens", "per-1k-chars", "per-minute", "per-mb"] }
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"Pricing": { "type": "array", "minItems": 1, "maxItems": 8, "items": { "$ref": "#/$defs/Price" } }
|
|
90
173
|
}
|
|
91
174
|
}
|