@canister-software/x402-icp 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Canister Software Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # @canister-software/x402-icp
2
+
3
+ [x402](https://x402.org) payment scheme for the [Internet Computer](https://internetcomputer.org) — brings native ICP token payments (ICP, ckUSDC, ckUSDT) to the HTTP 402 standard using ICRC-2 approve/transferFrom.
4
+
5
+ ## Overview
6
+
7
+ x402 is an open protocol for machine-native HTTP payments. This library adds ICP support to the x402 ecosystem, letting any HTTP resource accept ICP tokens with a single line of middleware — and any client pay automatically using an internet identity.
8
+
9
+ **Supported assets**
10
+
11
+ | Symbol | Asset |
12
+ |--------|---------|
13
+ | ICP | `icp:1:ryjl3-tyaaa-aaaaa-aaaba-cai` |
14
+ | ckUSDC | `icp:1:xevnm-gaaaa-aaaar-qafnq-cai` |
15
+ | ckUSDT | `icp:1:cngnf-vqaaa-aaaar-qag4q-cai` |
16
+
17
+ `icp:1` signifisy the network as
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @canister-software/x402-icp
23
+ ```
24
+
25
+ Requires `@x402/core >= 2.4.0` as a peer dependency.
26
+
27
+ ## Usage
28
+
29
+ ### Client — paying for a resource
30
+
31
+ ```typescript
32
+ import { x402Client } from '@x402/core/client'
33
+ import { wrapFetchWithPayment } from '@x402/fetch'
34
+ import { registerExactIcpScheme, createPemSigner } from '@canister-software/x402-icp/client'
35
+ import fs from 'fs'
36
+
37
+ const signer = await createPemSigner({
38
+ pem: fs.readFileSync('identity.pem', 'utf8'),
39
+ })
40
+
41
+ const client = new x402Client()
42
+ registerExactIcpScheme(client, { signer })
43
+
44
+ const fetch402 = wrapFetchWithPayment(fetch, client)
45
+ const res = await fetch402('https://api.example.com/premium')
46
+ ```
47
+
48
+ ### Server — protecting a resource
49
+
50
+ ```typescript
51
+ import { createIcpPaymentRequirements } from '@canister-software/x402-icp/server'
52
+
53
+ const requirements = createIcpPaymentRequirements({
54
+ network: 'icp:1:xevnm-gaaaa-aaaar-qafnq-cai', // ckUSDC
55
+ amount: '1000000', // 1 ckUSDC (6 decimals)
56
+ payTo: 'your-principal-id',
57
+ })
58
+ ```
59
+
60
+ ## Facilitator
61
+
62
+ A public facilitator for ICP is operated by Canister Software at:
63
+
64
+ ```
65
+ https://facilitator.consensus.canister.software
66
+ ```
67
+
68
+ Full documentation: [docs.canister.software](https://docs.canister.software)
69
+
70
+ ## License
71
+
72
+ MIT
@@ -0,0 +1,3 @@
1
+ export { ExactIcpClient } from './scheme.js';
2
+ export { registerExactIcpScheme, createPemSigner, createIdentitySigner, pemToSigner, } from './signer.js';
3
+ export type { IcpSigner, Icrc2ApproveParams, PemSignerOptions, IdentitySignerOptions } from './signer.js';
@@ -0,0 +1,3 @@
1
+ export { ExactIcpClient } from './scheme.js';
2
+ export { registerExactIcpScheme, createPemSigner, createIdentitySigner, pemToSigner, } from './signer.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EACL,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,WAAW,GACZ,MAAM,aAAa,CAAA"}
@@ -0,0 +1,40 @@
1
+ import type { IcpPayload } from '../types/index.js';
2
+ import type { IcpSigner } from './signer.js';
3
+ /**
4
+ * x402 PaymentRequirements as received from the 402 response.
5
+ * We define locally to avoid hard coupling to @x402/core version.
6
+ */
7
+ interface PaymentRequirements {
8
+ scheme: string;
9
+ network: string;
10
+ amount: string;
11
+ asset: string;
12
+ payTo: string;
13
+ maxTimeoutSeconds: number;
14
+ extra?: Record<string, unknown>;
15
+ }
16
+ /**
17
+ * ExactIcpClient implements SchemeNetworkClient for ICP.
18
+ *
19
+ * When @x402/fetch receives a 402 with network "icp:*", it calls
20
+ * createPaymentPayload() which:
21
+ * 1. Calls icrc2_approve on the ledger (authorize facilitator to spend)
22
+ * 2. Signs the authorization object
23
+ * 3. Returns the payload for the x-payment header
24
+ */
25
+ export declare class ExactIcpClient {
26
+ readonly scheme = "exact";
27
+ private signer;
28
+ constructor(signer: IcpSigner);
29
+ /**
30
+ * Create a payment payload from 402 payment requirements.
31
+ * Called by x402Client when it matches an icp:* network.
32
+ *
33
+ * Signature matches SchemeNetworkClient: (x402Version, requirements) => payload
34
+ */
35
+ createPaymentPayload(x402Version: number, requirements: PaymentRequirements): Promise<{
36
+ x402Version: number;
37
+ payload: IcpPayload;
38
+ }>;
39
+ }
40
+ export {};
@@ -0,0 +1,69 @@
1
+ import { ASSETS } from '../types/index.js';
2
+ /**
3
+ * ExactIcpClient implements SchemeNetworkClient for ICP.
4
+ *
5
+ * When @x402/fetch receives a 402 with network "icp:*", it calls
6
+ * createPaymentPayload() which:
7
+ * 1. Calls icrc2_approve on the ledger (authorize facilitator to spend)
8
+ * 2. Signs the authorization object
9
+ * 3. Returns the payload for the x-payment header
10
+ */
11
+ export class ExactIcpClient {
12
+ scheme = 'exact';
13
+ signer;
14
+ constructor(signer) {
15
+ this.signer = signer;
16
+ }
17
+ /**
18
+ * Create a payment payload from 402 payment requirements.
19
+ * Called by x402Client when it matches an icp:* network.
20
+ *
21
+ * Signature matches SchemeNetworkClient: (x402Version, requirements) => payload
22
+ */
23
+ async createPaymentPayload(x402Version, requirements) {
24
+ // Extract ledger canister ID from network: icp:1:<canisterId>
25
+ const network = requirements.network;
26
+ if (!network) {
27
+ throw new Error(`[ExactIcpClient] requirements.network is undefined.`);
28
+ }
29
+ const ledgerId = network.split(':').pop();
30
+ const asset = ASSETS[ledgerId];
31
+ if (!asset) {
32
+ throw new Error(`Unknown ICP asset for network: ${network}`);
33
+ }
34
+ // Get facilitator principal from requirements.extra
35
+ const facilitatorPrincipal = requirements.extra?.facilitatorPrincipal;
36
+ if (!facilitatorPrincipal) {
37
+ throw new Error('Missing facilitatorPrincipal in payment requirements extra. ' +
38
+ 'The resource server must include this from the facilitator.');
39
+ }
40
+ const amount = BigInt(requirements.amount);
41
+ const nonce = Date.now();
42
+ const expiresAt = Date.now() + (requirements.maxTimeoutSeconds * 1000);
43
+ // Step 1: Approve the facilitator to spend our tokens via ICRC-2
44
+ await this.signer.icrc2Approve({
45
+ ledgerCanisterId: ledgerId,
46
+ spender: facilitatorPrincipal,
47
+ amount: amount + BigInt(asset.transferFee),
48
+ expiresAt: BigInt(expiresAt) * 1000000n,
49
+ });
50
+ // Step 2: Build the authorization object (all JSON-safe types)
51
+ const authorization = {
52
+ to: String(requirements.payTo),
53
+ value: String(requirements.amount),
54
+ expiresAt: Number(expiresAt),
55
+ nonce: Number(nonce),
56
+ };
57
+ // Step 3: Sign the authorization
58
+ const signature = await this.signer.signAuthorization(authorization);
59
+ // Return only JSON-safe payload — no BigInts
60
+ return {
61
+ x402Version,
62
+ payload: {
63
+ signature,
64
+ authorization,
65
+ },
66
+ };
67
+ }
68
+ }
69
+ //# sourceMappingURL=scheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheme.js","sourceRoot":"","sources":["../../src/client/scheme.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAiB1C;;;;;;;;GAQG;AACH,MAAM,OAAO,cAAc;IAChB,MAAM,GAAG,OAAO,CAAA;IACjB,MAAM,CAAW;IAEzB,YAAY,MAAiB;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,oBAAoB,CACxB,WAAmB,EACnB,YAAiC;QAEjC,8DAA8D;QAC9D,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAA;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;QACxE,CAAC;QACD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAA;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;QAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,kCAAkC,OAAO,EAAE,CAAC,CAAA;QAC9D,CAAC;QAED,oDAAoD;QACpD,MAAM,oBAAoB,GAAG,YAAY,CAAC,KAAK,EAAE,oBAA8B,CAAA;QAC/E,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,8DAA8D;gBAC9D,6DAA6D,CAC9D,CAAA;QACH,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,YAAY,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAA;QAEtE,iEAAiE;QACjE,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC;YAC7B,gBAAgB,EAAE,QAAQ;YAC1B,OAAO,EAAE,oBAAoB;YAC7B,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;YAC1C,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG,QAAU;SAC1C,CAAC,CAAA;QAEF,+DAA+D;QAC/D,MAAM,aAAa,GAA4B;YAC7C,EAAE,EAAE,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC;YAC9B,KAAK,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;YAClC,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;YAC5B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;SACrB,CAAA;QAED,iCAAiC;QACjC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAA;QAEpE,6CAA6C;QAC7C,OAAO;YACL,WAAW;YACX,OAAO,EAAE;gBACP,SAAS;gBACT,aAAa;aACd;SACF,CAAA;IACH,CAAC;CACF"}
@@ -0,0 +1,89 @@
1
+ import type { IcpPayloadAuthorization } from '../types/index.js';
2
+ export interface Icrc2ApproveParams {
3
+ ledgerCanisterId: string;
4
+ spender: string;
5
+ amount: bigint;
6
+ expiresAt?: bigint;
7
+ }
8
+ export interface IcpSigner {
9
+ /** The principal ID of this signer */
10
+ readonly principal: string;
11
+ /** Call icrc2_approve on a ledger canister */
12
+ icrc2Approve(params: Icrc2ApproveParams): Promise<bigint>;
13
+ /** Sign an authorization object, return base64url-encoded CBOR envelope */
14
+ signAuthorization(auth: IcpPayloadAuthorization): Promise<string>;
15
+ }
16
+ export interface PemSignerOptions {
17
+ /** PEM file contents (string) — as exported by dfx */
18
+ pem: string;
19
+ /** IC host URL (default: https://icp-api.io) */
20
+ host?: string;
21
+ }
22
+ /**
23
+ * Create an ICP signer from a PEM file (as used by dfx identity export).
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * import { createPemSigner } from '@canister-software/x402-icp/client'
28
+ * import fs from 'fs'
29
+ *
30
+ * const pem = fs.readFileSync('identity.pem', 'utf8')
31
+ * const signer = await createPemSigner({ pem })
32
+ * ```
33
+ */
34
+ export declare function createPemSigner(opts: PemSignerOptions): Promise<IcpSigner>;
35
+ export interface IdentitySignerOptions {
36
+ /** Any @dfinity/identity Identity implementation */
37
+ identity: any;
38
+ /** IC host URL (default: https://icp-api.io) */
39
+ host?: string;
40
+ }
41
+ /**
42
+ * Create an ICP signer from an existing @dfinity/identity.
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * import { createIdentitySigner } from '@canister-software/x402-icp/client'
47
+ * import { Ed25519KeyIdentity } from '@dfinity/identity'
48
+ *
49
+ * const identity = Ed25519KeyIdentity.generate()
50
+ * const signer = await createIdentitySigner({ identity })
51
+ * ```
52
+ */
53
+ export declare function createIdentitySigner(opts: IdentitySignerOptions): Promise<IcpSigner>;
54
+ interface X402ClientLike {
55
+ register(network: string, scheme: any): X402ClientLike;
56
+ }
57
+ /**
58
+ * Register the ICP exact payment scheme with an x402Client.
59
+ *
60
+ * Mirrors registerExactEvmScheme(client, { signer }) from @x402/evm.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { x402Client } from '@x402/core/client'
65
+ * import { wrapFetchWithPayment } from '@x402/fetch'
66
+ * import { registerExactIcpScheme, createPemSigner } from '@canister-software/x402-icp/client'
67
+ * import fs from 'fs'
68
+ *
69
+ * const signer = await createPemSigner({ pem: fs.readFileSync('identity.pem', 'utf8') })
70
+ * const client = new x402Client()
71
+ * registerExactIcpScheme(client, { signer })
72
+ * const fetchWithPayment = wrapFetchWithPayment(fetch, client)
73
+ * ```
74
+ */
75
+ export declare function registerExactIcpScheme(client: X402ClientLike, opts: {
76
+ signer: IcpSigner;
77
+ }): X402ClientLike;
78
+ /**
79
+ * Convenience function to create a signer from a PEM file path.
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * import { pemToSigner } from '@canister-software/x402-icp/client'
84
+ *
85
+ * const signer = await pemToSigner('~/.config/dfx/identity/default/identity.pem')
86
+ * ```
87
+ */
88
+ export declare function pemToSigner(pemPath: string, host?: string): Promise<IcpSigner>;
89
+ export {};
@@ -0,0 +1,186 @@
1
+ import { ExactIcpClient } from './scheme.js';
2
+ /**
3
+ * Create an ICP signer from a PEM file (as used by dfx identity export).
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { createPemSigner } from '@canister-software/x402-icp/client'
8
+ * import fs from 'fs'
9
+ *
10
+ * const pem = fs.readFileSync('identity.pem', 'utf8')
11
+ * const signer = await createPemSigner({ pem })
12
+ * ```
13
+ */
14
+ export async function createPemSigner(opts) {
15
+ const { Ed25519KeyIdentity } = await import('@dfinity/identity');
16
+ // Parse PEM to DER bytes
17
+ const pemBody = opts.pem
18
+ .replace(/-----BEGIN[^-]*-----/, '')
19
+ .replace(/-----END[^-]*-----/, '')
20
+ .replace(/\s/g, '');
21
+ const derBuf = Buffer.from(pemBody, 'base64');
22
+ const derBytes = new Uint8Array(derBuf.buffer, derBuf.byteOffset, derBuf.byteLength);
23
+ let identity;
24
+ // Try Ed25519 first — extract last 32 bytes as seed
25
+ try {
26
+ const seed = derBytes.slice(derBytes.length - 32);
27
+ identity = Ed25519KeyIdentity.generate(seed);
28
+ }
29
+ catch {
30
+ // Fall back to Secp256k1
31
+ try {
32
+ const { Secp256k1KeyIdentity } = await import('@dfinity/identity-secp256k1');
33
+ identity = Secp256k1KeyIdentity.fromSecretKey(derBytes.buffer);
34
+ }
35
+ catch {
36
+ throw new Error('Could not parse PEM as Ed25519 or Secp256k1. ' +
37
+ 'For Secp256k1, install: npm install @dfinity/identity-secp256k1');
38
+ }
39
+ }
40
+ return createIdentitySigner({ identity, host: opts.host });
41
+ }
42
+ /**
43
+ * Create an ICP signer from an existing @dfinity/identity.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { createIdentitySigner } from '@canister-software/x402-icp/client'
48
+ * import { Ed25519KeyIdentity } from '@dfinity/identity'
49
+ *
50
+ * const identity = Ed25519KeyIdentity.generate()
51
+ * const signer = await createIdentitySigner({ identity })
52
+ * ```
53
+ */
54
+ export async function createIdentitySigner(opts) {
55
+ const { HttpAgent } = await import('@dfinity/agent');
56
+ const { Principal } = await import('@dfinity/principal');
57
+ const { IDL } = await import('@dfinity/candid');
58
+ const host = opts.host ?? 'https://icp-api.io';
59
+ const identity = opts.identity;
60
+ const agent = await HttpAgent.create({ identity, host });
61
+ // Fetch root key for local dev
62
+ if (host.includes('127.0.0.1') || host.includes('localhost')) {
63
+ await agent.fetchRootKey();
64
+ }
65
+ const principal = identity.getPrincipal().toText();
66
+ return {
67
+ principal,
68
+ async icrc2Approve(params) {
69
+ const { Actor } = await import('@dfinity/agent');
70
+ const icrc2Interface = IDL.Service({
71
+ icrc2_approve: IDL.Func([
72
+ IDL.Record({
73
+ fee: IDL.Opt(IDL.Nat),
74
+ memo: IDL.Opt(IDL.Vec(IDL.Nat8)),
75
+ from_subaccount: IDL.Opt(IDL.Vec(IDL.Nat8)),
76
+ created_at_time: IDL.Opt(IDL.Nat64),
77
+ amount: IDL.Nat,
78
+ expected_allowance: IDL.Opt(IDL.Nat),
79
+ expires_at: IDL.Opt(IDL.Nat64),
80
+ spender: IDL.Record({
81
+ owner: IDL.Principal,
82
+ subaccount: IDL.Opt(IDL.Vec(IDL.Nat8)),
83
+ }),
84
+ }),
85
+ ], [
86
+ IDL.Variant({
87
+ Ok: IDL.Nat,
88
+ Err: IDL.Variant({
89
+ GenericError: IDL.Record({ message: IDL.Text, error_code: IDL.Nat }),
90
+ TemporarilyUnavailable: IDL.Null,
91
+ Duplicate: IDL.Record({ duplicate_of: IDL.Nat }),
92
+ BadFee: IDL.Record({ expected_fee: IDL.Nat }),
93
+ AllowanceChanged: IDL.Record({ current_allowance: IDL.Nat }),
94
+ CreatedInFuture: IDL.Record({ ledger_time: IDL.Nat64 }),
95
+ TooOld: IDL.Null,
96
+ Expired: IDL.Record({ ledger_time: IDL.Nat64 }),
97
+ InsufficientFunds: IDL.Record({ balance: IDL.Nat }),
98
+ }),
99
+ }),
100
+ ], []),
101
+ });
102
+ const ledger = Actor.createActor(() => icrc2Interface, {
103
+ agent,
104
+ canisterId: params.ledgerCanisterId,
105
+ });
106
+ const result = await ledger.icrc2_approve({
107
+ fee: [],
108
+ memo: [],
109
+ from_subaccount: [],
110
+ created_at_time: [],
111
+ amount: params.amount,
112
+ expected_allowance: [],
113
+ expires_at: params.expiresAt !== undefined ? [params.expiresAt] : [],
114
+ spender: {
115
+ owner: Principal.fromText(params.spender),
116
+ subaccount: [],
117
+ },
118
+ });
119
+ if ('Err' in result) {
120
+ const errKey = Object.keys(result.Err)[0];
121
+ const errVal = result.Err[errKey];
122
+ throw new Error(`ICRC-2 approve failed: ${errKey} - ${JSON.stringify(errVal, (_k, v) => typeof v === 'bigint' ? v.toString() : v)}`);
123
+ }
124
+ return BigInt(result.Ok.toString());
125
+ },
126
+ async signAuthorization(auth) {
127
+ const { encode, rfc8949EncodeOptions } = await import('cborg');
128
+ const { sha3_256 } = await import('@noble/hashes/sha3.js');
129
+ // CBOR-encode with deterministic options, then sha3_256 — must match facilitator's computeDigest
130
+ const cborBytes = encode(auth, rfc8949EncodeOptions);
131
+ const digest = sha3_256(cborBytes);
132
+ const signature = await identity.sign(digest);
133
+ // Build the ic-auth envelope: { p: publicKey, s: signature }
134
+ const publicKeyDer = identity.getPublicKey().toDer();
135
+ const envelope = encode({
136
+ p: new Uint8Array(publicKeyDer),
137
+ s: new Uint8Array(signature),
138
+ });
139
+ // Return as base64url
140
+ return base64urlEncode(new Uint8Array(envelope));
141
+ },
142
+ };
143
+ }
144
+ /**
145
+ * Register the ICP exact payment scheme with an x402Client.
146
+ *
147
+ * Mirrors registerExactEvmScheme(client, { signer }) from @x402/evm.
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * import { x402Client } from '@x402/core/client'
152
+ * import { wrapFetchWithPayment } from '@x402/fetch'
153
+ * import { registerExactIcpScheme, createPemSigner } from '@canister-software/x402-icp/client'
154
+ * import fs from 'fs'
155
+ *
156
+ * const signer = await createPemSigner({ pem: fs.readFileSync('identity.pem', 'utf8') })
157
+ * const client = new x402Client()
158
+ * registerExactIcpScheme(client, { signer })
159
+ * const fetchWithPayment = wrapFetchWithPayment(fetch, client)
160
+ * ```
161
+ */
162
+ export function registerExactIcpScheme(client, opts) {
163
+ return client.register('icp:*', new ExactIcpClient(opts.signer));
164
+ }
165
+ // ─── Convenience: pemToSigner ────────────────────────────────────────────────
166
+ /**
167
+ * Convenience function to create a signer from a PEM file path.
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * import { pemToSigner } from '@canister-software/x402-icp/client'
172
+ *
173
+ * const signer = await pemToSigner('~/.config/dfx/identity/default/identity.pem')
174
+ * ```
175
+ */
176
+ export async function pemToSigner(pemPath, host) {
177
+ const fs = await import('fs');
178
+ const pem = fs.readFileSync(pemPath, 'utf8');
179
+ return createPemSigner({ pem, host });
180
+ }
181
+ // ─── Utilities ───────────────────────────────────────────────────────────────
182
+ function base64urlEncode(bytes) {
183
+ const b64 = Buffer.from(bytes).toString('base64');
184
+ return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
185
+ }
186
+ //# sourceMappingURL=signer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.js","sourceRoot":"","sources":["../../src/client/signer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { ExactIcpScheme } from './scheme.js';
2
+ export { createIcpPaymentRequirements, type CreateIcpPaymentRequirementsOptions } from './requirements.js';
@@ -0,0 +1,3 @@
1
+ export { ExactIcpScheme } from './scheme.js';
2
+ export { createIcpPaymentRequirements } from './requirements.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,4BAA4B,EAA4C,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,22 @@
1
+ import type { PaymentRequirements } from '../types/index.js';
2
+ export interface CreateIcpPaymentRequirementsOptions {
3
+ /** Amount in smallest unit (e8s for ICP, e6s for ckUSDC/ckUSDT) */
4
+ amount: string;
5
+ /** Network identifier, e.g. 'icp:1:ryjl3-tyaaa-aaaaa-aaaba-cai' */
6
+ network: string;
7
+ /** Recipient principal ID */
8
+ payTo: string;
9
+ /** Max timeout in seconds (default: 300) */
10
+ maxTimeoutSeconds?: number;
11
+ /**
12
+ * Extra fields to include in payment requirements.
13
+ *
14
+ * When using a facilitator, `facilitatorPrincipal` is injected automatically
15
+ * by `ExactIcpScheme.enhancePaymentRequirements` — you do not need to set it here.
16
+ *
17
+ * For self-hosted settlement (no facilitator), pass it manually:
18
+ * `extra: { facilitatorPrincipal: 'your-principal' }`
19
+ */
20
+ extra?: Record<string, unknown>;
21
+ }
22
+ export declare function createIcpPaymentRequirements(opts: CreateIcpPaymentRequirementsOptions): PaymentRequirements;
@@ -0,0 +1,19 @@
1
+ import { ASSETS } from '../types/index.js';
2
+ export function createIcpPaymentRequirements(opts) {
3
+ // network format: icp:1:<canisterId>
4
+ const ledgerId = opts.network.split(':').pop();
5
+ const asset = ASSETS[ledgerId];
6
+ return {
7
+ scheme: 'exact',
8
+ network: opts.network,
9
+ amount: opts.amount,
10
+ asset: opts.network, // asset = full network string for ICP
11
+ payTo: opts.payTo,
12
+ maxTimeoutSeconds: opts.maxTimeoutSeconds ?? 300,
13
+ extra: {
14
+ ...(asset ? { symbol: asset.symbol, decimals: asset.decimals } : {}),
15
+ ...opts.extra,
16
+ },
17
+ };
18
+ }
19
+ //# sourceMappingURL=requirements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirements.js","sourceRoot":"","sources":["../../src/server/requirements.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAuB1C,MAAM,UAAU,4BAA4B,CAC1C,IAAyC;IAEzC,qCAAqC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAA;IAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;IAE9B,OAAO;QACL,MAAM,EAAE,OAAO;QACf,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,sCAAsC;QAC3D,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,GAAG;QAChD,KAAK,EAAE;YACL,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpE,GAAG,IAAI,CAAC,KAAK;SACd;KACqB,CAAA;AAC1B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { PaymentRequirements, Network, Price, AssetAmount, SchemeNetworkServer } from '@x402/core/types';
2
+ export declare class ExactIcpScheme implements SchemeNetworkServer {
3
+ readonly scheme = "exact";
4
+ /**
5
+ * Convert a price to ICP asset amount.
6
+ *
7
+ * - If price is already an AssetAmount, pass through.
8
+ * - If price is a string/number in smallest units (e8s), use with the
9
+ * network's default asset.
10
+ * - USD-denominated prices (e.g. "$0.10") are not yet supported.
11
+ */
12
+ parsePrice(price: Price, network: Network): Promise<AssetAmount>;
13
+ /**
14
+ * Merge facilitator extra data (e.g. facilitatorPrincipal) into requirements.
15
+ */
16
+ enhancePaymentRequirements(paymentRequirements: PaymentRequirements, supportedKind: {
17
+ x402Version: number;
18
+ scheme: string;
19
+ network: Network;
20
+ extra?: Record<string, unknown>;
21
+ }, _facilitatorExtensions: string[]): Promise<PaymentRequirements>;
22
+ }
@@ -0,0 +1,51 @@
1
+ import { ASSETS } from '../types/index.js';
2
+ export class ExactIcpScheme {
3
+ scheme = 'exact';
4
+ /**
5
+ * Convert a price to ICP asset amount.
6
+ *
7
+ * - If price is already an AssetAmount, pass through.
8
+ * - If price is a string/number in smallest units (e8s), use with the
9
+ * network's default asset.
10
+ * - USD-denominated prices (e.g. "$0.10") are not yet supported.
11
+ */
12
+ async parsePrice(price, network) {
13
+ // Already an AssetAmount
14
+ if (typeof price === 'object' && 'amount' in price && 'asset' in price) {
15
+ return price;
16
+ }
17
+ const raw = typeof price === 'number' ? price.toString() : price;
18
+ if (raw.startsWith('$')) {
19
+ throw new Error('USD price conversion not yet supported for ICP. ' +
20
+ 'Specify amount in smallest token units (e8s for ICP, e6s for ckUSDC).');
21
+ }
22
+ // network format: icp:1:<canisterId>
23
+ const ledgerId = network.split(':').pop();
24
+ const asset = ASSETS[ledgerId];
25
+ if (!asset) {
26
+ throw new Error(`Unknown ICP asset for network: ${network}`);
27
+ }
28
+ return {
29
+ amount: raw,
30
+ asset: network, // asset = full network string, facilitator strips canisterId
31
+ };
32
+ }
33
+ /**
34
+ * Merge facilitator extra data (e.g. facilitatorPrincipal) into requirements.
35
+ */
36
+ async enhancePaymentRequirements(paymentRequirements, supportedKind, _facilitatorExtensions) {
37
+ const ledgerId = paymentRequirements.network.split(':').pop();
38
+ const asset = ASSETS[ledgerId];
39
+ return {
40
+ ...paymentRequirements,
41
+ extra: {
42
+ ...paymentRequirements.extra,
43
+ // Merge in facilitator-provided extra (e.g. facilitatorPrincipal)
44
+ ...supportedKind.extra,
45
+ // Add asset metadata for clients
46
+ ...(asset ? { symbol: asset.symbol, decimals: asset.decimals } : {}),
47
+ },
48
+ };
49
+ }
50
+ }
51
+ //# sourceMappingURL=scheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheme.js","sourceRoot":"","sources":["../../src/server/scheme.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE1C,MAAM,OAAO,cAAc;IAChB,MAAM,GAAG,OAAO,CAAA;IAEzB;;;;;;;OAOG;IACH,KAAK,CAAC,UAAU,CAAC,KAAY,EAAE,OAAgB;QAC7C,yBAAyB;QACzB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,EAAE,CAAC;YACvE,OAAO,KAAK,CAAA;QACd,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAe,CAAA;QAE1E,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,kDAAkD;gBAClD,uEAAuE,CACxE,CAAA;QACH,CAAC;QAED,qCAAqC;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAA;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;QAE9B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,kCAAkC,OAAO,EAAE,CAAC,CAAA;QAC9D,CAAC;QAED,OAAO;YACL,MAAM,EAAE,GAAG;YACX,KAAK,EAAE,OAAO,EAAE,6DAA6D;SAC9E,CAAA;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,0BAA0B,CAC9B,mBAAwC,EACxC,aAKC,EACD,sBAAgC;QAEhC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAA;QAC9D,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;QAE9B,OAAO;YACL,GAAG,mBAAmB;YACtB,KAAK,EAAE;gBACL,GAAG,mBAAmB,CAAC,KAAK;gBAC5B,kEAAkE;gBAClE,GAAG,aAAa,CAAC,KAAK;gBACtB,iCAAiC;gBACjC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACrE;SACF,CAAA;IACH,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ export type { PaymentPayload, PaymentRequirements, VerifyResponse, SettleResponse, Network, Price, AssetAmount, SchemeNetworkServer, } from '@x402/core/types';
2
+ export interface IcpPayloadAuthorization {
3
+ to: string;
4
+ value: string;
5
+ expiresAt: number;
6
+ nonce: number;
7
+ }
8
+ export interface IcpPayload {
9
+ signature: string;
10
+ authorization: IcpPayloadAuthorization;
11
+ }
12
+ export interface AssetConfig {
13
+ symbol: string;
14
+ name: string;
15
+ decimals: number;
16
+ ledgerId: string;
17
+ transferFee: number;
18
+ }
19
+ export declare const ASSETS: Record<string, AssetConfig>;
@@ -0,0 +1,24 @@
1
+ export const ASSETS = {
2
+ 'ryjl3-tyaaa-aaaaa-aaaba-cai': {
3
+ symbol: 'ICP', name: 'Internet Computer',
4
+ decimals: 8, ledgerId: 'ryjl3-tyaaa-aaaaa-aaaba-cai', transferFee: 10_000,
5
+ },
6
+ 'xevnm-gaaaa-aaaar-qafnq-cai': {
7
+ symbol: 'ckUSDC', name: 'Chain-key USDC',
8
+ decimals: 6, ledgerId: 'xevnm-gaaaa-aaaar-qafnq-cai', transferFee: 10_000,
9
+ },
10
+ 'cngnf-vqaaa-aaaar-qag4q-cai': {
11
+ symbol: 'ckUSDT', name: 'Chain-key USDT',
12
+ decimals: 6, ledgerId: 'cngnf-vqaaa-aaaar-qag4q-cai', transferFee: 10_000,
13
+ },
14
+ // Testnet tokens (faucet.internetcomputer.org)
15
+ 'xafvr-biaaa-aaaai-aql5q-cai': {
16
+ symbol: 'TESTICP', name: 'Test ICP',
17
+ decimals: 8, ledgerId: 'xafvr-biaaa-aaaai-aql5q-cai', transferFee: 10_000,
18
+ },
19
+ '3jkp5-oyaaa-aaaaj-azwqa-cai': {
20
+ symbol: 'TICRC1', name: 'Test ICRC-1',
21
+ decimals: 8, ledgerId: '3jkp5-oyaaa-aaaaj-azwqa-cai', transferFee: 10_000,
22
+ },
23
+ };
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAmCA,MAAM,CAAC,MAAM,MAAM,GAAgC;IACjD,6BAA6B,EAAE;QAC7B,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,mBAAmB;QACxC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,6BAA6B,EAAE,WAAW,EAAE,MAAM;KAC1E;IACD,6BAA6B,EAAE;QAC7B,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB;QACxC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,6BAA6B,EAAE,WAAW,EAAE,MAAM;KAC1E;IACD,6BAA6B,EAAE;QAC7B,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB;QACxC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,6BAA6B,EAAE,WAAW,EAAE,MAAM;KAC1E;IACD,+CAA+C;IAC/C,6BAA6B,EAAE;QAC7B,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU;QACnC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,6BAA6B,EAAE,WAAW,EAAE,MAAM;KAC1E;IACD,6BAA6B,EAAE;QAC7B,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa;QACrC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,6BAA6B,EAAE,WAAW,EAAE,MAAM;KAC1E;CACF,CAAA"}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@canister-software/x402-icp",
3
+ "version": "0.1.0",
4
+ "description": "x402 payment scheme for Internet Computer (ICP) — ICRC-2 approve/transferFrom",
5
+ "type": "module",
6
+ "exports": {
7
+ "./client": {
8
+ "types": "./dist/client/index.d.ts",
9
+ "import": "./dist/client/index.js"
10
+ },
11
+ "./server": {
12
+ "types": "./dist/server/index.d.ts",
13
+ "import": "./dist/server/index.js"
14
+ },
15
+ "./types": {
16
+ "types": "./dist/types/index.d.ts",
17
+ "import": "./dist/types/index.js"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "typecheck": "tsc --noEmit",
23
+ "lint": "eslint src",
24
+ "format": "prettier --write src",
25
+ "format:check": "prettier --check src"
26
+ },
27
+ "keywords": [
28
+ "x402",
29
+ "icp",
30
+ "internet-computer",
31
+ "icrc2",
32
+ "payments",
33
+ "web3",
34
+ "http-payments",
35
+ "dfinity",
36
+ "canister"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18"
40
+ },
41
+ "peerDependencies": {
42
+ "@x402/core": ">=2.4.0"
43
+ },
44
+ "dependencies": {
45
+ "@dfinity/agent": "^2.2.0",
46
+ "@dfinity/candid": "^2.2.0",
47
+ "@dfinity/identity": "^2.2.0",
48
+ "@dfinity/identity-secp256k1": "^2.4.1",
49
+ "@dfinity/principal": "^2.2.0",
50
+ "@noble/hashes": "^1.8.0",
51
+ "cborg": "^4.2.0"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^22.0.0",
55
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
56
+ "@typescript-eslint/parser": "^8.0.0",
57
+ "eslint": "^9.0.0",
58
+ "eslint-config-prettier": "^10.0.0",
59
+ "prettier": "^3.0.0",
60
+ "typescript": "^5.7.0"
61
+ },
62
+ "files": [
63
+ "dist",
64
+ "README.md",
65
+ "LICENSE"
66
+ ],
67
+ "author": "Canister Software Inc.",
68
+ "license": "MIT",
69
+ "homepage": "https://github.com/Canister-Software/x402-icp#readme",
70
+ "bugs": {
71
+ "url": "https://github.com/Canister-Software/x402-icp/issues"
72
+ },
73
+ "repository": {
74
+ "type": "git",
75
+ "url": "https://github.com/Canister-Software/x402-icp"
76
+ }
77
+ }