@x1id/resolve 0.23.0 → 0.44.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.
Files changed (49) hide show
  1. package/README.md +97 -0
  2. package/dist/accounts.d.ts +6 -0
  3. package/dist/accounts.js +6 -1
  4. package/dist/agent.d.ts +184 -8
  5. package/dist/agent.js +351 -3
  6. package/dist/agentPolicy.d.ts +238 -0
  7. package/dist/agentPolicy.js +364 -0
  8. package/dist/attestation.d.ts +25 -0
  9. package/dist/attestation.js +25 -0
  10. package/dist/checkout.d.ts +216 -0
  11. package/dist/checkout.js +327 -0
  12. package/dist/credentials.d.ts +466 -0
  13. package/dist/credentials.js +669 -0
  14. package/dist/gate.d.ts +94 -4
  15. package/dist/gate.js +206 -7
  16. package/dist/gateClient.d.ts +12 -0
  17. package/dist/giftLink.d.ts +272 -0
  18. package/dist/giftLink.js +452 -0
  19. package/dist/index.d.ts +13 -0
  20. package/dist/index.js +32 -0
  21. package/dist/loginButton.d.ts +69 -0
  22. package/dist/loginButton.js +207 -0
  23. package/dist/logo.d.ts +1 -0
  24. package/dist/logo.js +8 -0
  25. package/dist/messaging.d.ts +439 -0
  26. package/dist/messaging.js +979 -0
  27. package/dist/nsGate.d.ts +98 -0
  28. package/dist/nsGate.js +172 -0
  29. package/dist/oidc.d.ts +272 -0
  30. package/dist/oidc.js +641 -0
  31. package/dist/pay.d.ts +513 -0
  32. package/dist/pay.js +756 -0
  33. package/dist/payment.d.ts +9 -2
  34. package/dist/payment.js +9 -2
  35. package/dist/recovery.d.ts +186 -0
  36. package/dist/recovery.js +276 -0
  37. package/dist/recoveryNft.d.ts +183 -0
  38. package/dist/recoveryNft.js +291 -0
  39. package/dist/shield.d.ts +373 -0
  40. package/dist/shield.js +695 -0
  41. package/dist/subscription.d.ts +439 -0
  42. package/dist/subscription.js +642 -0
  43. package/dist/textRecords.d.ts +52 -6
  44. package/dist/textRecords.js +80 -8
  45. package/dist/types.d.ts +3 -0
  46. package/dist/x402Receipt.d.ts +246 -0
  47. package/dist/x402Receipt.js +374 -0
  48. package/package.json +18 -1
  49. package/schema/agent-manifest.json +91 -8
