@chainpayhq/sdk 0.0.0-stage → 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.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -2
  3. package/dist/accounts.d.ts +23 -0
  4. package/dist/accounts.js +148 -0
  5. package/dist/cards/accounts.d.ts +156 -0
  6. package/dist/cards/accounts.js +346 -0
  7. package/dist/cards/api.d.ts +379 -0
  8. package/dist/cards/api.js +241 -0
  9. package/dist/cards/commitment.d.ts +86 -0
  10. package/dist/cards/commitment.js +288 -0
  11. package/dist/cards/constants.d.ts +160 -0
  12. package/dist/cards/constants.js +163 -0
  13. package/dist/cards/draft.d.ts +57 -0
  14. package/dist/cards/draft.js +131 -0
  15. package/dist/cards/evidence.d.ts +69 -0
  16. package/dist/cards/evidence.js +41 -0
  17. package/dist/cards/hash.d.ts +9 -0
  18. package/dist/cards/hash.js +47 -0
  19. package/dist/cards/index.d.ts +14 -0
  20. package/dist/cards/index.js +14 -0
  21. package/dist/cards/instructions.d.ts +302 -0
  22. package/dist/cards/instructions.js +474 -0
  23. package/dist/cards/layout.d.ts +33 -0
  24. package/dist/cards/layout.js +137 -0
  25. package/dist/cards/math.d.ts +67 -0
  26. package/dist/cards/math.js +138 -0
  27. package/dist/cards/merchants.d.ts +23 -0
  28. package/dist/cards/merchants.js +38 -0
  29. package/dist/cards/pda.d.ts +42 -0
  30. package/dist/cards/pda.js +93 -0
  31. package/dist/cards/private-repayment.d.ts +198 -0
  32. package/dist/cards/private-repayment.js +485 -0
  33. package/dist/cards/redact.d.ts +18 -0
  34. package/dist/cards/redact.js +103 -0
  35. package/dist/cards/tee.d.ts +253 -0
  36. package/dist/cards/tee.js +605 -0
  37. package/dist/cli.d.ts +31 -0
  38. package/dist/cli.js +351 -0
  39. package/dist/client.d.ts +80 -0
  40. package/dist/client.js +493 -0
  41. package/dist/constants.d.ts +44 -0
  42. package/dist/constants.js +43 -0
  43. package/dist/crossmint-adapt.d.ts +43 -0
  44. package/dist/crossmint-adapt.js +68 -0
  45. package/dist/crossmint-order.d.ts +220 -0
  46. package/dist/crossmint-order.js +638 -0
  47. package/dist/delivery.d.ts +66 -0
  48. package/dist/delivery.js +232 -0
  49. package/dist/encoding.d.ts +51 -0
  50. package/dist/encoding.js +128 -0
  51. package/dist/index.d.ts +27 -0
  52. package/dist/index.js +26 -0
  53. package/dist/known-assets.d.ts +30 -0
  54. package/dist/known-assets.js +49 -0
  55. package/dist/mandate-request.d.ts +123 -0
  56. package/dist/mandate-request.js +401 -0
  57. package/dist/mandate.d.ts +44 -0
  58. package/dist/mandate.js +157 -0
  59. package/dist/ops-snapshot.d.ts +157 -0
  60. package/dist/ops-snapshot.js +356 -0
  61. package/dist/payment-request.d.ts +37 -0
  62. package/dist/payment-request.js +218 -0
  63. package/dist/payment.d.ts +40 -0
  64. package/dist/payment.js +214 -0
  65. package/dist/pda.d.ts +13 -0
  66. package/dist/pda.js +36 -0
  67. package/dist/receipt-export.d.ts +55 -0
  68. package/dist/receipt-export.js +149 -0
  69. package/dist/receipt.d.ts +108 -0
  70. package/dist/receipt.js +213 -0
  71. package/dist/solana.d.ts +5 -0
  72. package/dist/solana.js +20 -0
  73. package/dist/token-capabilities.d.ts +9 -0
  74. package/dist/token-capabilities.js +139 -0
  75. package/dist/token.d.ts +18 -0
  76. package/dist/token.js +55 -0
  77. package/dist/transaction-reader.d.ts +10 -0
  78. package/dist/transaction-reader.js +18 -0
  79. package/dist/transaction-v1.d.ts +18 -0
  80. package/dist/transaction-v1.js +73 -0
  81. package/dist/types.d.ts +267 -0
  82. package/dist/types.js +1 -0
  83. package/dist/x402-adapt.d.ts +20 -0
  84. package/dist/x402-adapt.js +62 -0
  85. package/dist/x402-challenge.d.ts +116 -0
  86. package/dist/x402-challenge.js +385 -0
  87. package/dist/x402.d.ts +18 -0
  88. package/dist/x402.js +35 -0
  89. package/package.json +54 -4
