@t402/polkadot 2.3.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 (40) hide show
  1. package/README.md +139 -0
  2. package/dist/exact-direct/client/index.cjs +189 -0
  3. package/dist/exact-direct/client/index.cjs.map +1 -0
  4. package/dist/exact-direct/client/index.d.cts +39 -0
  5. package/dist/exact-direct/client/index.d.ts +39 -0
  6. package/dist/exact-direct/client/index.mjs +161 -0
  7. package/dist/exact-direct/client/index.mjs.map +1 -0
  8. package/dist/exact-direct/facilitator/index.cjs +394 -0
  9. package/dist/exact-direct/facilitator/index.cjs.map +1 -0
  10. package/dist/exact-direct/facilitator/index.d.cts +55 -0
  11. package/dist/exact-direct/facilitator/index.d.ts +55 -0
  12. package/dist/exact-direct/facilitator/index.mjs +366 -0
  13. package/dist/exact-direct/facilitator/index.mjs.map +1 -0
  14. package/dist/exact-direct/server/index.cjs +277 -0
  15. package/dist/exact-direct/server/index.cjs.map +1 -0
  16. package/dist/exact-direct/server/index.d.cts +109 -0
  17. package/dist/exact-direct/server/index.d.ts +109 -0
  18. package/dist/exact-direct/server/index.mjs +248 -0
  19. package/dist/exact-direct/server/index.mjs.map +1 -0
  20. package/dist/index.cjs +293 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +148 -0
  23. package/dist/index.d.ts +148 -0
  24. package/dist/index.mjs +235 -0
  25. package/dist/index.mjs.map +1 -0
  26. package/dist/types-Dbjfcz2Y.d.cts +135 -0
  27. package/dist/types-Dbjfcz2Y.d.ts +135 -0
  28. package/package.json +103 -0
  29. package/src/constants.ts +87 -0
  30. package/src/exact-direct/client/index.ts +5 -0
  31. package/src/exact-direct/client/scheme.ts +117 -0
  32. package/src/exact-direct/facilitator/index.ts +4 -0
  33. package/src/exact-direct/facilitator/scheme.ts +315 -0
  34. package/src/exact-direct/server/index.ts +9 -0
  35. package/src/exact-direct/server/register.ts +57 -0
  36. package/src/exact-direct/server/scheme.ts +216 -0
  37. package/src/index.ts +84 -0
  38. package/src/tokens.ts +111 -0
  39. package/src/types.ts +151 -0
  40. package/src/utils.ts +176 -0
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # @t402/polkadot
2
+
3
+ Polkadot Asset Hub implementation of the t402 payment protocol using the **exact-direct** payment scheme with Assets Pallet transfers.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @t402/polkadot
9
+ # or
10
+ pnpm add @t402/polkadot
11
+ ```
12
+
13
+ ## Overview
14
+
15
+ This package provides support for USDT payments on Polkadot Asset Hub parachains using the exact-direct scheme. The client executes the transfer directly on-chain, then provides the extrinsic hash as proof of payment.
16
+
17
+ Three main components:
18
+
19
+ - **Client** - For applications that make payments (have Polkadot wallets)
20
+ - **Facilitator** - For payment processors that verify extrinsics via Subscan
21
+ - **Server** - For resource servers that accept payments and build payment requirements
22
+
23
+ ## Supported Networks
24
+
25
+ | Network | CAIP-2 Identifier | Asset ID | Status |
26
+ |---------|-------------------|----------|--------|
27
+ | Polkadot Asset Hub | `polkadot:68d56f15f85d3136970ec16946040bc1` | 1984 | Production |
28
+ | Kusama Asset Hub | `polkadot:48239ef607d7928...` | 1984 | Production |
29
+ | Westend Asset Hub | `polkadot:e143f23803ac50e8f6f8e62695d1ce9e` | 1984 | Testnet |
30
+
31
+ ## Package Exports
32
+
33
+ ### Main Package (`@t402/polkadot`)
34
+
35
+ **Constants:**
36
+ - `POLKADOT_ASSET_HUB_CAIP2` - CAIP-2 identifier for Polkadot Asset Hub
37
+ - `KUSAMA_ASSET_HUB_CAIP2` - CAIP-2 identifier for Kusama Asset Hub
38
+ - `WESTEND_ASSET_HUB_CAIP2` - CAIP-2 identifier for Westend testnet
39
+ - `POLKADOT_NETWORKS` - Array of supported networks
40
+ - `getNetworkConfig(network)` - Get network configuration
41
+
42
+ **Tokens:**
43
+ - `USDT_POLKADOT` - USDT token config (Asset ID: 1984)
44
+ - `TOKEN_REGISTRY` - Token configurations by network
45
+ - `getTokenConfig(network, symbol)` - Get token by symbol
46
+ - `getDefaultToken(network)` - Get default token for network
47
+
48
+ **Utilities:**
49
+ - `isValidAddress(address)` - Validate SS58 address format
50
+ - `isValidExtrinsicHash(hash)` - Validate extrinsic hash format
51
+ - `compareAddresses(addr1, addr2)` - Compare two addresses
52
+ - `formatAmount(amount, decimals)` - Format for display
53
+ - `parseAmount(amount, decimals)` - Parse to smallest units
54
+ - `extractAssetTransfer(extrinsic)` - Extract transfer details
55
+
56
+ ### Client (`@t402/polkadot/exact-direct/client`)
57
+
58
+ ```typescript
59
+ import { createExactDirectPolkadotClient } from '@t402/polkadot/exact-direct/client';
60
+
61
+ const client = createExactDirectPolkadotClient({
62
+ signer: myPolkadotSigner,
63
+ });
64
+ ```
65
+
66
+ ### Server (`@t402/polkadot/exact-direct/server`)
67
+
68
+ ```typescript
69
+ import { registerExactDirectPolkadotServer } from '@t402/polkadot/exact-direct/server';
70
+
71
+ registerExactDirectPolkadotServer(server);
72
+ ```
73
+
74
+ ### Facilitator (`@t402/polkadot/exact-direct/facilitator`)
75
+
76
+ ```typescript
77
+ import { createExactDirectPolkadotFacilitator } from '@t402/polkadot/exact-direct/facilitator';
78
+
79
+ const facilitator = createExactDirectPolkadotFacilitator(signer);
80
+ ```
81
+
82
+ ## Payment Flow
83
+
84
+ 1. **Client** requests protected resource
85
+ 2. **Server** responds with 402 + payment requirements (network, amount, payTo)
86
+ 3. **Client** executes `assets.transfer` extrinsic on Polkadot Asset Hub
87
+ 4. **Client** submits extrinsic hash as payment proof
88
+ 5. **Facilitator** queries Subscan API to verify the extrinsic
89
+ 6. **Facilitator** confirms payment matches requirements
90
+
91
+ ## Payload Structure
92
+
93
+ ```typescript
94
+ interface ExactDirectPolkadotPayload {
95
+ extrinsicHash: string; // 0x-prefixed extrinsic hash
96
+ blockHash: string; // 0x-prefixed block hash
97
+ extrinsicIndex: number; // Index within block
98
+ from: string; // Sender SS58 address
99
+ to: string; // Recipient SS58 address
100
+ amount: string; // Amount in smallest units
101
+ assetId: number; // Asset ID (1984 for USDT)
102
+ }
103
+ ```
104
+
105
+ ## Address Format
106
+
107
+ Polkadot uses SS58 addresses:
108
+ - 45-50 characters
109
+ - Base58 encoded (no 0, O, I, l)
110
+ - Network-specific prefixes (0 for Polkadot, 2 for Kusama, 42 for generic)
111
+
112
+ Examples:
113
+ ```
114
+ 15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5 (Polkadot)
115
+ HNZata7iMYWmk5RvZRTiAsSDhV8366zq2YGb3tLH5Upf74F (Kusama)
116
+ ```
117
+
118
+ ## Development
119
+
120
+ ```bash
121
+ # Build
122
+ pnpm build
123
+
124
+ # Test
125
+ pnpm test
126
+
127
+ # Test with coverage
128
+ pnpm test:coverage
129
+
130
+ # Lint
131
+ pnpm lint
132
+ ```
133
+
134
+ ## Related Packages
135
+
136
+ - `@t402/core` - Core protocol types and client
137
+ - `@t402/fetch` - HTTP wrapper with automatic payment handling
138
+ - `@t402/evm` - EVM chains implementation
139
+ - `@t402/tezos` - Tezos implementation
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/exact-direct/client/index.ts
21
+ var client_exports = {};
22
+ __export(client_exports, {
23
+ ExactDirectPolkadotClient: () => ExactDirectPolkadotClient,
24
+ createExactDirectPolkadotClient: () => createExactDirectPolkadotClient
25
+ });
26
+ module.exports = __toCommonJS(client_exports);
27
+
28
+ // src/constants.ts
29
+ var POLKADOT_CAIP2_NAMESPACE = "polkadot";
30
+ var POLKADOT_ASSET_HUB_CAIP2 = "polkadot:68d56f15f85d3136970ec16946040bc1";
31
+ var KUSAMA_ASSET_HUB_CAIP2 = "polkadot:48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a";
32
+ var WESTEND_ASSET_HUB_CAIP2 = "polkadot:e143f23803ac50e8f6f8e62695d1ce9e";
33
+ var SCHEME_EXACT_DIRECT = "exact-direct";
34
+ var DEFAULT_POLKADOT_INDEXER = "https://assethub-polkadot.api.subscan.io";
35
+ var DEFAULT_KUSAMA_INDEXER = "https://assethub-kusama.api.subscan.io";
36
+ var DEFAULT_WESTEND_INDEXER = "https://assethub-westend.api.subscan.io";
37
+ var DEFAULT_POLKADOT_RPC = "wss://polkadot-asset-hub-rpc.polkadot.io";
38
+ var DEFAULT_KUSAMA_RPC = "wss://kusama-asset-hub-rpc.polkadot.io";
39
+ var DEFAULT_WESTEND_RPC = "wss://westend-asset-hub-rpc.polkadot.io";
40
+ var POLKADOT_NETWORKS = {
41
+ [POLKADOT_ASSET_HUB_CAIP2]: {
42
+ name: "Polkadot Asset Hub",
43
+ caip2: POLKADOT_ASSET_HUB_CAIP2,
44
+ rpcUrl: DEFAULT_POLKADOT_RPC,
45
+ indexerUrl: DEFAULT_POLKADOT_INDEXER,
46
+ genesisHash: "0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f",
47
+ ss58Prefix: 0,
48
+ // Polkadot
49
+ isTestnet: false
50
+ },
51
+ [KUSAMA_ASSET_HUB_CAIP2]: {
52
+ name: "Kusama Asset Hub",
53
+ caip2: KUSAMA_ASSET_HUB_CAIP2,
54
+ rpcUrl: DEFAULT_KUSAMA_RPC,
55
+ indexerUrl: DEFAULT_KUSAMA_INDEXER,
56
+ genesisHash: "0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a",
57
+ ss58Prefix: 2,
58
+ // Kusama
59
+ isTestnet: false
60
+ },
61
+ [WESTEND_ASSET_HUB_CAIP2]: {
62
+ name: "Westend Asset Hub",
63
+ caip2: WESTEND_ASSET_HUB_CAIP2,
64
+ rpcUrl: DEFAULT_WESTEND_RPC,
65
+ indexerUrl: DEFAULT_WESTEND_INDEXER,
66
+ genesisHash: "0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e",
67
+ ss58Prefix: 42,
68
+ // Generic Substrate
69
+ isTestnet: true
70
+ }
71
+ };
72
+
73
+ // src/tokens.ts
74
+ var USDT_POLKADOT = {
75
+ assetId: 1984,
76
+ symbol: "USDT",
77
+ name: "Tether USD",
78
+ decimals: 6,
79
+ issuer: "Tether"
80
+ };
81
+ var USDT_KUSAMA = {
82
+ assetId: 1984,
83
+ symbol: "USDT",
84
+ name: "Tether USD",
85
+ decimals: 6,
86
+ issuer: "Tether"
87
+ };
88
+ var USDT_WESTEND = {
89
+ assetId: 1984,
90
+ symbol: "USDT",
91
+ name: "Test Tether USD",
92
+ decimals: 6
93
+ };
94
+ var TOKEN_REGISTRY = {
95
+ [POLKADOT_ASSET_HUB_CAIP2]: {
96
+ USDT: USDT_POLKADOT
97
+ },
98
+ [KUSAMA_ASSET_HUB_CAIP2]: {
99
+ USDT: USDT_KUSAMA
100
+ },
101
+ [WESTEND_ASSET_HUB_CAIP2]: {
102
+ USDT: USDT_WESTEND
103
+ }
104
+ };
105
+ var DEFAULT_TOKENS = {
106
+ [POLKADOT_ASSET_HUB_CAIP2]: USDT_POLKADOT,
107
+ [KUSAMA_ASSET_HUB_CAIP2]: USDT_KUSAMA,
108
+ [WESTEND_ASSET_HUB_CAIP2]: USDT_WESTEND
109
+ };
110
+ function getTokenConfig(network, symbol = "USDT") {
111
+ return TOKEN_REGISTRY[network]?.[symbol];
112
+ }
113
+ function getAssetId(network, symbol = "USDT") {
114
+ return getTokenConfig(network, symbol)?.assetId;
115
+ }
116
+
117
+ // src/utils.ts
118
+ function isValidAddress(address) {
119
+ if (!address || typeof address !== "string") {
120
+ return false;
121
+ }
122
+ const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{45,50}$/;
123
+ return base58Regex.test(address);
124
+ }
125
+
126
+ // src/exact-direct/client/scheme.ts
127
+ var ExactDirectPolkadotClient = class {
128
+ scheme = SCHEME_EXACT_DIRECT;
129
+ signer;
130
+ constructor(config) {
131
+ this.signer = config.signer;
132
+ }
133
+ /**
134
+ * Create a payment payload by executing the transfer
135
+ */
136
+ async createPaymentPayload(t402Version, requirements) {
137
+ this.validateRequirements(requirements);
138
+ const { network, amount, payTo, extra } = requirements;
139
+ const symbol = extra?.assetSymbol || "USDT";
140
+ const assetId = extra?.assetId ?? getAssetId(network, symbol);
141
+ if (assetId === void 0) {
142
+ throw new Error(`Unknown asset ${symbol} on network ${network}`);
143
+ }
144
+ const from = await this.signer.getAddress();
145
+ const { extrinsicHash, blockHash, extrinsicIndex } = await this.signer.transferAsset(assetId, payTo, amount);
146
+ const polkadotPayload = {
147
+ extrinsicHash,
148
+ blockHash,
149
+ extrinsicIndex,
150
+ from,
151
+ to: payTo,
152
+ amount,
153
+ assetId
154
+ };
155
+ return {
156
+ t402Version,
157
+ payload: polkadotPayload
158
+ };
159
+ }
160
+ /**
161
+ * Validate payment requirements
162
+ */
163
+ validateRequirements(requirements) {
164
+ if (requirements.scheme !== SCHEME_EXACT_DIRECT) {
165
+ throw new Error(
166
+ `Invalid scheme: expected ${SCHEME_EXACT_DIRECT}, got ${requirements.scheme}`
167
+ );
168
+ }
169
+ if (!requirements.network.startsWith(`${POLKADOT_CAIP2_NAMESPACE}:`)) {
170
+ throw new Error(`Invalid network: ${requirements.network}`);
171
+ }
172
+ if (!isValidAddress(requirements.payTo)) {
173
+ throw new Error(`Invalid payTo address: ${requirements.payTo}`);
174
+ }
175
+ const amount = BigInt(requirements.amount);
176
+ if (amount <= 0n) {
177
+ throw new Error(`Invalid amount: ${requirements.amount}`);
178
+ }
179
+ }
180
+ };
181
+ function createExactDirectPolkadotClient(config) {
182
+ return new ExactDirectPolkadotClient(config);
183
+ }
184
+ // Annotate the CommonJS export names for ESM import in node:
185
+ 0 && (module.exports = {
186
+ ExactDirectPolkadotClient,
187
+ createExactDirectPolkadotClient
188
+ });
189
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/exact-direct/client/index.ts","../../../src/constants.ts","../../../src/tokens.ts","../../../src/utils.ts","../../../src/exact-direct/client/scheme.ts"],"sourcesContent":["export {\n ExactDirectPolkadotClient,\n createExactDirectPolkadotClient,\n type ExactDirectPolkadotClientConfig,\n} from \"./scheme.js\";\n","/**\n * Polkadot Asset Hub T402 Constants\n *\n * Polkadot Asset Hub (formerly Statemint) is a common-good parachain\n * that hosts assets like USDT on the Polkadot network.\n */\n\n// CAIP-2 namespace for Polkadot\nexport const POLKADOT_CAIP2_NAMESPACE = \"polkadot\";\n\n// CAIP-2 network identifiers (first 32 chars of genesis hash)\n// Polkadot Asset Hub (Parachain ID: 1000)\nexport const POLKADOT_ASSET_HUB_CAIP2 = \"polkadot:68d56f15f85d3136970ec16946040bc1\";\n\n// Kusama Asset Hub (Parachain ID: 1000 on Kusama)\nexport const KUSAMA_ASSET_HUB_CAIP2 = \"polkadot:48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a\";\n\n// Westend Asset Hub (Testnet)\nexport const WESTEND_ASSET_HUB_CAIP2 = \"polkadot:e143f23803ac50e8f6f8e62695d1ce9e\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default indexers (Subscan API)\nexport const DEFAULT_POLKADOT_INDEXER = \"https://assethub-polkadot.api.subscan.io\";\nexport const DEFAULT_KUSAMA_INDEXER = \"https://assethub-kusama.api.subscan.io\";\nexport const DEFAULT_WESTEND_INDEXER = \"https://assethub-westend.api.subscan.io\";\n\n// Default RPC endpoints\nexport const DEFAULT_POLKADOT_RPC = \"wss://polkadot-asset-hub-rpc.polkadot.io\";\nexport const DEFAULT_KUSAMA_RPC = \"wss://kusama-asset-hub-rpc.polkadot.io\";\nexport const DEFAULT_WESTEND_RPC = \"wss://westend-asset-hub-rpc.polkadot.io\";\n\n// Network configurations\nexport interface PolkadotNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly rpcUrl: string;\n readonly indexerUrl: string;\n readonly genesisHash: string;\n readonly ss58Prefix: number;\n readonly isTestnet: boolean;\n}\n\nexport const POLKADOT_NETWORKS: Record<string, PolkadotNetworkConfig> = {\n [POLKADOT_ASSET_HUB_CAIP2]: {\n name: \"Polkadot Asset Hub\",\n caip2: POLKADOT_ASSET_HUB_CAIP2,\n rpcUrl: DEFAULT_POLKADOT_RPC,\n indexerUrl: DEFAULT_POLKADOT_INDEXER,\n genesisHash: \"0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f\",\n ss58Prefix: 0, // Polkadot\n isTestnet: false,\n },\n [KUSAMA_ASSET_HUB_CAIP2]: {\n name: \"Kusama Asset Hub\",\n caip2: KUSAMA_ASSET_HUB_CAIP2,\n rpcUrl: DEFAULT_KUSAMA_RPC,\n indexerUrl: DEFAULT_KUSAMA_INDEXER,\n genesisHash: \"0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a\",\n ss58Prefix: 2, // Kusama\n isTestnet: false,\n },\n [WESTEND_ASSET_HUB_CAIP2]: {\n name: \"Westend Asset Hub\",\n caip2: WESTEND_ASSET_HUB_CAIP2,\n rpcUrl: DEFAULT_WESTEND_RPC,\n indexerUrl: DEFAULT_WESTEND_INDEXER,\n genesisHash: \"0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e\",\n ss58Prefix: 42, // Generic Substrate\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): PolkadotNetworkConfig | undefined {\n return POLKADOT_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Polkadot network\n */\nexport function isPolkadotNetwork(network: string): boolean {\n return network.startsWith(`${POLKADOT_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Polkadot Asset Hub Token Registry\n *\n * On Polkadot Asset Hub, tokens are identified by Asset IDs.\n * USDT is Asset ID 1984, created by Tether.\n */\n\nimport {\n POLKADOT_ASSET_HUB_CAIP2,\n KUSAMA_ASSET_HUB_CAIP2,\n WESTEND_ASSET_HUB_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Polkadot Asset Hub\n */\nexport interface TokenConfig {\n /** Asset ID on Asset Hub */\n readonly assetId: number;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Issuer (creator of the asset) */\n readonly issuer?: string;\n}\n\n/**\n * USDT on Polkadot Asset Hub\n * Asset ID: 1984\n * Decimals: 6\n */\nexport const USDT_POLKADOT: TokenConfig = {\n assetId: 1984,\n symbol: \"USDT\",\n name: \"Tether USD\",\n decimals: 6,\n issuer: \"Tether\",\n};\n\n/**\n * USDT on Kusama Asset Hub\n * Asset ID: 1984 (same as Polkadot)\n */\nexport const USDT_KUSAMA: TokenConfig = {\n assetId: 1984,\n symbol: \"USDT\",\n name: \"Tether USD\",\n decimals: 6,\n issuer: \"Tether\",\n};\n\n/**\n * Test USDT on Westend Asset Hub (testnet)\n */\nexport const USDT_WESTEND: TokenConfig = {\n assetId: 1984,\n symbol: \"USDT\",\n name: \"Test Tether USD\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [POLKADOT_ASSET_HUB_CAIP2]: {\n USDT: USDT_POLKADOT,\n },\n [KUSAMA_ASSET_HUB_CAIP2]: {\n USDT: USDT_KUSAMA,\n },\n [WESTEND_ASSET_HUB_CAIP2]: {\n USDT: USDT_WESTEND,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [POLKADOT_ASSET_HUB_CAIP2]: USDT_POLKADOT,\n [KUSAMA_ASSET_HUB_CAIP2]: USDT_KUSAMA,\n [WESTEND_ASSET_HUB_CAIP2]: USDT_WESTEND,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"USDT\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get asset ID for a token on a network\n */\nexport function getAssetId(network: string, symbol: string = \"USDT\"): number | undefined {\n return getTokenConfig(network, symbol)?.assetId;\n}\n","/**\n * Polkadot Asset Hub Utility Functions\n */\n\nimport type { PolkadotExtrinsicResult, ParsedAssetTransfer } from \"./types.js\";\n\n/**\n * Validate a Polkadot SS58 address format\n * SS58 addresses are base58-encoded with a checksum\n */\nexport function isValidAddress(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // SS58 addresses typically start with 1 (Polkadot), or have other prefixes\n // Length is typically 47-48 characters for Polkadot addresses\n // For a simple validation, check base58 characters and length\n const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{45,50}$/;\n return base58Regex.test(address);\n}\n\n/**\n * Validate an extrinsic hash format\n * Extrinsic hashes are 32-byte hex strings prefixed with 0x\n */\nexport function isValidExtrinsicHash(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Validate a block hash format\n */\nexport function isValidBlockHash(hash: string): boolean {\n return isValidExtrinsicHash(hash); // Same format\n}\n\n/**\n * Compare two SS58 addresses (case-sensitive)\n */\nexport function compareAddresses(addr1: string, addr2: string): boolean {\n return addr1 === addr2;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract asset transfer details from an extrinsic result\n */\nexport function extractAssetTransfer(\n result: PolkadotExtrinsicResult,\n): ParsedAssetTransfer | null {\n if (!result.success) {\n return null;\n }\n\n // Check if this is an assets.transfer or assets.transferKeepAlive call\n if (result.module !== \"assets\") {\n return null;\n }\n\n if (result.call !== \"transfer\" && result.call !== \"transferKeepAlive\") {\n return null;\n }\n\n // Extract transfer details from args\n const assetId = result.args.id as number | undefined;\n const to = result.args.target as string | undefined;\n const amount = result.args.amount as string | undefined;\n\n if (assetId === undefined || !to || !amount) {\n return null;\n }\n\n return {\n assetId,\n from: result.signer,\n to,\n amount: amount.toString(),\n success: true,\n };\n}\n\n/**\n * Extract asset transfer from events (alternative method)\n */\nexport function extractAssetTransferFromEvents(\n result: PolkadotExtrinsicResult,\n): ParsedAssetTransfer | null {\n if (!result.success) {\n return null;\n }\n\n // Look for assets.Transferred event\n const transferEvent = result.events.find(\n (e) => e.module === \"assets\" && e.name === \"Transferred\",\n );\n\n if (!transferEvent) {\n return null;\n }\n\n const assetId = transferEvent.data.assetId as number | undefined;\n const from = transferEvent.data.from as string | undefined;\n const to = transferEvent.data.to as string | undefined;\n const amount = transferEvent.data.amount as string | undefined;\n\n if (assetId === undefined || !from || !to || !amount) {\n return null;\n }\n\n return {\n assetId,\n from,\n to,\n amount: amount.toString(),\n success: true,\n };\n}\n\n/**\n * Build a unique extrinsic identifier from block hash and index\n */\nexport function buildExtrinsicId(blockHash: string, extrinsicIndex: number): string {\n return `${blockHash}-${extrinsicIndex}`;\n}\n\n/**\n * Parse an extrinsic identifier back to components\n */\nexport function parseExtrinsicId(\n extrinsicId: string,\n): { blockHash: string; extrinsicIndex: number } | null {\n const lastDashIndex = extrinsicId.lastIndexOf(\"-\");\n if (lastDashIndex === -1) {\n return null;\n }\n\n const blockHash = extrinsicId.slice(0, lastDashIndex);\n const extrinsicIndex = parseInt(extrinsicId.slice(lastDashIndex + 1), 10);\n\n if (!isValidBlockHash(blockHash) || isNaN(extrinsicIndex)) {\n return null;\n }\n\n return { blockHash, extrinsicIndex };\n}\n","/**\n * Polkadot Exact-Direct Client Scheme\n *\n * In the exact-direct scheme, the client executes the asset transfer directly\n * and provides the extrinsic hash as proof of payment.\n */\n\nimport type {\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkClient,\n} from \"@t402/core/types\";\nimport type { ClientPolkadotSigner, ExactDirectPolkadotPayload } from \"../../types.js\";\nimport { SCHEME_EXACT_DIRECT, POLKADOT_CAIP2_NAMESPACE } from \"../../constants.js\";\nimport { getAssetId } from \"../../tokens.js\";\nimport { isValidAddress } from \"../../utils.js\";\n\n/**\n * Configuration for the exact-direct client\n */\nexport interface ExactDirectPolkadotClientConfig {\n /** Signer for executing transactions */\n signer: ClientPolkadotSigner;\n}\n\n/**\n * Exact-direct client scheme for Polkadot Asset Hub\n */\nexport class ExactDirectPolkadotClient implements SchemeNetworkClient {\n readonly scheme = SCHEME_EXACT_DIRECT;\n private readonly signer: ClientPolkadotSigner;\n\n constructor(config: ExactDirectPolkadotClientConfig) {\n this.signer = config.signer;\n }\n\n /**\n * Create a payment payload by executing the transfer\n */\n async createPaymentPayload(\n t402Version: number,\n requirements: PaymentRequirements,\n ): Promise<Pick<PaymentPayload, \"t402Version\" | \"payload\">> {\n // Validate requirements\n this.validateRequirements(requirements);\n\n const { network, amount, payTo, extra } = requirements;\n\n // Get asset ID from extra or use default USDT\n const symbol = (extra?.assetSymbol as string) || \"USDT\";\n const assetId = (extra?.assetId as number) ?? getAssetId(network, symbol);\n\n if (assetId === undefined) {\n throw new Error(`Unknown asset ${symbol} on network ${network}`);\n }\n\n // Get sender address\n const from = await this.signer.getAddress();\n\n // Execute the transfer\n const { extrinsicHash, blockHash, extrinsicIndex } =\n await this.signer.transferAsset(assetId, payTo, amount);\n\n // Build the payload\n const polkadotPayload: ExactDirectPolkadotPayload = {\n extrinsicHash,\n blockHash,\n extrinsicIndex,\n from,\n to: payTo,\n amount,\n assetId,\n };\n\n return {\n t402Version,\n payload: polkadotPayload,\n };\n }\n\n /**\n * Validate payment requirements\n */\n private validateRequirements(requirements: PaymentRequirements): void {\n // Check scheme\n if (requirements.scheme !== SCHEME_EXACT_DIRECT) {\n throw new Error(\n `Invalid scheme: expected ${SCHEME_EXACT_DIRECT}, got ${requirements.scheme}`,\n );\n }\n\n // Check network\n if (!requirements.network.startsWith(`${POLKADOT_CAIP2_NAMESPACE}:`)) {\n throw new Error(`Invalid network: ${requirements.network}`);\n }\n\n // Check payTo address\n if (!isValidAddress(requirements.payTo)) {\n throw new Error(`Invalid payTo address: ${requirements.payTo}`);\n }\n\n // Check amount\n const amount = BigInt(requirements.amount);\n if (amount <= 0n) {\n throw new Error(`Invalid amount: ${requirements.amount}`);\n }\n }\n}\n\n/**\n * Create an exact-direct client for Polkadot\n */\nexport function createExactDirectPolkadotClient(\n config: ExactDirectPolkadotClientConfig,\n): ExactDirectPolkadotClient {\n return new ExactDirectPolkadotClient(config);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,39 @@
1
+ import { SchemeNetworkClient, PaymentRequirements, PaymentPayload } from '@t402/core/types';
2
+ import { C as ClientPolkadotSigner } from '../../types-Dbjfcz2Y.cjs';
3
+
4
+ /**
5
+ * Polkadot Exact-Direct Client Scheme
6
+ *
7
+ * In the exact-direct scheme, the client executes the asset transfer directly
8
+ * and provides the extrinsic hash as proof of payment.
9
+ */
10
+
11
+ /**
12
+ * Configuration for the exact-direct client
13
+ */
14
+ interface ExactDirectPolkadotClientConfig {
15
+ /** Signer for executing transactions */
16
+ signer: ClientPolkadotSigner;
17
+ }
18
+ /**
19
+ * Exact-direct client scheme for Polkadot Asset Hub
20
+ */
21
+ declare class ExactDirectPolkadotClient implements SchemeNetworkClient {
22
+ readonly scheme = "exact-direct";
23
+ private readonly signer;
24
+ constructor(config: ExactDirectPolkadotClientConfig);
25
+ /**
26
+ * Create a payment payload by executing the transfer
27
+ */
28
+ createPaymentPayload(t402Version: number, requirements: PaymentRequirements): Promise<Pick<PaymentPayload, "t402Version" | "payload">>;
29
+ /**
30
+ * Validate payment requirements
31
+ */
32
+ private validateRequirements;
33
+ }
34
+ /**
35
+ * Create an exact-direct client for Polkadot
36
+ */
37
+ declare function createExactDirectPolkadotClient(config: ExactDirectPolkadotClientConfig): ExactDirectPolkadotClient;
38
+
39
+ export { ExactDirectPolkadotClient, type ExactDirectPolkadotClientConfig, createExactDirectPolkadotClient };
@@ -0,0 +1,39 @@
1
+ import { SchemeNetworkClient, PaymentRequirements, PaymentPayload } from '@t402/core/types';
2
+ import { C as ClientPolkadotSigner } from '../../types-Dbjfcz2Y.js';
3
+
4
+ /**
5
+ * Polkadot Exact-Direct Client Scheme
6
+ *
7
+ * In the exact-direct scheme, the client executes the asset transfer directly
8
+ * and provides the extrinsic hash as proof of payment.
9
+ */
10
+
11
+ /**
12
+ * Configuration for the exact-direct client
13
+ */
14
+ interface ExactDirectPolkadotClientConfig {
15
+ /** Signer for executing transactions */
16
+ signer: ClientPolkadotSigner;
17
+ }
18
+ /**
19
+ * Exact-direct client scheme for Polkadot Asset Hub
20
+ */
21
+ declare class ExactDirectPolkadotClient implements SchemeNetworkClient {
22
+ readonly scheme = "exact-direct";
23
+ private readonly signer;
24
+ constructor(config: ExactDirectPolkadotClientConfig);
25
+ /**
26
+ * Create a payment payload by executing the transfer
27
+ */
28
+ createPaymentPayload(t402Version: number, requirements: PaymentRequirements): Promise<Pick<PaymentPayload, "t402Version" | "payload">>;
29
+ /**
30
+ * Validate payment requirements
31
+ */
32
+ private validateRequirements;
33
+ }
34
+ /**
35
+ * Create an exact-direct client for Polkadot
36
+ */
37
+ declare function createExactDirectPolkadotClient(config: ExactDirectPolkadotClientConfig): ExactDirectPolkadotClient;
38
+
39
+ export { ExactDirectPolkadotClient, type ExactDirectPolkadotClientConfig, createExactDirectPolkadotClient };
@@ -0,0 +1,161 @@
1
+ // src/constants.ts
2
+ var POLKADOT_CAIP2_NAMESPACE = "polkadot";
3
+ var POLKADOT_ASSET_HUB_CAIP2 = "polkadot:68d56f15f85d3136970ec16946040bc1";
4
+ var KUSAMA_ASSET_HUB_CAIP2 = "polkadot:48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a";
5
+ var WESTEND_ASSET_HUB_CAIP2 = "polkadot:e143f23803ac50e8f6f8e62695d1ce9e";
6
+ var SCHEME_EXACT_DIRECT = "exact-direct";
7
+ var DEFAULT_POLKADOT_INDEXER = "https://assethub-polkadot.api.subscan.io";
8
+ var DEFAULT_KUSAMA_INDEXER = "https://assethub-kusama.api.subscan.io";
9
+ var DEFAULT_WESTEND_INDEXER = "https://assethub-westend.api.subscan.io";
10
+ var DEFAULT_POLKADOT_RPC = "wss://polkadot-asset-hub-rpc.polkadot.io";
11
+ var DEFAULT_KUSAMA_RPC = "wss://kusama-asset-hub-rpc.polkadot.io";
12
+ var DEFAULT_WESTEND_RPC = "wss://westend-asset-hub-rpc.polkadot.io";
13
+ var POLKADOT_NETWORKS = {
14
+ [POLKADOT_ASSET_HUB_CAIP2]: {
15
+ name: "Polkadot Asset Hub",
16
+ caip2: POLKADOT_ASSET_HUB_CAIP2,
17
+ rpcUrl: DEFAULT_POLKADOT_RPC,
18
+ indexerUrl: DEFAULT_POLKADOT_INDEXER,
19
+ genesisHash: "0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f",
20
+ ss58Prefix: 0,
21
+ // Polkadot
22
+ isTestnet: false
23
+ },
24
+ [KUSAMA_ASSET_HUB_CAIP2]: {
25
+ name: "Kusama Asset Hub",
26
+ caip2: KUSAMA_ASSET_HUB_CAIP2,
27
+ rpcUrl: DEFAULT_KUSAMA_RPC,
28
+ indexerUrl: DEFAULT_KUSAMA_INDEXER,
29
+ genesisHash: "0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a",
30
+ ss58Prefix: 2,
31
+ // Kusama
32
+ isTestnet: false
33
+ },
34
+ [WESTEND_ASSET_HUB_CAIP2]: {
35
+ name: "Westend Asset Hub",
36
+ caip2: WESTEND_ASSET_HUB_CAIP2,
37
+ rpcUrl: DEFAULT_WESTEND_RPC,
38
+ indexerUrl: DEFAULT_WESTEND_INDEXER,
39
+ genesisHash: "0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e",
40
+ ss58Prefix: 42,
41
+ // Generic Substrate
42
+ isTestnet: true
43
+ }
44
+ };
45
+
46
+ // src/tokens.ts
47
+ var USDT_POLKADOT = {
48
+ assetId: 1984,
49
+ symbol: "USDT",
50
+ name: "Tether USD",
51
+ decimals: 6,
52
+ issuer: "Tether"
53
+ };
54
+ var USDT_KUSAMA = {
55
+ assetId: 1984,
56
+ symbol: "USDT",
57
+ name: "Tether USD",
58
+ decimals: 6,
59
+ issuer: "Tether"
60
+ };
61
+ var USDT_WESTEND = {
62
+ assetId: 1984,
63
+ symbol: "USDT",
64
+ name: "Test Tether USD",
65
+ decimals: 6
66
+ };
67
+ var TOKEN_REGISTRY = {
68
+ [POLKADOT_ASSET_HUB_CAIP2]: {
69
+ USDT: USDT_POLKADOT
70
+ },
71
+ [KUSAMA_ASSET_HUB_CAIP2]: {
72
+ USDT: USDT_KUSAMA
73
+ },
74
+ [WESTEND_ASSET_HUB_CAIP2]: {
75
+ USDT: USDT_WESTEND
76
+ }
77
+ };
78
+ var DEFAULT_TOKENS = {
79
+ [POLKADOT_ASSET_HUB_CAIP2]: USDT_POLKADOT,
80
+ [KUSAMA_ASSET_HUB_CAIP2]: USDT_KUSAMA,
81
+ [WESTEND_ASSET_HUB_CAIP2]: USDT_WESTEND
82
+ };
83
+ function getTokenConfig(network, symbol = "USDT") {
84
+ return TOKEN_REGISTRY[network]?.[symbol];
85
+ }
86
+ function getAssetId(network, symbol = "USDT") {
87
+ return getTokenConfig(network, symbol)?.assetId;
88
+ }
89
+
90
+ // src/utils.ts
91
+ function isValidAddress(address) {
92
+ if (!address || typeof address !== "string") {
93
+ return false;
94
+ }
95
+ const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{45,50}$/;
96
+ return base58Regex.test(address);
97
+ }
98
+
99
+ // src/exact-direct/client/scheme.ts
100
+ var ExactDirectPolkadotClient = class {
101
+ scheme = SCHEME_EXACT_DIRECT;
102
+ signer;
103
+ constructor(config) {
104
+ this.signer = config.signer;
105
+ }
106
+ /**
107
+ * Create a payment payload by executing the transfer
108
+ */
109
+ async createPaymentPayload(t402Version, requirements) {
110
+ this.validateRequirements(requirements);
111
+ const { network, amount, payTo, extra } = requirements;
112
+ const symbol = extra?.assetSymbol || "USDT";
113
+ const assetId = extra?.assetId ?? getAssetId(network, symbol);
114
+ if (assetId === void 0) {
115
+ throw new Error(`Unknown asset ${symbol} on network ${network}`);
116
+ }
117
+ const from = await this.signer.getAddress();
118
+ const { extrinsicHash, blockHash, extrinsicIndex } = await this.signer.transferAsset(assetId, payTo, amount);
119
+ const polkadotPayload = {
120
+ extrinsicHash,
121
+ blockHash,
122
+ extrinsicIndex,
123
+ from,
124
+ to: payTo,
125
+ amount,
126
+ assetId
127
+ };
128
+ return {
129
+ t402Version,
130
+ payload: polkadotPayload
131
+ };
132
+ }
133
+ /**
134
+ * Validate payment requirements
135
+ */
136
+ validateRequirements(requirements) {
137
+ if (requirements.scheme !== SCHEME_EXACT_DIRECT) {
138
+ throw new Error(
139
+ `Invalid scheme: expected ${SCHEME_EXACT_DIRECT}, got ${requirements.scheme}`
140
+ );
141
+ }
142
+ if (!requirements.network.startsWith(`${POLKADOT_CAIP2_NAMESPACE}:`)) {
143
+ throw new Error(`Invalid network: ${requirements.network}`);
144
+ }
145
+ if (!isValidAddress(requirements.payTo)) {
146
+ throw new Error(`Invalid payTo address: ${requirements.payTo}`);
147
+ }
148
+ const amount = BigInt(requirements.amount);
149
+ if (amount <= 0n) {
150
+ throw new Error(`Invalid amount: ${requirements.amount}`);
151
+ }
152
+ }
153
+ };
154
+ function createExactDirectPolkadotClient(config) {
155
+ return new ExactDirectPolkadotClient(config);
156
+ }
157
+ export {
158
+ ExactDirectPolkadotClient,
159
+ createExactDirectPolkadotClient
160
+ };
161
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/constants.ts","../../../src/tokens.ts","../../../src/utils.ts","../../../src/exact-direct/client/scheme.ts"],"sourcesContent":["/**\n * Polkadot Asset Hub T402 Constants\n *\n * Polkadot Asset Hub (formerly Statemint) is a common-good parachain\n * that hosts assets like USDT on the Polkadot network.\n */\n\n// CAIP-2 namespace for Polkadot\nexport const POLKADOT_CAIP2_NAMESPACE = \"polkadot\";\n\n// CAIP-2 network identifiers (first 32 chars of genesis hash)\n// Polkadot Asset Hub (Parachain ID: 1000)\nexport const POLKADOT_ASSET_HUB_CAIP2 = \"polkadot:68d56f15f85d3136970ec16946040bc1\";\n\n// Kusama Asset Hub (Parachain ID: 1000 on Kusama)\nexport const KUSAMA_ASSET_HUB_CAIP2 = \"polkadot:48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a\";\n\n// Westend Asset Hub (Testnet)\nexport const WESTEND_ASSET_HUB_CAIP2 = \"polkadot:e143f23803ac50e8f6f8e62695d1ce9e\";\n\n// Scheme identifier\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n// Default indexers (Subscan API)\nexport const DEFAULT_POLKADOT_INDEXER = \"https://assethub-polkadot.api.subscan.io\";\nexport const DEFAULT_KUSAMA_INDEXER = \"https://assethub-kusama.api.subscan.io\";\nexport const DEFAULT_WESTEND_INDEXER = \"https://assethub-westend.api.subscan.io\";\n\n// Default RPC endpoints\nexport const DEFAULT_POLKADOT_RPC = \"wss://polkadot-asset-hub-rpc.polkadot.io\";\nexport const DEFAULT_KUSAMA_RPC = \"wss://kusama-asset-hub-rpc.polkadot.io\";\nexport const DEFAULT_WESTEND_RPC = \"wss://westend-asset-hub-rpc.polkadot.io\";\n\n// Network configurations\nexport interface PolkadotNetworkConfig {\n readonly name: string;\n readonly caip2: string;\n readonly rpcUrl: string;\n readonly indexerUrl: string;\n readonly genesisHash: string;\n readonly ss58Prefix: number;\n readonly isTestnet: boolean;\n}\n\nexport const POLKADOT_NETWORKS: Record<string, PolkadotNetworkConfig> = {\n [POLKADOT_ASSET_HUB_CAIP2]: {\n name: \"Polkadot Asset Hub\",\n caip2: POLKADOT_ASSET_HUB_CAIP2,\n rpcUrl: DEFAULT_POLKADOT_RPC,\n indexerUrl: DEFAULT_POLKADOT_INDEXER,\n genesisHash: \"0x68d56f15f85d3136970ec16946040bc1752654e906147f7e43e9d539d7c3de2f\",\n ss58Prefix: 0, // Polkadot\n isTestnet: false,\n },\n [KUSAMA_ASSET_HUB_CAIP2]: {\n name: \"Kusama Asset Hub\",\n caip2: KUSAMA_ASSET_HUB_CAIP2,\n rpcUrl: DEFAULT_KUSAMA_RPC,\n indexerUrl: DEFAULT_KUSAMA_INDEXER,\n genesisHash: \"0x48239ef607d7928874027a43a67689209727dfb3d3dc5e5b03a39bdc2eda771a\",\n ss58Prefix: 2, // Kusama\n isTestnet: false,\n },\n [WESTEND_ASSET_HUB_CAIP2]: {\n name: \"Westend Asset Hub\",\n caip2: WESTEND_ASSET_HUB_CAIP2,\n rpcUrl: DEFAULT_WESTEND_RPC,\n indexerUrl: DEFAULT_WESTEND_INDEXER,\n genesisHash: \"0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e\",\n ss58Prefix: 42, // Generic Substrate\n isTestnet: true,\n },\n};\n\n/**\n * Get network configuration by CAIP-2 identifier\n */\nexport function getNetworkConfig(network: string): PolkadotNetworkConfig | undefined {\n return POLKADOT_NETWORKS[network];\n}\n\n/**\n * Check if a network identifier is a Polkadot network\n */\nexport function isPolkadotNetwork(network: string): boolean {\n return network.startsWith(`${POLKADOT_CAIP2_NAMESPACE}:`);\n}\n","/**\n * Polkadot Asset Hub Token Registry\n *\n * On Polkadot Asset Hub, tokens are identified by Asset IDs.\n * USDT is Asset ID 1984, created by Tether.\n */\n\nimport {\n POLKADOT_ASSET_HUB_CAIP2,\n KUSAMA_ASSET_HUB_CAIP2,\n WESTEND_ASSET_HUB_CAIP2,\n} from \"./constants.js\";\n\n/**\n * Token configuration for Polkadot Asset Hub\n */\nexport interface TokenConfig {\n /** Asset ID on Asset Hub */\n readonly assetId: number;\n /** Token symbol */\n readonly symbol: string;\n /** Token name */\n readonly name: string;\n /** Decimal places */\n readonly decimals: number;\n /** Issuer (creator of the asset) */\n readonly issuer?: string;\n}\n\n/**\n * USDT on Polkadot Asset Hub\n * Asset ID: 1984\n * Decimals: 6\n */\nexport const USDT_POLKADOT: TokenConfig = {\n assetId: 1984,\n symbol: \"USDT\",\n name: \"Tether USD\",\n decimals: 6,\n issuer: \"Tether\",\n};\n\n/**\n * USDT on Kusama Asset Hub\n * Asset ID: 1984 (same as Polkadot)\n */\nexport const USDT_KUSAMA: TokenConfig = {\n assetId: 1984,\n symbol: \"USDT\",\n name: \"Tether USD\",\n decimals: 6,\n issuer: \"Tether\",\n};\n\n/**\n * Test USDT on Westend Asset Hub (testnet)\n */\nexport const USDT_WESTEND: TokenConfig = {\n assetId: 1984,\n symbol: \"USDT\",\n name: \"Test Tether USD\",\n decimals: 6,\n};\n\n/**\n * Network-specific token registries\n */\nexport const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {\n [POLKADOT_ASSET_HUB_CAIP2]: {\n USDT: USDT_POLKADOT,\n },\n [KUSAMA_ASSET_HUB_CAIP2]: {\n USDT: USDT_KUSAMA,\n },\n [WESTEND_ASSET_HUB_CAIP2]: {\n USDT: USDT_WESTEND,\n },\n};\n\n/**\n * Default tokens per network\n */\nexport const DEFAULT_TOKENS: Record<string, TokenConfig> = {\n [POLKADOT_ASSET_HUB_CAIP2]: USDT_POLKADOT,\n [KUSAMA_ASSET_HUB_CAIP2]: USDT_KUSAMA,\n [WESTEND_ASSET_HUB_CAIP2]: USDT_WESTEND,\n};\n\n/**\n * Get token configuration by network and symbol\n */\nexport function getTokenConfig(\n network: string,\n symbol: string = \"USDT\",\n): TokenConfig | undefined {\n return TOKEN_REGISTRY[network]?.[symbol];\n}\n\n/**\n * Get the default token for a network\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n return DEFAULT_TOKENS[network];\n}\n\n/**\n * Get asset ID for a token on a network\n */\nexport function getAssetId(network: string, symbol: string = \"USDT\"): number | undefined {\n return getTokenConfig(network, symbol)?.assetId;\n}\n","/**\n * Polkadot Asset Hub Utility Functions\n */\n\nimport type { PolkadotExtrinsicResult, ParsedAssetTransfer } from \"./types.js\";\n\n/**\n * Validate a Polkadot SS58 address format\n * SS58 addresses are base58-encoded with a checksum\n */\nexport function isValidAddress(address: string): boolean {\n if (!address || typeof address !== \"string\") {\n return false;\n }\n\n // SS58 addresses typically start with 1 (Polkadot), or have other prefixes\n // Length is typically 47-48 characters for Polkadot addresses\n // For a simple validation, check base58 characters and length\n const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{45,50}$/;\n return base58Regex.test(address);\n}\n\n/**\n * Validate an extrinsic hash format\n * Extrinsic hashes are 32-byte hex strings prefixed with 0x\n */\nexport function isValidExtrinsicHash(hash: string): boolean {\n if (!hash || typeof hash !== \"string\") {\n return false;\n }\n return /^0x[a-fA-F0-9]{64}$/.test(hash);\n}\n\n/**\n * Validate a block hash format\n */\nexport function isValidBlockHash(hash: string): boolean {\n return isValidExtrinsicHash(hash); // Same format\n}\n\n/**\n * Compare two SS58 addresses (case-sensitive)\n */\nexport function compareAddresses(addr1: string, addr2: string): boolean {\n return addr1 === addr2;\n}\n\n/**\n * Format an amount with decimals for display\n */\nexport function formatAmount(amount: string, decimals: number): string {\n const amountBigInt = BigInt(amount);\n const divisor = BigInt(10 ** decimals);\n const wholePart = amountBigInt / divisor;\n const fractionalPart = amountBigInt % divisor;\n\n if (fractionalPart === 0n) {\n return wholePart.toString();\n }\n\n const fractionalStr = fractionalPart.toString().padStart(decimals, \"0\");\n const trimmedFractional = fractionalStr.replace(/0+$/, \"\");\n return `${wholePart}.${trimmedFractional}`;\n}\n\n/**\n * Parse an amount string to the smallest unit (with decimals applied)\n */\nexport function parseAmount(amount: string, decimals: number): string {\n const parts = amount.split(\".\");\n const wholePart = parts[0] || \"0\";\n const fractionalPart = (parts[1] || \"\").padEnd(decimals, \"0\").slice(0, decimals);\n return BigInt(wholePart + fractionalPart).toString();\n}\n\n/**\n * Extract asset transfer details from an extrinsic result\n */\nexport function extractAssetTransfer(\n result: PolkadotExtrinsicResult,\n): ParsedAssetTransfer | null {\n if (!result.success) {\n return null;\n }\n\n // Check if this is an assets.transfer or assets.transferKeepAlive call\n if (result.module !== \"assets\") {\n return null;\n }\n\n if (result.call !== \"transfer\" && result.call !== \"transferKeepAlive\") {\n return null;\n }\n\n // Extract transfer details from args\n const assetId = result.args.id as number | undefined;\n const to = result.args.target as string | undefined;\n const amount = result.args.amount as string | undefined;\n\n if (assetId === undefined || !to || !amount) {\n return null;\n }\n\n return {\n assetId,\n from: result.signer,\n to,\n amount: amount.toString(),\n success: true,\n };\n}\n\n/**\n * Extract asset transfer from events (alternative method)\n */\nexport function extractAssetTransferFromEvents(\n result: PolkadotExtrinsicResult,\n): ParsedAssetTransfer | null {\n if (!result.success) {\n return null;\n }\n\n // Look for assets.Transferred event\n const transferEvent = result.events.find(\n (e) => e.module === \"assets\" && e.name === \"Transferred\",\n );\n\n if (!transferEvent) {\n return null;\n }\n\n const assetId = transferEvent.data.assetId as number | undefined;\n const from = transferEvent.data.from as string | undefined;\n const to = transferEvent.data.to as string | undefined;\n const amount = transferEvent.data.amount as string | undefined;\n\n if (assetId === undefined || !from || !to || !amount) {\n return null;\n }\n\n return {\n assetId,\n from,\n to,\n amount: amount.toString(),\n success: true,\n };\n}\n\n/**\n * Build a unique extrinsic identifier from block hash and index\n */\nexport function buildExtrinsicId(blockHash: string, extrinsicIndex: number): string {\n return `${blockHash}-${extrinsicIndex}`;\n}\n\n/**\n * Parse an extrinsic identifier back to components\n */\nexport function parseExtrinsicId(\n extrinsicId: string,\n): { blockHash: string; extrinsicIndex: number } | null {\n const lastDashIndex = extrinsicId.lastIndexOf(\"-\");\n if (lastDashIndex === -1) {\n return null;\n }\n\n const blockHash = extrinsicId.slice(0, lastDashIndex);\n const extrinsicIndex = parseInt(extrinsicId.slice(lastDashIndex + 1), 10);\n\n if (!isValidBlockHash(blockHash) || isNaN(extrinsicIndex)) {\n return null;\n }\n\n return { blockHash, extrinsicIndex };\n}\n","/**\n * Polkadot Exact-Direct Client Scheme\n *\n * In the exact-direct scheme, the client executes the asset transfer directly\n * and provides the extrinsic hash as proof of payment.\n */\n\nimport type {\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkClient,\n} from \"@t402/core/types\";\nimport type { ClientPolkadotSigner, ExactDirectPolkadotPayload } from \"../../types.js\";\nimport { SCHEME_EXACT_DIRECT, POLKADOT_CAIP2_NAMESPACE } from \"../../constants.js\";\nimport { getAssetId } from \"../../tokens.js\";\nimport { isValidAddress } from \"../../utils.js\";\n\n/**\n * Configuration for the exact-direct client\n */\nexport interface ExactDirectPolkadotClientConfig {\n /** Signer for executing transactions */\n signer: ClientPolkadotSigner;\n}\n\n/**\n * Exact-direct client scheme for Polkadot Asset Hub\n */\nexport class ExactDirectPolkadotClient implements SchemeNetworkClient {\n readonly scheme = SCHEME_EXACT_DIRECT;\n private readonly signer: ClientPolkadotSigner;\n\n constructor(config: ExactDirectPolkadotClientConfig) {\n this.signer = config.signer;\n }\n\n /**\n * Create a payment payload by executing the transfer\n */\n async createPaymentPayload(\n t402Version: number,\n requirements: PaymentRequirements,\n ): Promise<Pick<PaymentPayload, \"t402Version\" | \"payload\">> {\n // Validate requirements\n this.validateRequirements(requirements);\n\n const { network, amount, payTo, extra } = requirements;\n\n // Get asset ID from extra or use default USDT\n const symbol = (extra?.assetSymbol as string) || \"USDT\";\n const assetId = (extra?.assetId as number) ?? getAssetId(network, symbol);\n\n if (assetId === undefined) {\n throw new Error(`Unknown asset ${symbol} on network ${network}`);\n }\n\n // Get sender address\n const from = await this.signer.getAddress();\n\n // Execute the transfer\n const { extrinsicHash, blockHash, extrinsicIndex } =\n await this.signer.transferAsset(assetId, payTo, amount);\n\n // Build the payload\n const polkadotPayload: ExactDirectPolkadotPayload = {\n extrinsicHash,\n blockHash,\n extrinsicIndex,\n from,\n to: payTo,\n amount,\n assetId,\n };\n\n return {\n t402Version,\n payload: polkadotPayload,\n };\n }\n\n /**\n * Validate payment requirements\n */\n private validateRequirements(requirements: PaymentRequirements): void {\n // Check scheme\n if (requirements.scheme !== SCHEME_EXACT_DIRECT) {\n throw new Error(\n `Invalid scheme: expected ${SCHEME_EXACT_DIRECT}, got ${requirements.scheme}`,\n );\n }\n\n // Check network\n if (!requirements.network.startsWith(`${POLKADOT_CAIP2_NAMESPACE}:`)) {\n throw new Error(`Invalid network: ${requirements.network}`);\n }\n\n // Check payTo address\n if (!isValidAddress(requirements.payTo)) {\n throw new Error(`Invalid payTo address: ${requirements.payTo}`);\n }\n\n // Check amount\n const amount = BigInt(requirements.amount);\n if (amount <= 0n) {\n throw new Error(`Invalid amount: ${requirements.amount}`);\n }\n }\n}\n\n/**\n * Create an exact-direct client for Polkadot\n */\nexport function createExactDirectPolkadotClient(\n config: ExactDirectPolkadotClientConfig,\n): ExactDirectPolkadotClient {\n return new ExactDirectPolkadotClient(config);\n}\n"],"mappings":"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","names":[]}