@@ -0,0 +1,364 @@
1
+ /**
2
+ * Agent spend policies enforced by the registry (program upgrade 6,
3
+ * OpenProject #8456; spec docs/specs/agent-spend-policy.md).
4
+ *
5
+ * The current authority of an `@name` (untokenized owner, or the pNFT holder)
6
+ * authorises an agent wallet under a policy bound to the name:
7
+ * `["agent_policy", handle, agent]` holds per-mint caps per rolling period
8
+ * (native XNT = {@link AGENT_POLICY_NATIVE_MINT}), an expiry and an optional
9
+ * recipient allow-list. Native XNT is spent from the policy PDA's float (its
10
+ * lamports above rent); SPL / Token-2022 from a grantor token account through
11
+ * the policy's OWN delegate `["agent_delegate", policy, createdSlot]`, which
12
+ * the grantor approves with a plain SPL `Approve`
13
+ * ({@link buildApproveAgentDelegateIx}). One delegate per policy (Sentinel
14
+ * run-012 A-04): an allowance approved for one agent's policy can never be
15
+ * spent by another agent. One delegate per CREATION (D-02): the policy's
16
+ * `createdSlot` is a seed, so an approval left behind by a revoked policy
17
+ * can never re-arm when the same `(handle, agent)` policy is created again. A token account has
18
+ * one delegate, so give each agent (policy) its own token account per mint.
19
+ *
20
+ * Kill switches: lock the name (`lock_handle`, lock.ts) — every spend fails
21
+ * at once; {@link buildRevokeAgentPolicyIx} (works during lockdown and while
22
+ * locked); {@link buildWithdrawAgentFloatIx}; SPL `Revoke`.
23
+ *
24
+ * Tokenized names: pass `holderTokenAccount` (the grantor's canonical pNFT ATA)
25
+ * to create / update / spend; it is the program's `remaining_accounts[0]`
26
+ * authority proof.
27
+ *
28
+ * Hand-rolled like the rest of this package: no Anchor client, no web3.js.
29
+ */
30
+ import { encodeBase58, decodeBase58_32 } from "./base58.js";
31
+ export const AGENT_POLICY_SEED = "agent_policy";
32
+ export const AGENT_DELEGATE_SEED = "agent_delegate";
33
+ /** The "mint" a native-XNT cap is written under (all-zero pubkey). */
34
+ export const AGENT_POLICY_NATIVE_MINT = "11111111111111111111111111111111";
35
+ export const AGENT_MAX_CAPS = 4;
36
+ export const AGENT_MAX_RECIPIENTS = 4;
37
+ export const AGENT_PERIOD_MIN_SECS = 60;
38
+ export const AGENT_PERIOD_MAX_SECS = 366 * 24 * 60 * 60;
39
+ /** `8 + AgentPolicy::INIT_SPACE` (layout v2: `created_slot` appended). */
40
+ export const AGENT_POLICY_ACCOUNT_LEN = 525;
41
+ /** `AGENT_POLICY_VERSION` of the accounts this decoder reads. */
42
+ export const AGENT_POLICY_LAYOUT_VERSION = 2;
43
+ /** `sha256("global:<name>")[0..8]`, pinned + re-derived in the tests. */
44
+ export const CREATE_AGENT_POLICY_DISCRIMINATOR = Uint8Array.from([68, 244, 90, 9, 10, 95, 248, 78]);
45
+ export const UPDATE_AGENT_POLICY_DISCRIMINATOR = Uint8Array.from([55, 6, 173, 243, 185, 119, 129, 149]);
46
+ export const FUND_AGENT_POLICY_DISCRIMINATOR = Uint8Array.from([124, 198, 77, 243, 75, 95, 114, 231]);
47
+ export const WITHDRAW_AGENT_FLOAT_DISCRIMINATOR = Uint8Array.from([60, 8, 24, 156, 55, 183, 129, 174]);
48
+ export const REVOKE_AGENT_POLICY_DISCRIMINATOR = Uint8Array.from([230, 27, 101, 136, 225, 188, 175, 202]);
49
+ export const AGENT_SPEND_NATIVE_DISCRIMINATOR = Uint8Array.from([16, 112, 180, 241, 96, 128, 78, 177]);
50
+ export const AGENT_SPEND_SPL_DISCRIMINATOR = Uint8Array.from([19, 25, 249, 214, 32, 65, 131, 201]);
51
+ /** `sha256("account:AgentPolicy")[0..8]`. */
52
+ export const AGENT_POLICY_ACCOUNT_DISCRIMINATOR = Uint8Array.from([148, 193, 218, 129, 21, 96, 195, 77]);
53
+ /** Program error codes (AgentPolicyError, offset 6190). */
54
+ export const AGENT_POLICY_ERRORS = {
55
+ 6190: "AgentPolicyInvalidTerms",
56
+ 6191: "AgentPolicyTooManyEntries",
57
+ 6192: "AgentPolicyExpired",
58
+ 6193: "AgentPolicyStale",
59
+ 6194: "AgentPolicyNotAgent",
60
+ 6195: "AgentPolicyMintNotAllowed",
61
+ 6196: "AgentPolicyCapExceeded",
62
+ 6197: "AgentPolicyRecipientNotAllowed",
63
+ 6198: "AgentPolicyFloatInsufficient",
64
+ 6199: "AgentPolicyNotGrantor",
65
+ 6200: "AgentPolicyTokenAccountMismatch",
66
+ 6201: "AgentPolicyAllowanceExhausted",
67
+ 6202: "AgentPolicyAmountZero",
68
+ 6203: "AgentPolicyRevokeNotAllowed",
69
+ };
70
+ const SYSTEM_PROGRAM = "11111111111111111111111111111111";
71
+ const TE = new TextEncoder();
72
+ const U64_MAX = 0xffffffffffffffffn;
73
+ function b58(v, what) {
74
+ return encodeBase58(bytes(v, what));
75
+ }
76
+ function bytes(v, what) {
77
+ if (typeof v === "string") {
78
+ const b = decodeBase58_32(v);
79
+ if (!b)
80
+ throw new Error(`${what} is not a valid base58 address`);
81
+ return b;
82
+ }
83
+ if (v.length !== 32)
84
+ throw new Error(`${what} must be exactly 32 bytes`);
85
+ return v;
86
+ }
87
+ function k(v, what, isSigner, isWritable) {
88
+ return { pubkey: b58(v, what), isSigner, isWritable };
89
+ }
90
+ function cat(...parts) {
91
+ const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
92
+ let o = 0;
93
+ for (const p of parts) {
94
+ out.set(p, o);
95
+ o += p.length;
96
+ }
97
+ return out;
98
+ }
99
+ function u64(v, what) {
100
+ if (typeof v !== "bigint" || v < 0n || v > U64_MAX)
101
+ throw new Error(`${what} must be a u64 bigint`);
102
+ const out = new Uint8Array(8);
103
+ new DataView(out.buffer).setBigUint64(0, v, true);
104
+ return out;
105
+ }
106
+ function i64(v, what) {
107
+ if (typeof v !== "bigint" || v < -(1n << 63n) || v >= 1n << 63n)
108
+ throw new Error(`${what} must be an i64 bigint`);
109
+ const out = new Uint8Array(8);
110
+ new DataView(out.buffer).setBigInt64(0, v, true);
111
+ return out;
112
+ }
113
+ function u32(n) {
114
+ const out = new Uint8Array(4);
115
+ new DataView(out.buffer).setUint32(0, n, true);
116
+ return out;
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // PDAs
120
+ // ---------------------------------------------------------------------------
121
+ /** `["agent_policy", handle, agent]`. */
122
+ export async function deriveAgentPolicyPda(find, programId, handle, agent) {
123
+ return find([TE.encode(AGENT_POLICY_SEED), bytes(handle, "handle"), bytes(agent, "agent")], b58(programId, "programId"));
124
+ }
125
+ /**
126
+ * `["agent_delegate", policy, createdSlot (u64 LE)]` — the policy's own SPL
127
+ * delegate, which the grantor approves on the token account that policy's
128
+ * agent spends from. `createdSlot` is the policy's generation: read it from
129
+ * the account ({@link decodeAgentPolicy}`(…).createdSlot`) after
130
+ * `create_agent_policy` lands. A re-created policy (same address) has a new
131
+ * `createdSlot`, hence a new delegate (Sentinel run-012 D-02). Since 0.41.0;
132
+ * 0.40 used `["agent_delegate", policy]`, 0.38–0.39 the grantor (neither on
133
+ * mainnet).
134
+ */
135
+ export async function deriveAgentDelegatePda(find, programId, agentPolicy, createdSlot) {
136
+ return find([TE.encode(AGENT_DELEGATE_SEED), bytes(agentPolicy, "agentPolicy"), u64(createdSlot, "createdSlot")], b58(programId, "programId"));
137
+ }
138
+ /** Client-side mirror of the program's `validate_terms` (null = valid). */
139
+ export function agentPolicyTermsProblem(t, nowSecs) {
140
+ const rec = t.recipients ?? [];
141
+ if (t.caps.length > AGENT_MAX_CAPS || rec.length > AGENT_MAX_RECIPIENTS)
142
+ return "at most 4 caps and 4 recipients";
143
+ if (t.caps.length === 0)
144
+ return "at least one cap is required";
145
+ if (t.expiresAt <= nowSecs)
146
+ return "expiry must be in the future";
147
+ if (t.periodSecs < BigInt(AGENT_PERIOD_MIN_SECS) || t.periodSecs > BigInt(AGENT_PERIOD_MAX_SECS)) {
148
+ return "period must be between 60 seconds and 366 days";
149
+ }
150
+ const mints = t.caps.map((c) => b58(c.mint, "cap mint"));
151
+ if (new Set(mints).size !== mints.length)
152
+ return "duplicate mint in caps";
153
+ if (t.caps.some((c) => c.cap <= 0n))
154
+ return "every cap must be > 0";
155
+ const rs = rec.map((r) => b58(r, "recipient"));
156
+ if (new Set(rs).size !== rs.length)
157
+ return "duplicate recipient";
158
+ if (rs.includes(SYSTEM_PROGRAM))
159
+ return "the default pubkey is not a recipient";
160
+ return null;
161
+ }
162
+ /** Borsh `AgentPolicyTerms { expires_at, period_secs, caps: Vec<{mint,cap}>, recipients: Vec<Pubkey> }`. */
163
+ export function encodeAgentPolicyTerms(t) {
164
+ const rec = t.recipients ?? [];
165
+ return cat(i64(t.expiresAt, "expiresAt"), i64(t.periodSecs, "periodSecs"), u32(t.caps.length), ...t.caps.flatMap((c) => [bytes(c.mint, "cap mint"), u64(c.cap, "cap")]), u32(rec.length), ...rec.map((r) => bytes(r, "recipient")));
166
+ }
167
+ function proof(p, who) {
168
+ return p.holderTokenAccount === undefined ? [] : [k(p.holderTokenAccount, `${who} holderTokenAccount`, false, false)];
169
+ }
170
+ /** `create_agent_policy(agent, terms)`: grantor(s,w), handle, config, agent_policy(w), system [+ holder ATA]. */
171
+ export function buildCreateAgentPolicyIx(p) {
172
+ const problem = agentPolicyTermsProblem(p.terms, p.nowSecs ?? BigInt(Math.floor(Date.now() / 1000)));
173
+ if (problem)
174
+ throw new Error(problem);
175
+ return {
176
+ programId: b58(p.programId, "programId"),
177
+ keys: [
178
+ k(p.grantor, "grantor", true, true),
179
+ k(p.handle, "handle", false, false),
180
+ k(p.config, "config", false, false),
181
+ k(p.agentPolicy, "agentPolicy", false, true),
182
+ k(SYSTEM_PROGRAM, "systemProgram", false, false),
183
+ ...proof(p, "create"),
184
+ ],
185
+ data: cat(CREATE_AGENT_POLICY_DISCRIMINATOR, bytes(p.agent, "agent"), encodeAgentPolicyTerms(p.terms)),
186
+ };
187
+ }
188
+ /** `update_agent_policy(terms)`: grantor(s), handle, config, agent_policy(w) [+ holder ATA]. */
189
+ export function buildUpdateAgentPolicyIx(p) {
190
+ const problem = agentPolicyTermsProblem(p.terms, p.nowSecs ?? BigInt(Math.floor(Date.now() / 1000)));
191
+ if (problem)
192
+ throw new Error(problem);
193
+ return {
194
+ programId: b58(p.programId, "programId"),
195
+ keys: [
196
+ k(p.grantor, "grantor", true, false),
197
+ k(p.handle, "handle", false, false),
198
+ k(p.config, "config", false, false),
199
+ k(p.agentPolicy, "agentPolicy", false, true),
200
+ ...proof(p, "update"),
201
+ ],
202
+ data: cat(UPDATE_AGENT_POLICY_DISCRIMINATOR, encodeAgentPolicyTerms(p.terms)),
203
+ };
204
+ }
205
+ /** `fund_agent_policy(amount)`: grantor(s,w), config, agent_policy(w), system. */
206
+ export function buildFundAgentPolicyIx(p) {
207
+ if (p.lamports <= 0n)
208
+ throw new Error("lamports must be > 0");
209
+ return {
210
+ programId: b58(p.programId, "programId"),
211
+ keys: [
212
+ k(p.grantor, "grantor", true, true),
213
+ k(p.config, "config", false, false),
214
+ k(p.agentPolicy, "agentPolicy", false, true),
215
+ k(SYSTEM_PROGRAM, "systemProgram", false, false),
216
+ ],
217
+ data: cat(FUND_AGENT_POLICY_DISCRIMINATOR, u64(p.lamports, "lamports")),
218
+ };
219
+ }
220
+ /** `withdraw_agent_float(amount)`: grantor(s,w), agent_policy(w). Always allowed. */
221
+ export function buildWithdrawAgentFloatIx(p) {
222
+ if (p.lamports <= 0n)
223
+ throw new Error("lamports must be > 0");
224
+ return {
225
+ programId: b58(p.programId, "programId"),
226
+ keys: [k(p.grantor, "grantor", true, true), k(p.agentPolicy, "agentPolicy", false, true)],
227
+ data: cat(WITHDRAW_AGENT_FLOAT_DISCRIMINATOR, u64(p.lamports, "lamports")),
228
+ };
229
+ }
230
+ /** `revoke_agent_policy()`: signer(s), handle, agent_policy(w), grantor(w) [+ signer's pNFT ATA]. */
231
+ export function buildRevokeAgentPolicyIx(p) {
232
+ return {
233
+ programId: b58(p.programId, "programId"),
234
+ keys: [
235
+ k(p.signer, "signer", true, false),
236
+ k(p.handle, "handle", false, false),
237
+ k(p.agentPolicy, "agentPolicy", false, true),
238
+ k(p.grantor, "grantor", false, true),
239
+ ...proof(p, "revoke"),
240
+ ],
241
+ data: REVOKE_AGENT_POLICY_DISCRIMINATOR.slice(),
242
+ };
243
+ }
244
+ /** `agent_spend_native(amount)`: agent(s), agent_policy(w), handle, config, recipient(w) [+ grantor's pNFT ATA]. */
245
+ export function buildAgentSpendNativeIx(p) {
246
+ if (p.lamports <= 0n)
247
+ throw new Error("lamports must be > 0");
248
+ return {
249
+ programId: b58(p.programId, "programId"),
250
+ keys: [
251
+ k(p.agent, "agent", true, false),
252
+ k(p.agentPolicy, "agentPolicy", false, true),
253
+ k(p.handle, "handle", false, false),
254
+ k(p.config, "config", false, false),
255
+ k(p.recipient, "recipient", false, true),
256
+ ...proof(p, "spend"),
257
+ ],
258
+ data: cat(AGENT_SPEND_NATIVE_DISCRIMINATOR, u64(p.lamports, "lamports")),
259
+ };
260
+ }
261
+ /** `agent_spend_spl(amount)`: agent(s), agent_policy(w), handle, config, agent_delegate, mint, source(w), destination(w), token_program [+ grantor's pNFT ATA]. */
262
+ export function buildAgentSpendSplIx(p) {
263
+ if (p.amount <= 0n)
264
+ throw new Error("amount must be > 0");
265
+ return {
266
+ programId: b58(p.programId, "programId"),
267
+ keys: [
268
+ k(p.agent, "agent", true, false),
269
+ k(p.agentPolicy, "agentPolicy", false, true),
270
+ k(p.handle, "handle", false, false),
271
+ k(p.config, "config", false, false),
272
+ k(p.agentDelegate, "agentDelegate", false, false),
273
+ k(p.mint, "mint", false, false),
274
+ k(p.source, "source", false, true),
275
+ k(p.destination, "destination", false, true),
276
+ k(p.tokenProgram, "tokenProgram", false, false),
277
+ ...proof(p, "spend"),
278
+ ],
279
+ data: cat(AGENT_SPEND_SPL_DISCRIMINATOR, u64(p.amount, "amount")),
280
+ };
281
+ }
282
+ /** SPL `Approve` (tag 4) of the agent delegate — signed by the grantor. */
283
+ export function buildApproveAgentDelegateIx(p) {
284
+ return {
285
+ programId: b58(p.tokenProgram, "tokenProgram"),
286
+ keys: [k(p.source, "source", false, true), k(p.agentDelegate, "agentDelegate", false, false), k(p.grantor, "grantor", true, false)],
287
+ data: cat(Uint8Array.of(4), u64(p.amount, "amount")),
288
+ };
289
+ }
290
+ /**
291
+ * SPL `Revoke` (tag 5) — the grantor cuts the policy whose delegate is
292
+ * approved on that token account off it. `revoke_agent_policy` closes the
293
+ * policy but cannot clear the SPL approval (only the token-account owner
294
+ * can). The leftover approval names a delegate no policy can ever sign as
295
+ * again (re-creating the same `(handle, agent)` policy gets a new
296
+ * `createdSlot` and a new delegate), so an old approval can never re-arm;
297
+ * appending this still tidies the token account (it holds one delegate).
298
+ */
299
+ export function buildRevokeAgentDelegateIx(p) {
300
+ return {
301
+ programId: b58(p.tokenProgram, "tokenProgram"),
302
+ keys: [k(p.source, "source", false, true), k(p.grantor, "grantor", true, false)],
303
+ data: Uint8Array.of(5),
304
+ };
305
+ }
306
+ /** Decode an `AgentPolicy` account (null when not one). */
307
+ export function decodeAgentPolicy(data) {
308
+ if (data.length < AGENT_POLICY_ACCOUNT_LEN)
309
+ return null;
310
+ for (let i = 0; i < 8; i++)
311
+ if (data[i] !== AGENT_POLICY_ACCOUNT_DISCRIMINATOR[i])
312
+ return null;
313
+ const dv = new DataView(data.buffer, data.byteOffset, data.byteLength);
314
+ const pk = (o) => encodeBase58(data.subarray(o, o + 32));
315
+ const capsLen = data[147];
316
+ const recLen = data[372];
317
+ if (capsLen > AGENT_MAX_CAPS || recLen > AGENT_MAX_RECIPIENTS)
318
+ return null;
319
+ const caps = [];
320
+ for (let i = 0; i < capsLen; i++) {
321
+ const o = 148 + 56 * i;
322
+ const mint = pk(o);
323
+ caps.push({ mint, native: mint === AGENT_POLICY_NATIVE_MINT, cap: dv.getBigUint64(o + 32, true), spent: dv.getBigUint64(o + 40, true), periodStart: dv.getBigInt64(o + 48, true) });
324
+ }
325
+ const recipients = [];
326
+ for (let i = 0; i < recLen; i++)
327
+ recipients.push(pk(373 + 32 * i));
328
+ return {
329
+ version: data[8],
330
+ bump: data[9],
331
+ delegateBump: data[10],
332
+ handle: pk(11),
333
+ agent: pk(43),
334
+ grantor: pk(75),
335
+ epoch: dv.getBigInt64(107, true),
336
+ createdAt: dv.getBigInt64(115, true),
337
+ updatedAt: dv.getBigInt64(123, true),
338
+ expiresAt: dv.getBigInt64(131, true),
339
+ periodSecs: dv.getBigInt64(139, true),
340
+ caps,
341
+ recipients,
342
+ totalSpends: dv.getBigUint64(501, true),
343
+ lastSpentAt: dv.getBigInt64(509, true),
344
+ createdSlot: dv.getBigUint64(517, true),
345
+ };
346
+ }
347
+ /**
348
+ * What the agent may still spend of `mint` at `nowSecs` (the program's
349
+ * `roll_period` applied): null when the mint has no cap. Does NOT include
350
+ * the float / SPL allowance bound, the lock, or the policy's liveness.
351
+ */
352
+ export function agentPolicyRemaining(p, mint, nowSecs) {
353
+ const m = b58(mint, "mint");
354
+ const c = p.caps.find((x) => x.mint === m);
355
+ if (!c)
356
+ return null;
357
+ let start = c.periodStart;
358
+ let spent = c.spent;
359
+ if (p.periodSecs > 0n && nowSecs >= start && nowSecs - start >= p.periodSecs) {
360
+ start += ((nowSecs - start) / p.periodSecs) * p.periodSecs;
361
+ spent = 0n;
362
+ }
363
+ return { remaining: c.cap > spent ? c.cap - spent : 0n, periodEndsAt: start + p.periodSecs, expired: nowSecs >= p.expiresAt };
364
+ }
@@ -120,6 +120,8 @@ export declare const SET_ATTESTOR_DISCRIMINATOR: Uint8Array;
120
120
  export declare const CREATE_ATTESTATION_DISCRIMINATOR: Uint8Array;
121
121
  /** Anchor instruction discriminator: `sha256("global:close_attestation")[0..8]`. */
122
122
  export declare const CLOSE_ATTESTATION_DISCRIMINATOR: Uint8Array;
123
+ /** Anchor instruction discriminator: `sha256("global:remove_own_attestation")[0..8]`. */
124
+ export declare const REMOVE_OWN_ATTESTATION_DISCRIMINATOR: Uint8Array;
123
125
  /** Anchor instruction discriminator: `sha256("global:attest_validator")[0..8]` — the co-sign path. */
124
126
  export declare const ATTEST_VALIDATOR_DISCRIMINATOR: Uint8Array;
125
127
  /** Anchor instruction discriminator: `sha256("global:attest_validator_signed")[0..8]` — the detached path. */
@@ -356,6 +358,29 @@ export interface CloseAttestationParams {
356
358
  * a rotated-away key's output).
357
359
  */
358
360
  export declare function buildCloseAttestationIx(p: CloseAttestationParams): BuiltInstruction;
361
+ export interface RemoveOwnAttestationParams {
362
+ /** The registry program id. */
363
+ readonly programId: AddressLike;
364
+ /** The handle's current authority (any kind), or — for a validator link
365
+ * only — the withdraw authority that proved it. Signer. */
366
+ readonly signer: AddressLike;
367
+ /** The handle PDA the attestation belongs to. */
368
+ readonly handle: AddressLike;
369
+ /** The `["attestation", handle, [kind]]` account being removed. */
370
+ readonly attestation: AddressLike;
371
+ /** Rent refund, pinned on chain: the attestation's stored `attestor` for an
372
+ * attestor-minted kind (0–5), the `signer` for a validator link (6). */
373
+ readonly recipient: AddressLike;
374
+ /** ONLY for a tokenized handle signed by its holder: the holder's ATA for
375
+ * the handle's NFT mint (the `require_current_authority` proof). */
376
+ readonly holderTokenAccount?: AddressLike;
377
+ }
378
+ /**
379
+ * Build `remove_own_attestation` — self-service removal of one of the
380
+ * handle's own attestations (unlink a validator, drop a social/DNS
381
+ * verification). Removing can only take a badge away, never forge one.
382
+ */
383
+ export declare function buildRemoveOwnAttestationIx(p: RemoveOwnAttestationParams): BuiltInstruction;
359
384
  export interface AttestValidatorParams {
360
385
  /** The registry program id. */
361
386
  readonly programId: AddressLike;
@@ -129,6 +129,10 @@ export const CREATE_ATTESTATION_DISCRIMINATOR = Uint8Array.from([
129
129
  export const CLOSE_ATTESTATION_DISCRIMINATOR = Uint8Array.from([
130
130
  249, 84, 133, 23, 48, 175, 252, 221,
131
131
  ]);
132
+ /** Anchor instruction discriminator: `sha256("global:remove_own_attestation")[0..8]`. */
133
+ export const REMOVE_OWN_ATTESTATION_DISCRIMINATOR = Uint8Array.from([
134
+ 211, 214, 29, 219, 165, 179, 35, 209,
135
+ ]);
132
136
  /** Anchor instruction discriminator: `sha256("global:attest_validator")[0..8]` — the co-sign path. */
133
137
  export const ATTEST_VALIDATOR_DISCRIMINATOR = Uint8Array.from([
134
138
  170, 159, 195, 248, 59, 130, 28, 168,
@@ -452,6 +456,27 @@ export function buildCloseAttestationIx(p) {
452
456
  data: CLOSE_ATTESTATION_DISCRIMINATOR.slice(),
453
457
  };
454
458
  }
459
+ /**
460
+ * Build `remove_own_attestation` — self-service removal of one of the
461
+ * handle's own attestations (unlink a validator, drop a social/DNS
462
+ * verification). Removing can only take a badge away, never forge one.
463
+ */
464
+ export function buildRemoveOwnAttestationIx(p) {
465
+ const keys = [
466
+ { pubkey: toBase58(p.signer, "signer"), isSigner: true, isWritable: false },
467
+ { pubkey: toBase58(p.handle, "handle"), isSigner: false, isWritable: false },
468
+ { pubkey: toBase58(p.attestation, "attestation"), isSigner: false, isWritable: true },
469
+ { pubkey: toBase58(p.recipient, "recipient"), isSigner: false, isWritable: true },
470
+ ];
471
+ if (p.holderTokenAccount !== undefined) {
472
+ keys.push({ pubkey: toBase58(p.holderTokenAccount, "holderTokenAccount"), isSigner: false, isWritable: false });
473
+ }
474
+ return {
475
+ programId: toBase58(p.programId, "programId"),
476
+ keys,
477
+ data: REMOVE_OWN_ATTESTATION_DISCRIMINATOR.slice(),
478
+ };
479
+ }
455
480
  /**
456
481
  * Build `attest_validator` (co-sign path). Two signers — `owner` (authorizes
457
482
  * the link) and `withdrawAuthority` (the proof) — which MAY be the same key. No
@@ -0,0 +1,216 @@
1
+ /**
2
+ * X1ID Pay checkout widget ([Pay 7], docs/x1id-pay-checkout.md): the
3
+ * postMessage protocol between a merchant page and the X1 Pay invoice page (app.x1pay.me),
4
+ * plus the merchant-side controller that opens the checkout popup.
5
+ *
6
+ * The widget only PAYS an invoice that already exists on chain. The merchant
7
+ * creates the invoice (itself, or with a merchant-delegate key on its server),
8
+ * opens checkout for it, and then verifies payment SERVER-SIDE by reading the
9
+ * Invoice account ({@link verifyInvoicePaid} in pay.ts). What this protocol
10
+ * reports back to the merchant's browser is a UX hint only: a browser result
11
+ * can be forged by anyone who controls the merchant's page, so it must never
12
+ * release goods by itself.
13
+ *
14
+ * Flow (all messages are plain JSON-able objects, see {@link CheckoutMessage}):
15
+ *
16
+ * merchant page (origin M) popup: X1 Pay app (origin A)
17
+ * ─────────────────────── ──────────────────────────
18
+ * window.open(A/invoice?address=…&embed=1&origin=M&sid=S)
19
+ * ◀── ready {sid, invoice} (targetOrigin M)
20
+ * ack {sid} (targetOrigin A) ──▶
21
+ * ◀── result {sid, invoice, status, signature}
22
+ * popup closed without a result → status "closed" (detected locally)
23
+ *
24
+ * Origin checks on BOTH sides:
25
+ * - the popup posts only to `window.opener` with `targetOrigin` = the
26
+ * merchant origin it was opened for, so the browser drops the message if the
27
+ * opener is anything else; it trusts an `ack` only when `event.source` is
28
+ * its opener and `event.origin` is exactly that origin;
29
+ * - the merchant accepts a message only when `event.source` is the popup it
30
+ * opened, `event.origin` is exactly the X1 Pay app origin, and the session id
31
+ * (`sid`, 128 random bits per checkout) and invoice match.
32
+ *
33
+ * No iframe mode: the X1 Pay app keeps `frame-ancestors 'none'` /
34
+ * `X-Frame-Options: DENY` (clickjacking), and wallet extensions work in a
35
+ * top-level window.
36
+ *
37
+ * Dependency-free on purpose (no SDK imports), so the browser widget bundle
38
+ * stays tiny.
39
+ */
40
+ /** Protocol tag on every message. */
41
+ export declare const CHECKOUT_PROTOCOL = "x1id-checkout";
42
+ /** Protocol version on every message. */
43
+ export declare const CHECKOUT_VERSION = 1;
44
+ /** Where the hosted checkout lives. */
45
+ export declare const DEFAULT_CHECKOUT_APP_ORIGIN = "https://app.x1pay.me";
46
+ /**
47
+ * - `paid`: the invoice page read the Invoice account back as Paid.
48
+ * - `cancelled`: the payer returned to the merchant without paying.
49
+ * - `closed`: the popup was closed without a result (merchant side only, never
50
+ * sent over the wire).
51
+ */
52
+ export type CheckoutStatus = "paid" | "cancelled" | "closed";
53
+ /** What the merchant page learns. A HINT: verify on your server. */
54
+ export interface CheckoutResult {
55
+ /** The invoice address (base58) this checkout was opened for. */
56
+ readonly invoice: string;
57
+ /** The payment transaction, when known (only with `paid`). */
58
+ readonly signature: string | null;
59
+ readonly status: CheckoutStatus;
60
+ }
61
+ export interface CheckoutReadyMessage {
62
+ readonly protocol: typeof CHECKOUT_PROTOCOL;
63
+ readonly version: typeof CHECKOUT_VERSION;
64
+ readonly type: "ready";
65
+ readonly sid: string;
66
+ readonly invoice: string;
67
+ }
68
+ export interface CheckoutAckMessage {
69
+ readonly protocol: typeof CHECKOUT_PROTOCOL;
70
+ readonly version: typeof CHECKOUT_VERSION;
71
+ readonly type: "ack";
72
+ readonly sid: string;
73
+ }
74
+ export interface CheckoutResultMessage {
75
+ readonly protocol: typeof CHECKOUT_PROTOCOL;
76
+ readonly version: typeof CHECKOUT_VERSION;
77
+ readonly type: "result";
78
+ readonly sid: string;
79
+ readonly invoice: string;
80
+ /** Only the two statuses the popup can report. */
81
+ readonly status: "paid" | "cancelled";
82
+ readonly signature: string | null;
83
+ }
84
+ export type CheckoutMessage = CheckoutReadyMessage | CheckoutAckMessage | CheckoutResultMessage;
85
+ /** Shape check for a base58 account address (32-44 chars). */
86
+ export declare function isCheckoutAddress(s: unknown): s is string;
87
+ /** Shape check for a base58 transaction signature (64-88 chars). */
88
+ export declare function isCheckoutSignature(s: unknown): s is string;
89
+ /** A checkout session id: 32 lowercase hex chars (128 bits). */
90
+ export declare function isCheckoutSid(s: unknown): s is string;
91
+ /** 128 fresh random bits as a checkout session id (CSPRNG). */
92
+ export declare function newCheckoutSid(): string;
93
+ /**
94
+ * A web origin the protocol will talk to, or null. It must be exactly an
95
+ * origin (`scheme://host[:port]`, no path, query, fragment or credentials),
96
+ * `https:`, or `http:` on a loopback host for local development. `"null"`
97
+ * (sandboxed / `file:` pages) is refused: messages could not be pinned to it.
98
+ */
99
+ export declare function normalizeCheckoutOrigin(raw: unknown): string | null;
100
+ /**
101
+ * Parse an incoming `event.data` as a protocol message, or null. Strict:
102
+ * right protocol tag and version, a known `type`, only the fields that type
103
+ * carries, each well-formed. Says nothing about WHO sent it: check
104
+ * `event.origin` and `event.source` too (see {@link acceptCheckoutResult} and
105
+ * {@link acceptCheckoutAck}).
106
+ */
107
+ export declare function parseCheckoutMessage(data: unknown): CheckoutMessage | null;
108
+ export declare function checkoutReadyMessage(sid: string, invoice: string): CheckoutReadyMessage;
109
+ export declare function checkoutAckMessage(sid: string): CheckoutAckMessage;
110
+ export declare function checkoutResultMessage(p: {
111
+ readonly sid: string;
112
+ readonly invoice: string;
113
+ readonly status: "paid" | "cancelled";
114
+ readonly signature: string | null;
115
+ }): CheckoutResultMessage;
116
+ /** The parts of a `MessageEvent` the checks read. */
117
+ export interface CheckoutEventLike {
118
+ readonly origin: string;
119
+ readonly source: unknown;
120
+ readonly data: unknown;
121
+ }
122
+ /**
123
+ * Merchant side: the result carried by `ev`, or null unless it comes from
124
+ * exactly `expect.source` (the popup window this page opened) at exactly
125
+ * `expect.appOrigin`, for this checkout's `sid` and `invoice`.
126
+ */
127
+ export declare function acceptCheckoutResult(ev: CheckoutEventLike, expect: {
128
+ readonly appOrigin: string;
129
+ readonly source: unknown;
130
+ readonly sid: string;
131
+ readonly invoice: string;
132
+ }): CheckoutResult | null;
133
+ /**
134
+ * Popup side: true when `ev` is this checkout's `ack`, sent by the window that
135
+ * opened the popup (`expect.opener`) from exactly `expect.origin`.
136
+ */
137
+ export declare function acceptCheckoutAck(ev: CheckoutEventLike, expect: {
138
+ readonly origin: string;
139
+ readonly opener: unknown;
140
+ readonly sid: string;
141
+ }): boolean;
142
+ /** The hosted checkout URL for an invoice. */
143
+ export declare function buildCheckoutUrl(p: {
144
+ readonly invoice: string;
145
+ /** The merchant page's own origin (`location.origin`). */
146
+ readonly origin: string;
147
+ readonly sid: string;
148
+ readonly appOrigin?: string;
149
+ }): string;
150
+ /** Popup side: the checkout parameters of an invoice page URL, or null when it
151
+ * is not an embedded checkout (or any parameter is malformed). */
152
+ export declare function parseCheckoutParams(get: (name: string) => string | null): {
153
+ readonly origin: string;
154
+ readonly sid: string;
155
+ } | null;
156
+ /** The popup window, as far as the controller uses it. */
157
+ export interface CheckoutPopupLike {
158
+ readonly closed: boolean;
159
+ postMessage(message: unknown, targetOrigin: string): void;
160
+ close(): void;
161
+ focus?(): void;
162
+ }
163
+ /** The merchant window, as far as the controller uses it (`window` in a
164
+ * browser; a fake in tests). */
165
+ export interface CheckoutHostLike {
166
+ readonly location: {
167
+ readonly origin: string;
168
+ };
169
+ open(url: string, target: string, features: string): CheckoutPopupLike | null;
170
+ addEventListener(type: "message", listener: (ev: CheckoutEventLike) => void): void;
171
+ removeEventListener(type: "message", listener: (ev: CheckoutEventLike) => void): void;
172
+ setInterval(fn: () => void, ms: number): unknown;
173
+ clearInterval(handle: unknown): void;
174
+ readonly screenX?: number;
175
+ readonly screenY?: number;
176
+ readonly outerWidth?: number;
177
+ readonly outerHeight?: number;
178
+ }
179
+ export interface OpenCheckoutOptions {
180
+ /** The invoice address (base58) the merchant created on chain. */
181
+ readonly invoice: string;
182
+ /** Override the hosted checkout origin (e.g. a testnet deployment). */
183
+ readonly appOrigin?: string;
184
+ /** Called once the popup has proved it is the X1 Pay app for this checkout. */
185
+ readonly onReady?: () => void;
186
+ /** Popup size. */
187
+ readonly width?: number;
188
+ readonly height?: number;
189
+ /** How often to check whether the popup was closed (ms). */
190
+ readonly pollMs?: number;
191
+ /** Injected for tests; defaults to {@link newCheckoutSid}. */
192
+ readonly sid?: string;
193
+ }
194
+ export interface CheckoutSession {
195
+ readonly sid: string;
196
+ /** Settles once: the popup's result, or `closed`. Rejects only on
197
+ * {@link CheckoutSession.abort}. */
198
+ readonly result: Promise<CheckoutResult>;
199
+ /** Stop listening, close the popup, reject `result`. */
200
+ abort(): void;
201
+ }
202
+ /** Thrown by {@link openCheckout} when the browser blocked the popup (call it
203
+ * straight from a click handler). */
204
+ export declare class CheckoutPopupBlockedError extends Error {
205
+ constructor();
206
+ }
207
+ /**
208
+ * Open the hosted checkout for `opts.invoice` in a popup and listen for its
209
+ * result. Call it synchronously from a user gesture (a click), or the browser
210
+ * blocks the popup ({@link CheckoutPopupBlockedError}).
211
+ *
212
+ * The result is a HINT for your UI. Before you ship anything, have your server
213
+ * call `verifyInvoicePaid` with the invoice address and terms YOU stored when
214
+ * you created the invoice.
215
+ */
216
+ export declare function openCheckout(host: CheckoutHostLike, opts: OpenCheckoutOptions): CheckoutSession;