@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.
- package/LICENSE +21 -0
- package/README.md +42 -2
- package/dist/accounts.d.ts +23 -0
- package/dist/accounts.js +148 -0
- package/dist/cards/accounts.d.ts +156 -0
- package/dist/cards/accounts.js +346 -0
- package/dist/cards/api.d.ts +379 -0
- package/dist/cards/api.js +241 -0
- package/dist/cards/commitment.d.ts +86 -0
- package/dist/cards/commitment.js +288 -0
- package/dist/cards/constants.d.ts +160 -0
- package/dist/cards/constants.js +163 -0
- package/dist/cards/draft.d.ts +57 -0
- package/dist/cards/draft.js +131 -0
- package/dist/cards/evidence.d.ts +69 -0
- package/dist/cards/evidence.js +41 -0
- package/dist/cards/hash.d.ts +9 -0
- package/dist/cards/hash.js +47 -0
- package/dist/cards/index.d.ts +14 -0
- package/dist/cards/index.js +14 -0
- package/dist/cards/instructions.d.ts +302 -0
- package/dist/cards/instructions.js +474 -0
- package/dist/cards/layout.d.ts +33 -0
- package/dist/cards/layout.js +137 -0
- package/dist/cards/math.d.ts +67 -0
- package/dist/cards/math.js +138 -0
- package/dist/cards/merchants.d.ts +23 -0
- package/dist/cards/merchants.js +38 -0
- package/dist/cards/pda.d.ts +42 -0
- package/dist/cards/pda.js +93 -0
- package/dist/cards/private-repayment.d.ts +198 -0
- package/dist/cards/private-repayment.js +485 -0
- package/dist/cards/redact.d.ts +18 -0
- package/dist/cards/redact.js +103 -0
- package/dist/cards/tee.d.ts +253 -0
- package/dist/cards/tee.js +605 -0
- package/dist/cli.d.ts +31 -0
- package/dist/cli.js +351 -0
- package/dist/client.d.ts +80 -0
- package/dist/client.js +493 -0
- package/dist/constants.d.ts +44 -0
- package/dist/constants.js +43 -0
- package/dist/crossmint-adapt.d.ts +43 -0
- package/dist/crossmint-adapt.js +68 -0
- package/dist/crossmint-order.d.ts +220 -0
- package/dist/crossmint-order.js +638 -0
- package/dist/delivery.d.ts +66 -0
- package/dist/delivery.js +232 -0
- package/dist/encoding.d.ts +51 -0
- package/dist/encoding.js +128 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +26 -0
- package/dist/known-assets.d.ts +30 -0
- package/dist/known-assets.js +49 -0
- package/dist/mandate-request.d.ts +123 -0
- package/dist/mandate-request.js +401 -0
- package/dist/mandate.d.ts +44 -0
- package/dist/mandate.js +157 -0
- package/dist/ops-snapshot.d.ts +157 -0
- package/dist/ops-snapshot.js +356 -0
- package/dist/payment-request.d.ts +37 -0
- package/dist/payment-request.js +218 -0
- package/dist/payment.d.ts +40 -0
- package/dist/payment.js +214 -0
- package/dist/pda.d.ts +13 -0
- package/dist/pda.js +36 -0
- package/dist/receipt-export.d.ts +55 -0
- package/dist/receipt-export.js +149 -0
- package/dist/receipt.d.ts +108 -0
- package/dist/receipt.js +213 -0
- package/dist/solana.d.ts +5 -0
- package/dist/solana.js +20 -0
- package/dist/token-capabilities.d.ts +9 -0
- package/dist/token-capabilities.js +139 -0
- package/dist/token.d.ts +18 -0
- package/dist/token.js +55 -0
- package/dist/transaction-reader.d.ts +10 -0
- package/dist/transaction-reader.js +18 -0
- package/dist/transaction-v1.d.ts +18 -0
- package/dist/transaction-v1.js +73 -0
- package/dist/types.d.ts +267 -0
- package/dist/types.js +1 -0
- package/dist/x402-adapt.d.ts +20 -0
- package/dist/x402-adapt.js +62 -0
- package/dist/x402-challenge.d.ts +116 -0
- package/dist/x402-challenge.js +385 -0
- package/dist/x402.d.ts +18 -0
- package/dist/x402.js +35 -0
- package/package.json +54 -4
package/dist/token.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, } from "./constants.js";
|
|
3
|
+
import { instruction, meta, publicKey, systemProgramMeta, tokenProgramAddress, } from "./encoding.js";
|
|
4
|
+
export function deriveAssociatedTokenAddress(owner, mint, tokenProgram) {
|
|
5
|
+
// The ATA program derives addresses from owner, token program, and mint.
|
|
6
|
+
// Keeping this helper in the SDK avoids making callers duplicate the
|
|
7
|
+
// classic SPL versus Token-2022 distinction.
|
|
8
|
+
return PublicKey.findProgramAddressSync([
|
|
9
|
+
publicKey(owner).toBytes(),
|
|
10
|
+
publicKey(tokenProgramAddress(tokenProgram)).toBytes(),
|
|
11
|
+
publicKey(mint).toBytes(),
|
|
12
|
+
], publicKey(ASSOCIATED_TOKEN_PROGRAM_ID))[0].toBase58();
|
|
13
|
+
}
|
|
14
|
+
export function buildCreateAssociatedTokenAccountInstruction(input) {
|
|
15
|
+
const associatedTokenAccount = deriveAssociatedTokenAddress(input.owner, input.mint, input.tokenProgram);
|
|
16
|
+
return instruction("create_associated_token_account", ASSOCIATED_TOKEN_PROGRAM_ID, [
|
|
17
|
+
meta(input.payer, true, true),
|
|
18
|
+
meta(associatedTokenAccount, true),
|
|
19
|
+
meta(input.owner),
|
|
20
|
+
meta(input.mint),
|
|
21
|
+
systemProgramMeta(),
|
|
22
|
+
meta(tokenProgramAddress(input.tokenProgram)),
|
|
23
|
+
],
|
|
24
|
+
// 1 is CreateIdempotent. Empty data is the legacy Create, which fails
|
|
25
|
+
// when the account already exists and Phantom shows that as a red banner.
|
|
26
|
+
new Uint8Array([1]));
|
|
27
|
+
}
|
|
28
|
+
export function associatedTokenProgramAddress() {
|
|
29
|
+
return ASSOCIATED_TOKEN_PROGRAM_ID;
|
|
30
|
+
}
|
|
31
|
+
export function readTokenAccountFields(data) {
|
|
32
|
+
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
33
|
+
if (bytes.length < 165)
|
|
34
|
+
return null;
|
|
35
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
36
|
+
const delegateOption = view.getUint32(72, true);
|
|
37
|
+
return {
|
|
38
|
+
mint: new PublicKey(bytes.slice(0, 32)).toBase58(),
|
|
39
|
+
owner: new PublicKey(bytes.slice(32, 64)).toBase58(),
|
|
40
|
+
balance: view.getBigUint64(64, true),
|
|
41
|
+
delegate: delegateOption === 0 ? null : new PublicKey(bytes.slice(76, 108)).toBase58(),
|
|
42
|
+
delegatedAmount: bytes.length >= 129 ? view.getBigUint64(121, true) : 0n,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function paymentPreflightContextFromTokenAccount(data) {
|
|
46
|
+
const fields = readTokenAccountFields(data);
|
|
47
|
+
if (!fields)
|
|
48
|
+
return null;
|
|
49
|
+
return {
|
|
50
|
+
sourceBalance: fields.balance,
|
|
51
|
+
sourceOwner: fields.owner,
|
|
52
|
+
delegate: fields.delegate,
|
|
53
|
+
delegatedAmount: fields.delegatedAmount,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Read-only official codec boundary. Construction defaults to legacy; v1 compile is explicit. */
|
|
2
|
+
export declare function decodeSupportedTransaction(bytes: Uint8Array): {
|
|
3
|
+
transaction: Readonly<{
|
|
4
|
+
messageBytes: import("@solana/transactions").TransactionMessageBytes;
|
|
5
|
+
signatures: import("@solana/transactions").SignaturesMap;
|
|
6
|
+
}>;
|
|
7
|
+
message: import("@solana/transaction-messages").CompiledTransactionMessage & Readonly<{
|
|
8
|
+
lifetimeToken: string;
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getTransactionDecoder, getTransactionEncoder } from "@solana/transactions";
|
|
2
|
+
import { getCompiledTransactionMessageDecoder, getCompiledTransactionMessageEncoder } from "@solana/transaction-messages";
|
|
3
|
+
/** Read-only official codec boundary. Construction defaults to legacy; v1 compile is explicit. */
|
|
4
|
+
export function decodeSupportedTransaction(bytes) {
|
|
5
|
+
if (bytes.length === 0 || bytes.length > 4096)
|
|
6
|
+
throw new Error("Transaction exceeds the supported wire size");
|
|
7
|
+
const transaction = getTransactionDecoder().decode(bytes);
|
|
8
|
+
const message = getCompiledTransactionMessageDecoder().decode(transaction.messageBytes);
|
|
9
|
+
if (message.version !== 1 && bytes.length > 1232)
|
|
10
|
+
throw new Error("Legacy/v0 transaction exceeds 1232 bytes");
|
|
11
|
+
const canonicalMessage = getCompiledTransactionMessageEncoder().encode(message);
|
|
12
|
+
if (canonicalMessage.length !== transaction.messageBytes.length || canonicalMessage.some((value, index) => value !== transaction.messageBytes[index]))
|
|
13
|
+
throw new Error("Noncanonical transaction message bytes");
|
|
14
|
+
const canonical = getTransactionEncoder().encode(transaction);
|
|
15
|
+
if (canonical.length !== bytes.length || canonical.some((value, index) => value !== bytes[index]))
|
|
16
|
+
throw new Error("Noncanonical transaction wire bytes");
|
|
17
|
+
return { transaction, message };
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Address, ChainPayInstruction, PreparedTransaction } from "./types.js";
|
|
2
|
+
/** Dashboard and unsigned wallets stay on this unless compile is called with version 1. */
|
|
3
|
+
export declare const DEFAULT_TRANSACTION_VERSION: "legacy";
|
|
4
|
+
export type ProductionTransactionVersion = typeof DEFAULT_TRANSACTION_VERSION | 1;
|
|
5
|
+
/** Explicit v1 budgets. Defaults are unused; v1 treats omitted limits as zero. */
|
|
6
|
+
export declare const V1_COMPUTE_UNIT_LIMIT = 200000;
|
|
7
|
+
export declare const V1_LOADED_ACCOUNTS_DATA_SIZE_LIMIT = 65536;
|
|
8
|
+
export type V1CompileOptions = {
|
|
9
|
+
version: 1;
|
|
10
|
+
lifetime: {
|
|
11
|
+
blockhash: string;
|
|
12
|
+
lastValidBlockHeight: bigint;
|
|
13
|
+
};
|
|
14
|
+
computeUnitLimit: number;
|
|
15
|
+
loadedAccountsDataSizeLimit: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function systemTransferInstruction(from: Address, to: Address, lamports: bigint): ChainPayInstruction;
|
|
18
|
+
export declare function compileV1TransactionBytes(prepared: PreparedTransaction, options: V1CompileOptions): Uint8Array;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { address as kitAddress } from "@solana/addresses";
|
|
2
|
+
import { AccountRole } from "@solana/instructions";
|
|
3
|
+
import { appendTransactionMessageInstructions, createTransactionMessage, setTransactionMessageComputeUnitLimit, setTransactionMessageFeePayer, setTransactionMessageLifetimeUsingBlockhash, setTransactionMessageLoadedAccountsDataSizeLimit, } from "@solana/transaction-messages";
|
|
4
|
+
import { compileTransaction, getTransactionEncoder } from "@solana/transactions";
|
|
5
|
+
import { SYSTEM_PROGRAM_ID } from "./constants.js";
|
|
6
|
+
import { address } from "./encoding.js";
|
|
7
|
+
/** Dashboard and unsigned wallets stay on this unless compile is called with version 1. */
|
|
8
|
+
export const DEFAULT_TRANSACTION_VERSION = "legacy";
|
|
9
|
+
/** Explicit v1 budgets. Defaults are unused; v1 treats omitted limits as zero. */
|
|
10
|
+
export const V1_COMPUTE_UNIT_LIMIT = 200_000;
|
|
11
|
+
export const V1_LOADED_ACCOUNTS_DATA_SIZE_LIMIT = 65_536;
|
|
12
|
+
function accountRole(key) {
|
|
13
|
+
if (key.isSigner && key.isWritable)
|
|
14
|
+
return AccountRole.WRITABLE_SIGNER;
|
|
15
|
+
if (key.isSigner)
|
|
16
|
+
return AccountRole.READONLY_SIGNER;
|
|
17
|
+
if (key.isWritable)
|
|
18
|
+
return AccountRole.WRITABLE;
|
|
19
|
+
return AccountRole.READONLY;
|
|
20
|
+
}
|
|
21
|
+
function kitInstruction(instruction) {
|
|
22
|
+
return {
|
|
23
|
+
programAddress: kitAddress(address(instruction.programId)),
|
|
24
|
+
accounts: instruction.keys.map((key) => ({
|
|
25
|
+
address: kitAddress(address(key.address)),
|
|
26
|
+
role: accountRole(key),
|
|
27
|
+
})),
|
|
28
|
+
data: instruction.data,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function systemTransferInstruction(from, to, lamports) {
|
|
32
|
+
if (lamports <= 0n)
|
|
33
|
+
throw new Error("amount must be positive");
|
|
34
|
+
const data = new Uint8Array(12);
|
|
35
|
+
data[0] = 2;
|
|
36
|
+
const view = new DataView(data.buffer);
|
|
37
|
+
view.setBigUint64(4, lamports, true);
|
|
38
|
+
return {
|
|
39
|
+
name: "system_transfer",
|
|
40
|
+
programId: SYSTEM_PROGRAM_ID,
|
|
41
|
+
keys: [
|
|
42
|
+
{ address: address(from), isSigner: true, isWritable: true },
|
|
43
|
+
{ address: address(to), isSigner: false, isWritable: true },
|
|
44
|
+
],
|
|
45
|
+
data,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function compileV1TransactionBytes(prepared, options) {
|
|
49
|
+
if (options.version !== 1)
|
|
50
|
+
throw new Error("compileV1TransactionBytes requires version 1");
|
|
51
|
+
if (options.computeUnitLimit <= 0 || options.computeUnitLimit > 1_400_000) {
|
|
52
|
+
throw new Error("V1 compute unit limit must be explicit and at most 1400000");
|
|
53
|
+
}
|
|
54
|
+
if (options.loadedAccountsDataSizeLimit <= 0 || options.loadedAccountsDataSizeLimit > 64 * 1024 * 1024) {
|
|
55
|
+
throw new Error("V1 loaded accounts data size limit must be explicit and at most 64 MiB");
|
|
56
|
+
}
|
|
57
|
+
if (prepared.instructions.length === 0)
|
|
58
|
+
throw new Error("A prepared transaction needs instructions");
|
|
59
|
+
const feePayer = prepared.feePayer ?? prepared.requiredSigners[0];
|
|
60
|
+
if (!feePayer)
|
|
61
|
+
throw new Error("A prepared transaction needs a fee payer");
|
|
62
|
+
const empty = createTransactionMessage({ version: 1 });
|
|
63
|
+
const withFeePayer = setTransactionMessageFeePayer(kitAddress(address(feePayer)), empty);
|
|
64
|
+
const withLifetime = setTransactionMessageLifetimeUsingBlockhash({
|
|
65
|
+
blockhash: options.lifetime.blockhash,
|
|
66
|
+
lastValidBlockHeight: options.lifetime.lastValidBlockHeight,
|
|
67
|
+
}, withFeePayer);
|
|
68
|
+
const withCompute = setTransactionMessageComputeUnitLimit(options.computeUnitLimit, withLifetime);
|
|
69
|
+
const withDataLimit = setTransactionMessageLoadedAccountsDataSizeLimit(options.loadedAccountsDataSizeLimit, withCompute);
|
|
70
|
+
const withInstructions = appendTransactionMessageInstructions(prepared.instructions.map(kitInstruction), withDataLimit);
|
|
71
|
+
const compiled = compileTransaction(withInstructions);
|
|
72
|
+
return new Uint8Array(getTransactionEncoder().encode(compiled));
|
|
73
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
export type Address = string;
|
|
2
|
+
export type TokenProgram = "spl-token" | "token-2022";
|
|
3
|
+
export type AccountMeta = {
|
|
4
|
+
address: Address;
|
|
5
|
+
isSigner: boolean;
|
|
6
|
+
isWritable: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type ChainPayInstruction = {
|
|
9
|
+
name: string;
|
|
10
|
+
programId: Address;
|
|
11
|
+
keys: AccountMeta[];
|
|
12
|
+
data: Uint8Array;
|
|
13
|
+
};
|
|
14
|
+
export type PreparedTransaction = {
|
|
15
|
+
instructions: ChainPayInstruction[];
|
|
16
|
+
requiredSigners: Address[];
|
|
17
|
+
feePayer?: Address;
|
|
18
|
+
};
|
|
19
|
+
export type PaymentSubmission = {
|
|
20
|
+
signature: string;
|
|
21
|
+
slot?: bigint;
|
|
22
|
+
status?: "submitted" | "confirmed";
|
|
23
|
+
};
|
|
24
|
+
export type PaymentSubmissionAdapter = {
|
|
25
|
+
submit(prepared: PreparedTransaction): Promise<PaymentSubmission>;
|
|
26
|
+
confirm?(signature: string): Promise<{
|
|
27
|
+
slot?: bigint;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
export type MandateStatus = "active" | "paused" | "revoked" | "expired";
|
|
31
|
+
export type PaymentStatus = "prepared" | "submitted" | "confirmed" | "failed";
|
|
32
|
+
export type Mandate = {
|
|
33
|
+
address: Address;
|
|
34
|
+
owner: Address;
|
|
35
|
+
approvedAgent: Address;
|
|
36
|
+
sourceTokenAccount: Address;
|
|
37
|
+
allowedMint: Address;
|
|
38
|
+
/** Populated only when decoding a legacy fixed-recipient mandate. */
|
|
39
|
+
legacyAllowedRecipient?: Address;
|
|
40
|
+
maxPerPayment: bigint;
|
|
41
|
+
totalLimit: bigint;
|
|
42
|
+
amountSpent: bigint;
|
|
43
|
+
paymentCount: bigint;
|
|
44
|
+
expiresAtSlot: bigint;
|
|
45
|
+
maxPaymentCount: bigint;
|
|
46
|
+
cooldownSlots: bigint;
|
|
47
|
+
lastPaymentSlot: bigint;
|
|
48
|
+
paused: boolean;
|
|
49
|
+
revoked: boolean;
|
|
50
|
+
status: MandateStatus;
|
|
51
|
+
tokenProgram?: TokenProgram;
|
|
52
|
+
/** Present for nonce-scoped mandates; absent for legacy accounts. */
|
|
53
|
+
mandateNonce?: Address;
|
|
54
|
+
/** Unix timestamp from the oldest confirmed transaction for this PDA. */
|
|
55
|
+
createdAt?: number;
|
|
56
|
+
/** Slot from the oldest confirmed transaction for this PDA. */
|
|
57
|
+
createdAtSlot?: bigint;
|
|
58
|
+
};
|
|
59
|
+
export type SupportedAsset = {
|
|
60
|
+
address: Address;
|
|
61
|
+
authority: Address;
|
|
62
|
+
mint: Address;
|
|
63
|
+
tokenProgram: Address;
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
bump: number;
|
|
66
|
+
};
|
|
67
|
+
export type AssociatedTokenAccountPreparation = {
|
|
68
|
+
asset: SupportedAsset;
|
|
69
|
+
address: Address;
|
|
70
|
+
owner: Address;
|
|
71
|
+
mint: Address;
|
|
72
|
+
tokenProgram: TokenProgram;
|
|
73
|
+
status: "ready" | "missing";
|
|
74
|
+
/** Present only when the canonical account is missing. The SDK never submits it. */
|
|
75
|
+
transaction?: PreparedTransaction;
|
|
76
|
+
};
|
|
77
|
+
export type TokenCapabilityProfile = {
|
|
78
|
+
mint: Address;
|
|
79
|
+
tokenProgram: TokenProgram;
|
|
80
|
+
compatible: boolean;
|
|
81
|
+
mintExtensions: string[];
|
|
82
|
+
sourceAccountExtensions: string[];
|
|
83
|
+
recipientAccountExtensions: string[];
|
|
84
|
+
blockers: string[];
|
|
85
|
+
warnings: string[];
|
|
86
|
+
transferFee?: {
|
|
87
|
+
basisPoints: number;
|
|
88
|
+
maximumFee: bigint;
|
|
89
|
+
};
|
|
90
|
+
transferHookProgram?: Address;
|
|
91
|
+
};
|
|
92
|
+
/** One line of what was bought. Amounts are base units of the request mint. */
|
|
93
|
+
export type PaymentRequestLineItem = {
|
|
94
|
+
label: string;
|
|
95
|
+
/** Unsigned integer string in base units of the request mint. */
|
|
96
|
+
amount?: string;
|
|
97
|
+
/** Unsigned decimal string, for example "1" or "2.5". */
|
|
98
|
+
quantity?: string;
|
|
99
|
+
};
|
|
100
|
+
export type PaymentRequestPayload = {
|
|
101
|
+
version: 1;
|
|
102
|
+
cluster: "devnet" | "mainnet-beta";
|
|
103
|
+
merchant: Address;
|
|
104
|
+
invoice: string;
|
|
105
|
+
mint: Address;
|
|
106
|
+
tokenProgram: TokenProgram;
|
|
107
|
+
recipient: Address;
|
|
108
|
+
amount: string;
|
|
109
|
+
decimals: number;
|
|
110
|
+
nonce: string;
|
|
111
|
+
expiresAtSlot?: string;
|
|
112
|
+
resource?: string;
|
|
113
|
+
/**
|
|
114
|
+
* Seller's plain description of what is being bought. Signed with the rest
|
|
115
|
+
* of the payload, so it is part of the invoice hash when present.
|
|
116
|
+
*/
|
|
117
|
+
description?: string;
|
|
118
|
+
lineItems?: PaymentRequestLineItem[];
|
|
119
|
+
};
|
|
120
|
+
export type SignedPaymentRequest = {
|
|
121
|
+
payload: PaymentRequestPayload;
|
|
122
|
+
signature: string;
|
|
123
|
+
};
|
|
124
|
+
export type PaymentRequestVerification = {
|
|
125
|
+
valid: boolean;
|
|
126
|
+
payload: PaymentRequestPayload;
|
|
127
|
+
invoiceHash: Uint8Array;
|
|
128
|
+
reason?: string;
|
|
129
|
+
};
|
|
130
|
+
export type PaymentRequest = {
|
|
131
|
+
mandate: Address;
|
|
132
|
+
invoiceHash: Uint8Array;
|
|
133
|
+
paymentId: Uint8Array;
|
|
134
|
+
signatureReference: Uint8Array;
|
|
135
|
+
mint: Address;
|
|
136
|
+
recipient: Address;
|
|
137
|
+
amount: bigint;
|
|
138
|
+
tokenProgram?: TokenProgram;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Mandate policy values the program copied into a v2 receipt at payment time.
|
|
142
|
+
* Every value is the mandate's state immediately after this payment.
|
|
143
|
+
*/
|
|
144
|
+
export type ReceiptPolicyLimits = {
|
|
145
|
+
maxPerPayment: bigint;
|
|
146
|
+
totalLimit: bigint;
|
|
147
|
+
amountSpentAfter: bigint;
|
|
148
|
+
paymentCountAfter: bigint;
|
|
149
|
+
/** 0 means no payment-count cap. */
|
|
150
|
+
maxPaymentCount: bigint;
|
|
151
|
+
expiresAtSlot: bigint;
|
|
152
|
+
cooldownSlots: bigint;
|
|
153
|
+
};
|
|
154
|
+
export type ReceiptPolicySnapshot = ReceiptPolicyLimits & {
|
|
155
|
+
/** Layout version byte from the receipt. 1 is the first snapshot layout. */
|
|
156
|
+
version: number;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Where the limits shown beside a receipt came from.
|
|
160
|
+
* - on-chain: written by the program into the receipt account itself.
|
|
161
|
+
* - relay-observed: read from the mandate by the ChainPay relay after the
|
|
162
|
+
* payment finalized. Not stored on Solana, and later payments in the same
|
|
163
|
+
* observation window may already be counted.
|
|
164
|
+
* - not-recorded: neither exists for this receipt.
|
|
165
|
+
*/
|
|
166
|
+
export type ReceiptPolicySource = "on-chain" | "relay-observed" | "not-recorded";
|
|
167
|
+
export type ReceiptPolicy = {
|
|
168
|
+
source: "on-chain";
|
|
169
|
+
limits: ReceiptPolicyLimits;
|
|
170
|
+
} | {
|
|
171
|
+
source: "relay-observed";
|
|
172
|
+
limits: ReceiptPolicyLimits;
|
|
173
|
+
observedAtSlot: bigint;
|
|
174
|
+
/**
|
|
175
|
+
* The mandate had paid again after this receipt when the relay read it,
|
|
176
|
+
* so `amountSpentAfter` and `paymentCountAfter` include later payments.
|
|
177
|
+
*/
|
|
178
|
+
includesLaterPayments: boolean;
|
|
179
|
+
} | {
|
|
180
|
+
source: "not-recorded";
|
|
181
|
+
};
|
|
182
|
+
export type PaymentReceipt = {
|
|
183
|
+
address: Address;
|
|
184
|
+
mandate: Address;
|
|
185
|
+
invoiceHash: Uint8Array;
|
|
186
|
+
paymentId: Uint8Array;
|
|
187
|
+
mint: Address;
|
|
188
|
+
recipient: Address;
|
|
189
|
+
sourceTokenAccount: Address;
|
|
190
|
+
recipientTokenAccount: Address;
|
|
191
|
+
amount: bigint;
|
|
192
|
+
agent: Address;
|
|
193
|
+
executedAtSlot: bigint;
|
|
194
|
+
/** Replay lock from settle. Not seller delivery and not the transaction signature. */
|
|
195
|
+
signatureReference: Uint8Array;
|
|
196
|
+
status: PaymentStatus;
|
|
197
|
+
onChainStatus: number;
|
|
198
|
+
bump: number;
|
|
199
|
+
transactionSignature?: string;
|
|
200
|
+
/**
|
|
201
|
+
* Policy snapshot from a v2 (371-byte) receipt. Null for an original
|
|
202
|
+
* 282-byte receipt or when the program wrote no snapshot.
|
|
203
|
+
*/
|
|
204
|
+
policySnapshot: ReceiptPolicySnapshot | null;
|
|
205
|
+
};
|
|
206
|
+
export type PolicyCheck = {
|
|
207
|
+
name: string;
|
|
208
|
+
ok: boolean;
|
|
209
|
+
message: string;
|
|
210
|
+
};
|
|
211
|
+
/** Verified source token account fields supplied by preparePayment. */
|
|
212
|
+
export type PaymentPreflightContext = {
|
|
213
|
+
sourceBalance: bigint;
|
|
214
|
+
sourceOwner: Address;
|
|
215
|
+
delegate: Address | null;
|
|
216
|
+
delegatedAmount: bigint;
|
|
217
|
+
};
|
|
218
|
+
export type PaymentPreflight = {
|
|
219
|
+
valid: boolean;
|
|
220
|
+
currentSlot: bigint;
|
|
221
|
+
checks: PolicyCheck[];
|
|
222
|
+
};
|
|
223
|
+
export type BatchPreflightEntry = {
|
|
224
|
+
request: PaymentRequest;
|
|
225
|
+
mandate: Mandate;
|
|
226
|
+
agent?: Address;
|
|
227
|
+
receiptAlreadyExists?: boolean;
|
|
228
|
+
sourceContext?: PaymentPreflightContext;
|
|
229
|
+
};
|
|
230
|
+
export type PaymentBatchPreflight = {
|
|
231
|
+
valid: boolean;
|
|
232
|
+
currentSlot: bigint;
|
|
233
|
+
entries: Array<{
|
|
234
|
+
request: PaymentRequest;
|
|
235
|
+
mandate: Address;
|
|
236
|
+
preflight: PaymentPreflight;
|
|
237
|
+
}>;
|
|
238
|
+
batchChecks: PolicyCheck[];
|
|
239
|
+
};
|
|
240
|
+
export type PreparedPayment = {
|
|
241
|
+
request: PaymentRequest;
|
|
242
|
+
mandate: Mandate;
|
|
243
|
+
receiptAddress: Address;
|
|
244
|
+
instruction: ChainPayInstruction;
|
|
245
|
+
transaction: PreparedTransaction;
|
|
246
|
+
preflight: PaymentPreflight;
|
|
247
|
+
capabilityProfile?: TokenCapabilityProfile;
|
|
248
|
+
};
|
|
249
|
+
export type PaymentExecutionResult = {
|
|
250
|
+
status: "submitted" | "confirmed" | "failed";
|
|
251
|
+
receiptAddress: Address;
|
|
252
|
+
signature?: string;
|
|
253
|
+
slot?: bigint;
|
|
254
|
+
error?: string;
|
|
255
|
+
/** Set when the receipt already existed: nothing new was paid. */
|
|
256
|
+
code?: "DuplicateInvoice";
|
|
257
|
+
};
|
|
258
|
+
export type PreparedMandate = {
|
|
259
|
+
mandateAddress: Address;
|
|
260
|
+
configAddress: Address;
|
|
261
|
+
transaction: PreparedTransaction;
|
|
262
|
+
};
|
|
263
|
+
export type ChainPayClientOptions = {
|
|
264
|
+
rpcUrl?: string;
|
|
265
|
+
programId?: Address;
|
|
266
|
+
commitment?: "processed" | "confirmed" | "finalized";
|
|
267
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TokenProgram } from "./types.js";
|
|
2
|
+
import type { PreparePaymentInput } from "./payment.js";
|
|
3
|
+
import { type CustomChallengeOption, type StandardV2Option, type DetectedChallenge } from "./x402-challenge.js";
|
|
4
|
+
export type X402PrepareFields = {
|
|
5
|
+
mint: string;
|
|
6
|
+
recipient: string;
|
|
7
|
+
amount: string;
|
|
8
|
+
resource: string;
|
|
9
|
+
tokenProgram: TokenProgram;
|
|
10
|
+
invoiceHash: string;
|
|
11
|
+
paymentId: string;
|
|
12
|
+
signatureReference: string;
|
|
13
|
+
nonce: string;
|
|
14
|
+
merchantOwner?: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function standardV2RecipientTokenAccount(option: StandardV2Option, tokenProgram: TokenProgram): string;
|
|
17
|
+
export declare function customChallengeToPrepareFields(option: CustomChallengeOption, tokenProgram: TokenProgram): Promise<X402PrepareFields>;
|
|
18
|
+
export declare function standardV2ChallengeToPrepareFields(option: StandardV2Option, tokenProgram: TokenProgram): Promise<X402PrepareFields>;
|
|
19
|
+
export declare function toPreparePaymentInput(mandate: string, fields: X402PrepareFields): Promise<PreparePaymentInput>;
|
|
20
|
+
export declare function detectedChallengeToPrepareFields(detected: DetectedChallenge, tokenProgram: TokenProgram): Promise<X402PrepareFields>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { hexToBytes } from "./receipt.js";
|
|
2
|
+
import { deriveAssociatedTokenAddress } from "./token.js";
|
|
3
|
+
import { deriveX402PaymentReferences } from "./x402.js";
|
|
4
|
+
export function standardV2RecipientTokenAccount(option, tokenProgram) {
|
|
5
|
+
return deriveAssociatedTokenAddress(option.merchantOwner, option.asset, tokenProgram);
|
|
6
|
+
}
|
|
7
|
+
export async function customChallengeToPrepareFields(option, tokenProgram) {
|
|
8
|
+
const references = await deriveX402PaymentReferences({
|
|
9
|
+
mint: option.mint,
|
|
10
|
+
recipient: option.recipient,
|
|
11
|
+
amount: option.amount,
|
|
12
|
+
resource: option.resource,
|
|
13
|
+
tokenProgram,
|
|
14
|
+
...(option.nonce ? { nonce: option.nonce } : {}),
|
|
15
|
+
...(option.expiresAtSlot ? { expiresAtSlot: option.expiresAtSlot } : {}),
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
mint: option.mint,
|
|
19
|
+
recipient: option.recipient,
|
|
20
|
+
amount: option.amount,
|
|
21
|
+
resource: option.resource,
|
|
22
|
+
tokenProgram,
|
|
23
|
+
...references,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export async function standardV2ChallengeToPrepareFields(option, tokenProgram) {
|
|
27
|
+
const recipient = standardV2RecipientTokenAccount(option, tokenProgram);
|
|
28
|
+
const references = await deriveX402PaymentReferences({
|
|
29
|
+
mint: option.asset,
|
|
30
|
+
recipient,
|
|
31
|
+
amount: option.amount,
|
|
32
|
+
resource: option.resource,
|
|
33
|
+
tokenProgram,
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
mint: option.asset,
|
|
37
|
+
recipient,
|
|
38
|
+
amount: option.amount,
|
|
39
|
+
resource: option.resource,
|
|
40
|
+
tokenProgram,
|
|
41
|
+
merchantOwner: option.merchantOwner,
|
|
42
|
+
...references,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export async function toPreparePaymentInput(mandate, fields) {
|
|
46
|
+
return {
|
|
47
|
+
mandate,
|
|
48
|
+
invoiceHash: hexToBytes(fields.invoiceHash, "invoiceHash"),
|
|
49
|
+
paymentId: hexToBytes(fields.paymentId, "paymentId"),
|
|
50
|
+
signatureReference: hexToBytes(fields.signatureReference, "signatureReference"),
|
|
51
|
+
mint: fields.mint,
|
|
52
|
+
recipient: fields.recipient,
|
|
53
|
+
amount: BigInt(fields.amount),
|
|
54
|
+
tokenProgram: fields.tokenProgram,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export async function detectedChallengeToPrepareFields(detected, tokenProgram) {
|
|
58
|
+
if (detected.kind === "custom") {
|
|
59
|
+
return customChallengeToPrepareFields(detected.option, tokenProgram);
|
|
60
|
+
}
|
|
61
|
+
return standardV2ChallengeToPrepareFields(detected.option, tokenProgram);
|
|
62
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export declare const CUSTOM_X402_VERSION: "x402/1.0";
|
|
2
|
+
export declare const SOLANA_DEVNET_CAIP2 = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
|
|
3
|
+
export declare const CUSTOM_NETWORK: "solana-devnet";
|
|
4
|
+
export declare const CUSTOM_PROTOCOL: "chainpay-custom-x402/1.0";
|
|
5
|
+
export declare const STANDARD_V2_PROTOCOL: "x402-v2";
|
|
6
|
+
export declare const CUSTOM_PROTOCOL_LABEL = "ChainPay custom receipt-proof flow (x402/1.0). Proof is a settled signature and receipt PDA, not a standard x402 v2 sponsor countersign.";
|
|
7
|
+
export declare const STANDARD_V2_PROTOCOL_LABEL = "Standard x402 v2 exact SVM. Proof is a partially signed transaction for sponsor countersign. ChainPay does not operate a facilitator.";
|
|
8
|
+
export type X402ProtocolCode = "malformed" | "unsupported_network" | "unsupported_scheme" | "resource_mismatch" | "conflicting_protocols";
|
|
9
|
+
export declare class X402ProtocolError extends Error {
|
|
10
|
+
readonly code: X402ProtocolCode;
|
|
11
|
+
constructor(code: X402ProtocolCode, message: string);
|
|
12
|
+
}
|
|
13
|
+
export type CustomChallengeOption = {
|
|
14
|
+
protocol: typeof CUSTOM_PROTOCOL;
|
|
15
|
+
protocolLabel: string;
|
|
16
|
+
proofKind: "settled-receipt-pda";
|
|
17
|
+
network: typeof CUSTOM_NETWORK;
|
|
18
|
+
scheme: "exact";
|
|
19
|
+
mint: string;
|
|
20
|
+
recipient: string;
|
|
21
|
+
amount: string;
|
|
22
|
+
resource: string;
|
|
23
|
+
tokenProgram?: "spl-token" | "token-2022";
|
|
24
|
+
nonce?: string;
|
|
25
|
+
expiresAtSlot?: string;
|
|
26
|
+
};
|
|
27
|
+
export type StandardV2Option = {
|
|
28
|
+
protocol: typeof STANDARD_V2_PROTOCOL;
|
|
29
|
+
protocolLabel: string;
|
|
30
|
+
proofKind: "partially-signed-sponsored-transaction";
|
|
31
|
+
network: typeof SOLANA_DEVNET_CAIP2;
|
|
32
|
+
scheme: "exact";
|
|
33
|
+
amount: string;
|
|
34
|
+
asset: string;
|
|
35
|
+
merchantOwner: string;
|
|
36
|
+
resource: string;
|
|
37
|
+
maxTimeoutSeconds?: number;
|
|
38
|
+
feePayer?: string;
|
|
39
|
+
};
|
|
40
|
+
export type DetectedChallenge = {
|
|
41
|
+
kind: "custom";
|
|
42
|
+
option: CustomChallengeOption;
|
|
43
|
+
envelope: Record<string, unknown>;
|
|
44
|
+
} | {
|
|
45
|
+
kind: "standard-v2";
|
|
46
|
+
option: StandardV2Option;
|
|
47
|
+
envelope: Record<string, unknown>;
|
|
48
|
+
};
|
|
49
|
+
export declare function canonicalU64String(value: unknown, name: string, allowZero?: boolean): string;
|
|
50
|
+
export declare function decodePaymentRequiredWire(value: string): unknown;
|
|
51
|
+
export declare function paymentRequiredDocumentsFromResponse(headers: {
|
|
52
|
+
get(name: string): string | null;
|
|
53
|
+
}, body?: {
|
|
54
|
+
parsed?: unknown;
|
|
55
|
+
}): unknown[];
|
|
56
|
+
export declare function parsePaymentRequiredDocument(value: unknown, expectedResource?: string): DetectedChallenge;
|
|
57
|
+
export declare function detectAndParsePaymentRequired(documents: unknown[], expectedResource?: string): DetectedChallenge;
|
|
58
|
+
/** Alias for agent/SDK callers: parse HTTP 402 headers and body into a detected challenge. */
|
|
59
|
+
export declare function detectPaymentChallenge(headers: {
|
|
60
|
+
get(name: string): string | null;
|
|
61
|
+
}, body: {
|
|
62
|
+
parsed?: unknown;
|
|
63
|
+
}, expectedResource?: string): DetectedChallenge;
|
|
64
|
+
export declare function parsePaymentRequiredFromResponse(headers: {
|
|
65
|
+
get(name: string): string | null;
|
|
66
|
+
}, body: {
|
|
67
|
+
parsed?: unknown;
|
|
68
|
+
}, expectedResource?: string): DetectedChallenge;
|
|
69
|
+
export declare function customReceiptProofDocument(signature: string, receiptPDA: string): {
|
|
70
|
+
version: "x402/1.0";
|
|
71
|
+
scheme: "exact";
|
|
72
|
+
network: "solana-devnet";
|
|
73
|
+
payload: {
|
|
74
|
+
signature: string;
|
|
75
|
+
receiptPDA: string;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
export type UnsupportedSponsorResult = {
|
|
79
|
+
action: "x402_unsupported_sponsor";
|
|
80
|
+
mode: "unsupported-sponsor";
|
|
81
|
+
protocol: typeof STANDARD_V2_PROTOCOL;
|
|
82
|
+
protocolLabel: string;
|
|
83
|
+
proofKind: "partially-signed-sponsored-transaction";
|
|
84
|
+
network: typeof SOLANA_DEVNET_CAIP2;
|
|
85
|
+
scheme: "exact";
|
|
86
|
+
amount: string;
|
|
87
|
+
asset: string;
|
|
88
|
+
merchantOwner: string;
|
|
89
|
+
resource: string;
|
|
90
|
+
sponsorAvailable: false;
|
|
91
|
+
wouldSettle: false;
|
|
92
|
+
reason: "facilitator_required";
|
|
93
|
+
message: string;
|
|
94
|
+
derivedRecipientTokenAccount?: string;
|
|
95
|
+
mandateQuote?: {
|
|
96
|
+
status: "ready" | "needs_details" | "blocked";
|
|
97
|
+
preflightValid?: boolean;
|
|
98
|
+
checks?: Array<{
|
|
99
|
+
key: string;
|
|
100
|
+
label: string;
|
|
101
|
+
status: string;
|
|
102
|
+
detail: string;
|
|
103
|
+
}>;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
export declare function unsupportedSponsorResult(option: StandardV2Option, extras?: Pick<UnsupportedSponsorResult, "derivedRecipientTokenAccount" | "mandateQuote">): UnsupportedSponsorResult;
|
|
107
|
+
export type MppUnsupportedResult = {
|
|
108
|
+
action: "mpp_unsupported";
|
|
109
|
+
mode: "unsupported-mpp";
|
|
110
|
+
message: string;
|
|
111
|
+
wwwAuthenticate?: string;
|
|
112
|
+
intent?: string;
|
|
113
|
+
method?: string;
|
|
114
|
+
challengeId?: string;
|
|
115
|
+
};
|
|
116
|
+
export declare function parseMppWwwAuthenticate(value: string | null): MppUnsupportedResult | null;
|