@@ -0,0 +1,66 @@
1
+ import type { Address, PaymentReceipt } from "./types.js";
2
+ export declare const DELIVERY_STATEMENT = "chainpay.response-served";
3
+ export declare const DELIVERY_VERSION = 1;
4
+ export declare const DELIVERY_CLUSTER = "devnet";
5
+ /**
6
+ * Exact UTF-8 body for the cross-language content-hash fixture.
7
+ * Includes non-ASCII and whitespace that must hash as transmitted bytes.
8
+ */
9
+ export declare const DELIVERY_HASH_FIXTURE_UTF8 = "ChainPay served body: caf\u00E9\n\t ";
10
+ /** SHA-256 lowercase hex of {@link DELIVERY_HASH_FIXTURE_UTF8}. */
11
+ export declare const DELIVERY_HASH_FIXTURE_SHA256 = "8e60b641218418bde0cde3b190a028e846ccebed8dc804901b8e6f87b9072eee";
12
+ /** Test-only seller seed (32 bytes of 0x07). Not a live merchant key. */
13
+ export declare const DELIVERY_FIXTURE_SELLER_SEED: Uint8Array<ArrayBuffer>;
14
+ export declare const DELIVERY_FIXTURE_RECEIPT_ADDRESS = "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1";
15
+ export declare const DELIVERY_FIXTURE_SELLER_ADDRESS = "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB";
16
+ /** Exact canonical JSON bytes for cross-language signature fixtures. */
17
+ export declare const DELIVERY_CANONICAL_FIXTURE_JSON = "{\"version\":1,\"statement\":\"chainpay.response-served\",\"cluster\":\"devnet\",\"programId\":\"3H9TV1EPR2BAQgVmcMqpufiZKPXbAMnjHp13LA9Lndv4\",\"receiptAddress\":\"2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1\",\"seller\":\"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB\",\"contentHash\":\"8e60b641218418bde0cde3b190a028e846ccebed8dc804901b8e6f87b9072eee\",\"servedAt\":\"2026-09-15T04:16:00.000Z\"}";
18
+ /** Deterministic Ed25519 signature over {@link DELIVERY_CANONICAL_FIXTURE_JSON}. */
19
+ export declare const DELIVERY_CANONICAL_FIXTURE_SIGNATURE = "K+hQlY/7U7cbhE2229fk/C2g5MHUahSrhBZz4F3SUxTqyVvLgfV0gZkPosZKdFZzIXbawLS3xJQ3XsrtYXqIDg==";
20
+ export type DeliveryAttestationPayload = {
21
+ version: 1;
22
+ statement: typeof DELIVERY_STATEMENT;
23
+ cluster: typeof DELIVERY_CLUSTER;
24
+ programId: Address;
25
+ receiptAddress: Address;
26
+ seller: Address;
27
+ contentHash: string;
28
+ servedAt: string;
29
+ };
30
+ export type SignedDeliveryAttestation = {
31
+ payload: DeliveryAttestationPayload;
32
+ signature: string;
33
+ };
34
+ export type TrustedSellerMapping = {
35
+ cluster: typeof DELIVERY_CLUSTER;
36
+ programId: Address;
37
+ /** Seller signing public keys. These are identities, not ATA addresses. */
38
+ sellers: readonly Address[];
39
+ recipientTokenAccount?: Address;
40
+ };
41
+ export type DeliveryVerification = {
42
+ valid: boolean;
43
+ payload?: DeliveryAttestationPayload;
44
+ reason?: string;
45
+ code?: string;
46
+ };
47
+ export type VerifyDeliveryOptions = {
48
+ trustedSellers?: TrustedSellerMapping;
49
+ receipt?: Pick<PaymentReceipt, "address" | "recipientTokenAccount">;
50
+ programId?: Address;
51
+ now?: Date;
52
+ maxFutureSkewMs?: number;
53
+ };
54
+ export declare function bytesToCanonicalBase64(bytes: Uint8Array): string;
55
+ export declare function canonicalBase64ToBytes(value: string): Uint8Array;
56
+ export declare function sha256HexBytes(bytes: Uint8Array): Promise<string>;
57
+ /**
58
+ * Reject unsupported version and unknown fields before any
59
+ * canonical JSON is produced. This ordering is load-bearing.
60
+ */
61
+ export declare function parseDeliveryPayload(value: unknown): DeliveryAttestationPayload;
62
+ export declare function parseDeliveryEnvelope(value: unknown): SignedDeliveryAttestation;
63
+ export declare function canonicalDeliveryPayload(payload: DeliveryAttestationPayload): string;
64
+ export declare function signDeliveryAttestation(payload: unknown, secretKey: Uint8Array): Promise<SignedDeliveryAttestation>;
65
+ export declare function verifyDeliveryAttestation(envelope: unknown, options?: VerifyDeliveryOptions): Promise<DeliveryVerification>;
66
+ export declare function defaultTrustedSellerMapping(sellers: readonly Address[], programId?: Address): TrustedSellerMapping;
@@ -0,0 +1,232 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import { address } from "./encoding.js";
3
+ import { DEFAULT_PROGRAM_ID } from "./constants.js";
4
+ export const DELIVERY_STATEMENT = "chainpay.response-served";
5
+ export const DELIVERY_VERSION = 1;
6
+ export const DELIVERY_CLUSTER = "devnet";
7
+ const PAYLOAD_KEYS = [
8
+ "version",
9
+ "statement",
10
+ "cluster",
11
+ "programId",
12
+ "receiptAddress",
13
+ "seller",
14
+ "contentHash",
15
+ "servedAt",
16
+ ];
17
+ const ENVELOPE_KEYS = ["payload", "signature"];
18
+ const CONTENT_HASH_PATTERN = /^[0-9a-f]{64}$/;
19
+ const CANONICAL_UTC_ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
20
+ const CANONICAL_BASE64 = /^[A-Za-z0-9+/]+={0,2}$/;
21
+ const ED25519_PKCS8_PREFIX = Uint8Array.from([
22
+ 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20,
23
+ ]);
24
+ /**
25
+ * Exact UTF-8 body for the cross-language content-hash fixture.
26
+ * Includes non-ASCII and whitespace that must hash as transmitted bytes.
27
+ */
28
+ export const DELIVERY_HASH_FIXTURE_UTF8 = "ChainPay served body: café\n\t ";
29
+ /** SHA-256 lowercase hex of {@link DELIVERY_HASH_FIXTURE_UTF8}. */
30
+ export const DELIVERY_HASH_FIXTURE_SHA256 = "8e60b641218418bde0cde3b190a028e846ccebed8dc804901b8e6f87b9072eee";
31
+ /** Test-only seller seed (32 bytes of 0x07). Not a live merchant key. */
32
+ export const DELIVERY_FIXTURE_SELLER_SEED = Uint8Array.from({ length: 32 }, () => 7);
33
+ export const DELIVERY_FIXTURE_RECEIPT_ADDRESS = "2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1";
34
+ export const DELIVERY_FIXTURE_SELLER_ADDRESS = "GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB";
35
+ /** Exact canonical JSON bytes for cross-language signature fixtures. */
36
+ export const DELIVERY_CANONICAL_FIXTURE_JSON = '{"version":1,"statement":"chainpay.response-served","cluster":"devnet","programId":"3H9TV1EPR2BAQgVmcMqpufiZKPXbAMnjHp13LA9Lndv4","receiptAddress":"2KW2XRd9kwqet15Aha2oK3tYvd3nWbTFH1MBiRAv1BE1","seller":"GmaDrppBC7P5ARKV8g3djiwP89vz1jLK23V2GBjuAEGB","contentHash":"8e60b641218418bde0cde3b190a028e846ccebed8dc804901b8e6f87b9072eee","servedAt":"2026-09-15T04:16:00.000Z"}';
37
+ /** Deterministic Ed25519 signature over {@link DELIVERY_CANONICAL_FIXTURE_JSON}. */
38
+ export const DELIVERY_CANONICAL_FIXTURE_SIGNATURE = "K+hQlY/7U7cbhE2229fk/C2g5MHUahSrhBZz4F3SUxTqyVvLgfV0gZkPosZKdFZzIXbawLS3xJQ3XsrtYXqIDg==";
39
+ function isPlainObject(value) {
40
+ return value !== null && typeof value === "object" && !Array.isArray(value);
41
+ }
42
+ function unknownKeys(value, allowed) {
43
+ return Object.keys(value).filter((key) => !allowed.includes(key));
44
+ }
45
+ function validationError(reason, code, payload) {
46
+ return { valid: false, reason, code, ...(payload ? { payload } : {}) };
47
+ }
48
+ function isCanonicalUtcIso(value) {
49
+ if (!CANONICAL_UTC_ISO.test(value))
50
+ return false;
51
+ const parsed = Date.parse(value);
52
+ if (!Number.isFinite(parsed))
53
+ return false;
54
+ return new Date(parsed).toISOString() === value;
55
+ }
56
+ function ed25519Seed(secretKey) {
57
+ if (secretKey.length === 32)
58
+ return new Uint8Array(secretKey);
59
+ if (secretKey.length === 64)
60
+ return new Uint8Array(secretKey.subarray(0, 32));
61
+ throw new Error("Ed25519 secret key must be 32 or 64 bytes");
62
+ }
63
+ function pkcs8FromSeed(seed) {
64
+ const pkcs8 = new Uint8Array(ED25519_PKCS8_PREFIX.length + 32);
65
+ pkcs8.set(ED25519_PKCS8_PREFIX);
66
+ pkcs8.set(seed, ED25519_PKCS8_PREFIX.length);
67
+ return pkcs8.buffer.slice(pkcs8.byteOffset, pkcs8.byteOffset + pkcs8.byteLength);
68
+ }
69
+ export function bytesToCanonicalBase64(bytes) {
70
+ let binary = "";
71
+ for (const byte of bytes)
72
+ binary += String.fromCharCode(byte);
73
+ return btoa(binary);
74
+ }
75
+ export function canonicalBase64ToBytes(value) {
76
+ if (!CANONICAL_BASE64.test(value) || value.length % 4 !== 0) {
77
+ throw new Error("Delivery signature must be canonical base64");
78
+ }
79
+ const binary = atob(value);
80
+ return Uint8Array.from(binary, (character) => character.charCodeAt(0));
81
+ }
82
+ export async function sha256HexBytes(bytes) {
83
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes.slice().buffer);
84
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
85
+ }
86
+ /**
87
+ * Reject unsupported version and unknown fields before any
88
+ * canonical JSON is produced. This ordering is load-bearing.
89
+ */
90
+ export function parseDeliveryPayload(value) {
91
+ if (!isPlainObject(value)) {
92
+ throw new Error("Delivery payload must be an object");
93
+ }
94
+ if (!Object.prototype.hasOwnProperty.call(value, "version") || value.version !== DELIVERY_VERSION) {
95
+ throw new Error("Unsupported delivery attestation version");
96
+ }
97
+ const extra = unknownKeys(value, PAYLOAD_KEYS);
98
+ if (extra.length > 0) {
99
+ throw new Error(`Unknown delivery attestation field: ${extra[0]}`);
100
+ }
101
+ if (value.statement !== DELIVERY_STATEMENT) {
102
+ throw new Error("Unsupported delivery statement");
103
+ }
104
+ if (value.cluster !== DELIVERY_CLUSTER) {
105
+ throw new Error("Unsupported delivery cluster");
106
+ }
107
+ if (typeof value.contentHash !== "string" || !CONTENT_HASH_PATTERN.test(value.contentHash)) {
108
+ throw new Error("contentHash must be 64-char lowercase SHA-256 hex");
109
+ }
110
+ if (typeof value.servedAt !== "string" || !isCanonicalUtcIso(value.servedAt)) {
111
+ throw new Error("servedAt must be a canonical UTC ISO-8601 timestamp");
112
+ }
113
+ if (typeof value.programId !== "string" || typeof value.receiptAddress !== "string" || typeof value.seller !== "string") {
114
+ throw new Error("programId, receiptAddress, and seller must be Solana addresses");
115
+ }
116
+ return {
117
+ version: 1,
118
+ statement: DELIVERY_STATEMENT,
119
+ cluster: DELIVERY_CLUSTER,
120
+ programId: address(value.programId),
121
+ receiptAddress: address(value.receiptAddress),
122
+ seller: address(value.seller),
123
+ contentHash: value.contentHash,
124
+ servedAt: value.servedAt,
125
+ };
126
+ }
127
+ export function parseDeliveryEnvelope(value) {
128
+ if (!isPlainObject(value)) {
129
+ throw new Error("Delivery attestation must be an object");
130
+ }
131
+ const extra = unknownKeys(value, ENVELOPE_KEYS);
132
+ if (extra.length > 0) {
133
+ throw new Error(`Unknown delivery envelope field: ${extra[0]}`);
134
+ }
135
+ if (typeof value.signature !== "string") {
136
+ throw new Error("Delivery signature is required");
137
+ }
138
+ return {
139
+ payload: parseDeliveryPayload(value.payload),
140
+ signature: value.signature,
141
+ };
142
+ }
143
+ export function canonicalDeliveryPayload(payload) {
144
+ const parsed = parseDeliveryPayload(payload);
145
+ return JSON.stringify({
146
+ version: 1,
147
+ statement: DELIVERY_STATEMENT,
148
+ cluster: DELIVERY_CLUSTER,
149
+ programId: parsed.programId,
150
+ receiptAddress: parsed.receiptAddress,
151
+ seller: parsed.seller,
152
+ contentHash: parsed.contentHash,
153
+ servedAt: parsed.servedAt,
154
+ });
155
+ }
156
+ export async function signDeliveryAttestation(payload, secretKey) {
157
+ const parsed = parseDeliveryPayload(payload);
158
+ const message = new TextEncoder().encode(canonicalDeliveryPayload(parsed));
159
+ const key = await globalThis.crypto.subtle.importKey("pkcs8", pkcs8FromSeed(ed25519Seed(secretKey)), { name: "Ed25519" }, false, ["sign"]);
160
+ const signature = new Uint8Array(await globalThis.crypto.subtle.sign("Ed25519", key, message.slice().buffer));
161
+ return { payload: parsed, signature: bytesToCanonicalBase64(signature) };
162
+ }
163
+ function trustedSellerAllows(payload, mapping) {
164
+ if (mapping.cluster !== payload.cluster)
165
+ return "Delivery cluster is not trusted";
166
+ if (address(mapping.programId) !== payload.programId)
167
+ return "Delivery programId is not trusted";
168
+ const allowed = new Set(mapping.sellers.map((seller) => address(seller)));
169
+ if (!allowed.has(payload.seller))
170
+ return "Unknown delivery seller";
171
+ return undefined;
172
+ }
173
+ export async function verifyDeliveryAttestation(envelope, options = {}) {
174
+ let parsed;
175
+ try {
176
+ parsed = parseDeliveryEnvelope(envelope);
177
+ }
178
+ catch (error) {
179
+ return validationError(error instanceof Error ? error.message : String(error), "invalid_payload");
180
+ }
181
+ try {
182
+ // Fail closed on identity. Without a trusted-seller mapping the only thing
183
+ // left to check is that the payload is signed by the key the payload itself
184
+ // names, which any anonymous poster can satisfy with a throwaway keypair.
185
+ // Every caller in this repo already passes a mapping; this stops the next
186
+ // one from accidentally accepting a self-signed statement.
187
+ if (!options.trustedSellers) {
188
+ return validationError("A trusted seller mapping is required to verify a delivery attestation.", "unknown_seller", parsed.payload);
189
+ }
190
+ {
191
+ const trustError = trustedSellerAllows(parsed.payload, options.trustedSellers);
192
+ if (trustError)
193
+ return validationError(trustError, "unknown_seller", parsed.payload);
194
+ if (options.trustedSellers.recipientTokenAccount
195
+ && options.receipt
196
+ && address(options.trustedSellers.recipientTokenAccount) !== address(options.receipt.recipientTokenAccount)) {
197
+ return validationError("Trusted seller does not match receipt recipient", "recipient_mismatch", parsed.payload);
198
+ }
199
+ }
200
+ if (options.programId && address(options.programId) !== parsed.payload.programId) {
201
+ return validationError("Delivery programId does not match expected program", "program_mismatch", parsed.payload);
202
+ }
203
+ if (options.receipt && address(options.receipt.address) !== parsed.payload.receiptAddress) {
204
+ return validationError("Delivery receiptAddress does not match the receipt", "receipt_mismatch", parsed.payload);
205
+ }
206
+ const now = options.now ?? new Date();
207
+ const servedAt = Date.parse(parsed.payload.servedAt);
208
+ const maxFutureSkewMs = options.maxFutureSkewMs ?? 5 * 60_000;
209
+ if (servedAt - now.getTime() > maxFutureSkewMs) {
210
+ return validationError("Delivery servedAt is unreasonably far in the future", "invalid_timestamp", parsed.payload);
211
+ }
212
+ const signature = canonicalBase64ToBytes(parsed.signature);
213
+ if (signature.length !== 64)
214
+ return validationError("Ed25519 signature must be 64 bytes", "invalid_signature", parsed.payload);
215
+ const key = await globalThis.crypto.subtle.importKey("raw", new PublicKey(parsed.payload.seller).toBytes().slice().buffer, { name: "Ed25519" }, false, ["verify"]);
216
+ const message = new TextEncoder().encode(canonicalDeliveryPayload(parsed.payload));
217
+ const validSignature = await globalThis.crypto.subtle.verify("Ed25519", key, signature.slice().buffer, message.slice().buffer);
218
+ if (!validSignature)
219
+ return validationError("Delivery signature is invalid", "invalid_signature", parsed.payload);
220
+ return { valid: true, payload: parsed.payload };
221
+ }
222
+ catch (error) {
223
+ return validationError(error instanceof Error ? error.message : String(error), "invalid_payload", parsed.payload);
224
+ }
225
+ }
226
+ export function defaultTrustedSellerMapping(sellers, programId = DEFAULT_PROGRAM_ID) {
227
+ return {
228
+ cluster: DELIVERY_CLUSTER,
229
+ programId: address(programId),
230
+ sellers: sellers.map((seller) => address(seller)),
231
+ };
232
+ }
@@ -0,0 +1,51 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import type { AccountMeta, Address, ChainPayInstruction, TokenProgram } from "./types.js";
3
+ export declare function publicKey(value: Address): PublicKey;
4
+ export declare function address(value: Address): Address;
5
+ export declare function bytes32(value: Uint8Array, name: string): Uint8Array;
6
+ export declare function writeU64(value: bigint, name: string): Uint8Array;
7
+ export declare function readU64(data: Uint8Array, offset: number): bigint;
8
+ export declare function readU8(data: Uint8Array, offset: number): number;
9
+ export declare function readBytes32(data: Uint8Array, offset: number, name: string): Uint8Array;
10
+ export declare function assertDiscriminator(data: Uint8Array, discriminator: Uint8Array, accountName: string): void;
11
+ export declare function readPublicKey(data: Uint8Array, offset: number): Address;
12
+ export declare function isMandateNonce(value: Address): boolean;
13
+ export declare function concat(...parts: Uint8Array[]): Uint8Array;
14
+ export declare function meta(value: Address, isWritable?: boolean, isSigner?: boolean): AccountMeta;
15
+ export declare function tokenProgramAddress(tokenProgram: TokenProgram): Address;
16
+ export declare function tokenProgramFromAddress(value: Address): TokenProgram | undefined;
17
+ export declare function instruction(name: string, programId: Address, keys: AccountMeta[], data: Uint8Array): ChainPayInstruction;
18
+ export declare function encodeCreateMandate(params: {
19
+ approvedAgent: Address;
20
+ sourceTokenAccount: Address;
21
+ allowedMint: Address;
22
+ maxPerPayment: bigint;
23
+ totalLimit: bigint;
24
+ expiresAtSlot: bigint;
25
+ maxPaymentCount: bigint;
26
+ cooldownSlots: bigint;
27
+ mandateNonce: Address;
28
+ }): Uint8Array;
29
+ export declare function encodeInitializeConfig(supportedMints: readonly Address[]): Uint8Array;
30
+ export declare function encodeRegisterAsset(mint: Address): Uint8Array;
31
+ export declare function encodeSetAssetStatus(enabled: boolean): Uint8Array;
32
+ export declare function encodeUpdateMandate(params: {
33
+ approvedAgent: Address;
34
+ maxPerPayment: bigint;
35
+ totalLimit: bigint;
36
+ expiresAtSlot: bigint;
37
+ maxPaymentCount: bigint;
38
+ cooldownSlots: bigint;
39
+ paused: boolean;
40
+ }): Uint8Array;
41
+ export declare function encodePayment(params: {
42
+ invoiceHash: Uint8Array;
43
+ paymentId: Uint8Array;
44
+ signatureReference: Uint8Array;
45
+ amount: bigint;
46
+ }): Uint8Array;
47
+ export declare function encodePauseMandate(): Uint8Array;
48
+ export declare function encodeRevokeMandate(): Uint8Array;
49
+ export declare function encodeApproveChecked(amount: bigint, decimals: number): Uint8Array;
50
+ export declare function encodeRevokeDelegate(): Uint8Array;
51
+ export declare function systemProgramMeta(): AccountMeta;
@@ -0,0 +1,128 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import { DISCRIMINATORS, MANDATE_NONCE_PREFIX, SYSTEM_PROGRAM_ID } from "./constants.js";
3
+ export function publicKey(value) {
4
+ try {
5
+ return new PublicKey(value);
6
+ }
7
+ catch {
8
+ throw new Error(`Invalid Solana address: ${value}`);
9
+ }
10
+ }
11
+ export function address(value) {
12
+ return publicKey(value).toBase58();
13
+ }
14
+ export function bytes32(value, name) {
15
+ if (value.length !== 32)
16
+ throw new Error(`${name} must be exactly 32 bytes`);
17
+ return new Uint8Array(value);
18
+ }
19
+ export function writeU64(value, name) {
20
+ if (value < 0n || value > 18446744073709551615n) {
21
+ throw new Error(`${name} must fit in an unsigned 64-bit integer`);
22
+ }
23
+ const bytes = new Uint8Array(8);
24
+ new DataView(bytes.buffer).setBigUint64(0, value, true);
25
+ return bytes;
26
+ }
27
+ export function readU64(data, offset) {
28
+ if (offset + 8 > data.length)
29
+ throw new Error("Account data ended while reading u64");
30
+ return new DataView(data.buffer, data.byteOffset + offset, 8).getBigUint64(0, true);
31
+ }
32
+ export function readU8(data, offset) {
33
+ if (offset >= data.length)
34
+ throw new Error("Account data ended while reading u8");
35
+ return data[offset];
36
+ }
37
+ export function readBytes32(data, offset, name) {
38
+ if (offset + 32 > data.length) {
39
+ throw new Error(`Account data ended while reading ${name}`);
40
+ }
41
+ return new Uint8Array(data.slice(offset, offset + 32));
42
+ }
43
+ export function assertDiscriminator(data, discriminator, accountName) {
44
+ if (data.length < discriminator.length) {
45
+ throw new Error(`${accountName} account data is truncated`);
46
+ }
47
+ for (let index = 0; index < discriminator.length; index += 1) {
48
+ if (data[index] !== discriminator[index]) {
49
+ throw new Error(`Invalid ${accountName} account discriminator`);
50
+ }
51
+ }
52
+ }
53
+ export function readPublicKey(data, offset) {
54
+ if (offset + 32 > data.length)
55
+ throw new Error("Account data ended while reading public key");
56
+ return new PublicKey(data.slice(offset, offset + 32)).toBase58();
57
+ }
58
+ export function isMandateNonce(value) {
59
+ const bytes = publicKey(value).toBytes();
60
+ return MANDATE_NONCE_PREFIX.every((byte, index) => bytes[index] === byte);
61
+ }
62
+ export function concat(...parts) {
63
+ const result = new Uint8Array(parts.reduce((total, part) => total + part.length, 0));
64
+ let offset = 0;
65
+ for (const part of parts) {
66
+ result.set(part, offset);
67
+ offset += part.length;
68
+ }
69
+ return result;
70
+ }
71
+ export function meta(value, isWritable = false, isSigner = false) {
72
+ return { address: address(value), isWritable, isSigner };
73
+ }
74
+ export function tokenProgramAddress(tokenProgram) {
75
+ return tokenProgram === "token-2022"
76
+ ? "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
77
+ : "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
78
+ }
79
+ export function tokenProgramFromAddress(value) {
80
+ const normalized = address(value);
81
+ if (normalized === tokenProgramAddress("spl-token"))
82
+ return "spl-token";
83
+ if (normalized === tokenProgramAddress("token-2022"))
84
+ return "token-2022";
85
+ return undefined;
86
+ }
87
+ export function instruction(name, programId, keys, data) {
88
+ return { name, programId: address(programId), keys, data: new Uint8Array(data) };
89
+ }
90
+ export function encodeCreateMandate(params) {
91
+ return concat(DISCRIMINATORS.createMandate, publicKey(params.approvedAgent).toBytes(), publicKey(params.sourceTokenAccount).toBytes(), publicKey(params.allowedMint).toBytes(), writeU64(params.maxPerPayment, "maxPerPayment"), writeU64(params.totalLimit, "totalLimit"), writeU64(params.expiresAtSlot, "expiresAtSlot"), writeU64(params.maxPaymentCount, "maxPaymentCount"), writeU64(params.cooldownSlots, "cooldownSlots"), publicKey(params.mandateNonce).toBytes());
92
+ }
93
+ export function encodeInitializeConfig(supportedMints) {
94
+ if (supportedMints.length !== 3) {
95
+ throw new Error("initialize_config requires exactly three mint slots");
96
+ }
97
+ return concat(DISCRIMINATORS.initializeConfig, ...supportedMints.map((mint) => publicKey(mint).toBytes()));
98
+ }
99
+ export function encodeRegisterAsset(mint) {
100
+ return concat(DISCRIMINATORS.registerAsset, publicKey(mint).toBytes());
101
+ }
102
+ export function encodeSetAssetStatus(enabled) {
103
+ return concat(DISCRIMINATORS.setAssetStatus, Uint8Array.of(enabled ? 1 : 0));
104
+ }
105
+ export function encodeUpdateMandate(params) {
106
+ return concat(DISCRIMINATORS.updateMandate, publicKey(params.approvedAgent).toBytes(), writeU64(params.maxPerPayment, "maxPerPayment"), writeU64(params.totalLimit, "totalLimit"), writeU64(params.expiresAtSlot, "expiresAtSlot"), writeU64(params.maxPaymentCount, "maxPaymentCount"), writeU64(params.cooldownSlots, "cooldownSlots"), Uint8Array.of(params.paused ? 1 : 0));
107
+ }
108
+ export function encodePayment(params) {
109
+ return concat(DISCRIMINATORS.executePayment, bytes32(params.invoiceHash, "invoiceHash"), bytes32(params.paymentId, "paymentId"), bytes32(params.signatureReference, "signatureReference"), writeU64(params.amount, "amount"));
110
+ }
111
+ export function encodePauseMandate() {
112
+ return new Uint8Array(DISCRIMINATORS.pauseMandate);
113
+ }
114
+ export function encodeRevokeMandate() {
115
+ return new Uint8Array(DISCRIMINATORS.revokeMandate);
116
+ }
117
+ export function encodeApproveChecked(amount, decimals) {
118
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
119
+ throw new Error("Token decimals must be between 0 and 255");
120
+ }
121
+ return concat(DISCRIMINATORS.approveChecked, writeU64(amount, "delegateAmount"), Uint8Array.of(decimals));
122
+ }
123
+ export function encodeRevokeDelegate() {
124
+ return new Uint8Array(DISCRIMINATORS.revokeDelegate);
125
+ }
126
+ export function systemProgramMeta() {
127
+ return meta(SYSTEM_PROGRAM_ID);
128
+ }
@@ -0,0 +1,27 @@
1
+ export * from "./accounts.js";
2
+ export * from "./client.js";
3
+ export * from "./constants.js";
4
+ export * from "./crossmint-adapt.js";
5
+ export * from "./crossmint-order.js";
6
+ export * from "./delivery.js";
7
+ export * from "./encoding.js";
8
+ export * from "./known-assets.js";
9
+ export * from "./mandate.js";
10
+ export * from "./ops-snapshot.js";
11
+ export * from "./payment.js";
12
+ export * from "./payment-request.js";
13
+ export * from "./mandate-request.js";
14
+ export * from "./pda.js";
15
+ export * from "./receipt.js";
16
+ export * from "./receipt-export.js";
17
+ export * from "./solana.js";
18
+ export * from "./token.js";
19
+ export * from "./token-capabilities.js";
20
+ export * from "./types.js";
21
+ export * from "./x402.js";
22
+ export * from "./x402-challenge.js";
23
+ export * from "./x402-adapt.js";
24
+ export * from "./cards/index.js";
25
+ export { decodeSupportedTransaction } from "./transaction-reader.js";
26
+ export { DEFAULT_TRANSACTION_VERSION, V1_COMPUTE_UNIT_LIMIT, V1_LOADED_ACCOUNTS_DATA_SIZE_LIMIT, compileV1TransactionBytes, systemTransferInstruction, } from "./transaction-v1.js";
27
+ export type { ProductionTransactionVersion, V1CompileOptions } from "./transaction-v1.js";
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ export * from "./accounts.js";
2
+ export * from "./client.js";
3
+ export * from "./constants.js";
4
+ export * from "./crossmint-adapt.js";
5
+ export * from "./crossmint-order.js";
6
+ export * from "./delivery.js";
7
+ export * from "./encoding.js";
8
+ export * from "./known-assets.js";
9
+ export * from "./mandate.js";
10
+ export * from "./ops-snapshot.js";
11
+ export * from "./payment.js";
12
+ export * from "./payment-request.js";
13
+ export * from "./mandate-request.js";
14
+ export * from "./pda.js";
15
+ export * from "./receipt.js";
16
+ export * from "./receipt-export.js";
17
+ export * from "./solana.js";
18
+ export * from "./token.js";
19
+ export * from "./token-capabilities.js";
20
+ export * from "./types.js";
21
+ export * from "./x402.js";
22
+ export * from "./x402-challenge.js";
23
+ export * from "./x402-adapt.js";
24
+ export * from "./cards/index.js";
25
+ export { decodeSupportedTransaction } from "./transaction-reader.js";
26
+ export { DEFAULT_TRANSACTION_VERSION, V1_COMPUTE_UNIT_LIMIT, V1_LOADED_ACCOUNTS_DATA_SIZE_LIMIT, compileV1TransactionBytes, systemTransferInstruction, } from "./transaction-v1.js";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The mints ChainPay can name, and the order it shows them in.
3
+ *
4
+ * Naming a mint here is presentation only. Whether an asset can be paid at all
5
+ * is decided on chain by the registry account the protocol authority creates,
6
+ * and a mint absent from this table still works — it is labelled by its address
7
+ * instead. The two must be kept apart: adding a row here does not enable a
8
+ * token, and enabling a token on chain does not require a row here.
9
+ *
10
+ * Devnet and mainnet mints for one asset share a label, so the dashboard reads
11
+ * the same on either cluster without the caller knowing which it is on.
12
+ */
13
+ export type KnownAsset = {
14
+ label: string;
15
+ /** Lower sorts first, so the assets an owner expects lead the list. */
16
+ order: number;
17
+ /** Every mint that is this asset, across clusters. */
18
+ mints: readonly string[];
19
+ };
20
+ export declare const KNOWN_ASSETS: readonly KnownAsset[];
21
+ export declare function knownAsset(mint: string): KnownAsset | undefined;
22
+ /** The order an unnamed mint sorts at: after everything this table names. */
23
+ export declare const UNKNOWN_ASSET_ORDER: number;
24
+ export declare function assetOrder(mint: string): number;
25
+ /**
26
+ * The symbol to print beside an amount in this mint. Unknown mints fall back
27
+ * to the neutral word rather than a guessed ticker, because a wrong symbol
28
+ * beside an amount misidentifies the money.
29
+ */
30
+ export declare function assetLabel(mint: string, fallback?: string): string;
@@ -0,0 +1,49 @@
1
+ export const KNOWN_ASSETS = [
2
+ {
3
+ label: "USDC",
4
+ order: 0,
5
+ mints: [
6
+ "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU", // devnet
7
+ "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // mainnet
8
+ ],
9
+ },
10
+ {
11
+ label: "PYUSD",
12
+ order: 1,
13
+ mints: [
14
+ "CXk2AMBfi3TwaEL2468s6zP8xq9NxTXjp9gjMgzeUynM", // devnet, Token-2022
15
+ "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo", // mainnet, Token-2022
16
+ ],
17
+ },
18
+ {
19
+ label: "EURC",
20
+ order: 2,
21
+ mints: [
22
+ "HzwqbKZw8HxMN6bF2yFZNrht3c2iXXzpKcFu7uBEDKtr", // devnet
23
+ ],
24
+ },
25
+ {
26
+ label: "USDG",
27
+ order: 3,
28
+ mints: [
29
+ "4F6PM96JJxngmHnZLBh9n58RH4aTVNWvDs2nuwrT5BP7", // devnet, Token-2022
30
+ ],
31
+ },
32
+ ];
33
+ const byMint = new Map(KNOWN_ASSETS.flatMap((asset) => asset.mints.map((mint) => [mint, asset])));
34
+ export function knownAsset(mint) {
35
+ return byMint.get(mint);
36
+ }
37
+ /** The order an unnamed mint sorts at: after everything this table names. */
38
+ export const UNKNOWN_ASSET_ORDER = KNOWN_ASSETS.length;
39
+ export function assetOrder(mint) {
40
+ return byMint.get(mint)?.order ?? UNKNOWN_ASSET_ORDER;
41
+ }
42
+ /**
43
+ * The symbol to print beside an amount in this mint. Unknown mints fall back
44
+ * to the neutral word rather than a guessed ticker, because a wrong symbol
45
+ * beside an amount misidentifies the money.
46
+ */
47
+ export function assetLabel(mint, fallback = "tokens") {
48
+ return byMint.get(mint)?.label ?? fallback;
49
+ }