@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.
- package/README.md +139 -0
- package/dist/exact-direct/client/index.cjs +189 -0
- package/dist/exact-direct/client/index.cjs.map +1 -0
- package/dist/exact-direct/client/index.d.cts +39 -0
- package/dist/exact-direct/client/index.d.ts +39 -0
- package/dist/exact-direct/client/index.mjs +161 -0
- package/dist/exact-direct/client/index.mjs.map +1 -0
- package/dist/exact-direct/facilitator/index.cjs +394 -0
- package/dist/exact-direct/facilitator/index.cjs.map +1 -0
- package/dist/exact-direct/facilitator/index.d.cts +55 -0
- package/dist/exact-direct/facilitator/index.d.ts +55 -0
- package/dist/exact-direct/facilitator/index.mjs +366 -0
- package/dist/exact-direct/facilitator/index.mjs.map +1 -0
- package/dist/exact-direct/server/index.cjs +277 -0
- package/dist/exact-direct/server/index.cjs.map +1 -0
- package/dist/exact-direct/server/index.d.cts +109 -0
- package/dist/exact-direct/server/index.d.ts +109 -0
- package/dist/exact-direct/server/index.mjs +248 -0
- package/dist/exact-direct/server/index.mjs.map +1 -0
- package/dist/index.cjs +293 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +148 -0
- package/dist/index.d.ts +148 -0
- package/dist/index.mjs +235 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types-Dbjfcz2Y.d.cts +135 -0
- package/dist/types-Dbjfcz2Y.d.ts +135 -0
- package/package.json +103 -0
- package/src/constants.ts +87 -0
- package/src/exact-direct/client/index.ts +5 -0
- package/src/exact-direct/client/scheme.ts +117 -0
- package/src/exact-direct/facilitator/index.ts +4 -0
- package/src/exact-direct/facilitator/scheme.ts +315 -0
- package/src/exact-direct/server/index.ts +9 -0
- package/src/exact-direct/server/register.ts +57 -0
- package/src/exact-direct/server/scheme.ts +216 -0
- package/src/index.ts +84 -0
- package/src/tokens.ts +111 -0
- package/src/types.ts +151 -0
- package/src/utils.ts +176 -0
package/src/tokens.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polkadot Asset Hub Token Registry
|
|
3
|
+
*
|
|
4
|
+
* On Polkadot Asset Hub, tokens are identified by Asset IDs.
|
|
5
|
+
* USDT is Asset ID 1984, created by Tether.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
POLKADOT_ASSET_HUB_CAIP2,
|
|
10
|
+
KUSAMA_ASSET_HUB_CAIP2,
|
|
11
|
+
WESTEND_ASSET_HUB_CAIP2,
|
|
12
|
+
} from "./constants.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Token configuration for Polkadot Asset Hub
|
|
16
|
+
*/
|
|
17
|
+
export interface TokenConfig {
|
|
18
|
+
/** Asset ID on Asset Hub */
|
|
19
|
+
readonly assetId: number;
|
|
20
|
+
/** Token symbol */
|
|
21
|
+
readonly symbol: string;
|
|
22
|
+
/** Token name */
|
|
23
|
+
readonly name: string;
|
|
24
|
+
/** Decimal places */
|
|
25
|
+
readonly decimals: number;
|
|
26
|
+
/** Issuer (creator of the asset) */
|
|
27
|
+
readonly issuer?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* USDT on Polkadot Asset Hub
|
|
32
|
+
* Asset ID: 1984
|
|
33
|
+
* Decimals: 6
|
|
34
|
+
*/
|
|
35
|
+
export const USDT_POLKADOT: TokenConfig = {
|
|
36
|
+
assetId: 1984,
|
|
37
|
+
symbol: "USDT",
|
|
38
|
+
name: "Tether USD",
|
|
39
|
+
decimals: 6,
|
|
40
|
+
issuer: "Tether",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* USDT on Kusama Asset Hub
|
|
45
|
+
* Asset ID: 1984 (same as Polkadot)
|
|
46
|
+
*/
|
|
47
|
+
export const USDT_KUSAMA: TokenConfig = {
|
|
48
|
+
assetId: 1984,
|
|
49
|
+
symbol: "USDT",
|
|
50
|
+
name: "Tether USD",
|
|
51
|
+
decimals: 6,
|
|
52
|
+
issuer: "Tether",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Test USDT on Westend Asset Hub (testnet)
|
|
57
|
+
*/
|
|
58
|
+
export const USDT_WESTEND: TokenConfig = {
|
|
59
|
+
assetId: 1984,
|
|
60
|
+
symbol: "USDT",
|
|
61
|
+
name: "Test Tether USD",
|
|
62
|
+
decimals: 6,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Network-specific token registries
|
|
67
|
+
*/
|
|
68
|
+
export const TOKEN_REGISTRY: Record<string, Record<string, TokenConfig>> = {
|
|
69
|
+
[POLKADOT_ASSET_HUB_CAIP2]: {
|
|
70
|
+
USDT: USDT_POLKADOT,
|
|
71
|
+
},
|
|
72
|
+
[KUSAMA_ASSET_HUB_CAIP2]: {
|
|
73
|
+
USDT: USDT_KUSAMA,
|
|
74
|
+
},
|
|
75
|
+
[WESTEND_ASSET_HUB_CAIP2]: {
|
|
76
|
+
USDT: USDT_WESTEND,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Default tokens per network
|
|
82
|
+
*/
|
|
83
|
+
export const DEFAULT_TOKENS: Record<string, TokenConfig> = {
|
|
84
|
+
[POLKADOT_ASSET_HUB_CAIP2]: USDT_POLKADOT,
|
|
85
|
+
[KUSAMA_ASSET_HUB_CAIP2]: USDT_KUSAMA,
|
|
86
|
+
[WESTEND_ASSET_HUB_CAIP2]: USDT_WESTEND,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get token configuration by network and symbol
|
|
91
|
+
*/
|
|
92
|
+
export function getTokenConfig(
|
|
93
|
+
network: string,
|
|
94
|
+
symbol: string = "USDT",
|
|
95
|
+
): TokenConfig | undefined {
|
|
96
|
+
return TOKEN_REGISTRY[network]?.[symbol];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get the default token for a network
|
|
101
|
+
*/
|
|
102
|
+
export function getDefaultToken(network: string): TokenConfig | undefined {
|
|
103
|
+
return DEFAULT_TOKENS[network];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get asset ID for a token on a network
|
|
108
|
+
*/
|
|
109
|
+
export function getAssetId(network: string, symbol: string = "USDT"): number | undefined {
|
|
110
|
+
return getTokenConfig(network, symbol)?.assetId;
|
|
111
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polkadot Asset Hub T402 Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Network } from "@t402/core/types";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Payment payload for exact-direct scheme on Polkadot
|
|
9
|
+
*/
|
|
10
|
+
export type ExactDirectPolkadotPayload = {
|
|
11
|
+
/** Extrinsic hash (block hash + extrinsic index) */
|
|
12
|
+
extrinsicHash: string;
|
|
13
|
+
/** Block hash containing the extrinsic */
|
|
14
|
+
blockHash: string;
|
|
15
|
+
/** Extrinsic index within the block */
|
|
16
|
+
extrinsicIndex: number;
|
|
17
|
+
/** Sender address (SS58 format) */
|
|
18
|
+
from: string;
|
|
19
|
+
/** Recipient address (SS58 format) */
|
|
20
|
+
to: string;
|
|
21
|
+
/** Amount in smallest unit (with decimals) */
|
|
22
|
+
amount: string;
|
|
23
|
+
/** Asset ID */
|
|
24
|
+
assetId: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Result of a Polkadot extrinsic query
|
|
29
|
+
*/
|
|
30
|
+
export interface PolkadotExtrinsicResult {
|
|
31
|
+
/** Extrinsic hash */
|
|
32
|
+
extrinsicHash: string;
|
|
33
|
+
/** Block hash */
|
|
34
|
+
blockHash: string;
|
|
35
|
+
/** Block number */
|
|
36
|
+
blockNumber: number;
|
|
37
|
+
/** Extrinsic index */
|
|
38
|
+
extrinsicIndex: number;
|
|
39
|
+
/** Timestamp (ISO 8601) */
|
|
40
|
+
timestamp: string;
|
|
41
|
+
/** Signer address */
|
|
42
|
+
signer: string;
|
|
43
|
+
/** Success status */
|
|
44
|
+
success: boolean;
|
|
45
|
+
/** Module name (e.g., "assets") */
|
|
46
|
+
module: string;
|
|
47
|
+
/** Call name (e.g., "transfer") */
|
|
48
|
+
call: string;
|
|
49
|
+
/** Call arguments */
|
|
50
|
+
args: Record<string, unknown>;
|
|
51
|
+
/** Events emitted by the extrinsic */
|
|
52
|
+
events: PolkadotEvent[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Polkadot event structure
|
|
57
|
+
*/
|
|
58
|
+
export interface PolkadotEvent {
|
|
59
|
+
/** Module name */
|
|
60
|
+
module: string;
|
|
61
|
+
/** Event name */
|
|
62
|
+
name: string;
|
|
63
|
+
/** Event data */
|
|
64
|
+
data: Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Parsed asset transfer from extrinsic
|
|
69
|
+
*/
|
|
70
|
+
export interface ParsedAssetTransfer {
|
|
71
|
+
/** Asset ID */
|
|
72
|
+
assetId: number;
|
|
73
|
+
/** Sender address */
|
|
74
|
+
from: string;
|
|
75
|
+
/** Recipient address */
|
|
76
|
+
to: string;
|
|
77
|
+
/** Amount transferred */
|
|
78
|
+
amount: string;
|
|
79
|
+
/** Whether the transfer was successful */
|
|
80
|
+
success: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Signer interface for Polkadot facilitator
|
|
85
|
+
*/
|
|
86
|
+
export interface FacilitatorPolkadotSigner {
|
|
87
|
+
/**
|
|
88
|
+
* Get the facilitator's addresses for a network
|
|
89
|
+
*/
|
|
90
|
+
getAddresses(network: Network): string[];
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Query an extrinsic by hash
|
|
94
|
+
*/
|
|
95
|
+
queryExtrinsic(
|
|
96
|
+
extrinsicHash: string,
|
|
97
|
+
blockHash?: string,
|
|
98
|
+
extrinsicIndex?: number,
|
|
99
|
+
): Promise<PolkadotExtrinsicResult | null>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get balance of an asset for an address
|
|
103
|
+
*/
|
|
104
|
+
getBalance(assetId: number, address: string): Promise<string>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Client signer interface for signing transactions
|
|
109
|
+
*/
|
|
110
|
+
export interface ClientPolkadotSigner {
|
|
111
|
+
/**
|
|
112
|
+
* Get the signer's address
|
|
113
|
+
*/
|
|
114
|
+
getAddress(): Promise<string>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Sign and submit an asset transfer
|
|
118
|
+
* Returns the extrinsic hash, block hash, and extrinsic index
|
|
119
|
+
*/
|
|
120
|
+
transferAsset(
|
|
121
|
+
assetId: number,
|
|
122
|
+
to: string,
|
|
123
|
+
amount: string,
|
|
124
|
+
): Promise<{
|
|
125
|
+
extrinsicHash: string;
|
|
126
|
+
blockHash: string;
|
|
127
|
+
extrinsicIndex: number;
|
|
128
|
+
}>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Configuration for Polkadot server
|
|
133
|
+
*/
|
|
134
|
+
export interface PolkadotServerConfig {
|
|
135
|
+
/** Custom RPC URL */
|
|
136
|
+
rpcUrl?: string;
|
|
137
|
+
/** Custom indexer URL */
|
|
138
|
+
indexerUrl?: string;
|
|
139
|
+
/** Facilitator addresses per network */
|
|
140
|
+
facilitatorAddresses?: Record<string, string>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Configuration for Polkadot facilitator
|
|
145
|
+
*/
|
|
146
|
+
export interface PolkadotFacilitatorConfig {
|
|
147
|
+
/** Maximum age of extrinsic to accept (in seconds) */
|
|
148
|
+
maxExtrinsicAge?: number;
|
|
149
|
+
/** Duration to cache used extrinsic hashes */
|
|
150
|
+
usedExtrinsicCacheDuration?: number;
|
|
151
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Polkadot Asset Hub Utility Functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { PolkadotExtrinsicResult, ParsedAssetTransfer } from "./types.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validate a Polkadot SS58 address format
|
|
9
|
+
* SS58 addresses are base58-encoded with a checksum
|
|
10
|
+
*/
|
|
11
|
+
export function isValidAddress(address: string): boolean {
|
|
12
|
+
if (!address || typeof address !== "string") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// SS58 addresses typically start with 1 (Polkadot), or have other prefixes
|
|
17
|
+
// Length is typically 47-48 characters for Polkadot addresses
|
|
18
|
+
// For a simple validation, check base58 characters and length
|
|
19
|
+
const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{45,50}$/;
|
|
20
|
+
return base58Regex.test(address);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validate an extrinsic hash format
|
|
25
|
+
* Extrinsic hashes are 32-byte hex strings prefixed with 0x
|
|
26
|
+
*/
|
|
27
|
+
export function isValidExtrinsicHash(hash: string): boolean {
|
|
28
|
+
if (!hash || typeof hash !== "string") {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
return /^0x[a-fA-F0-9]{64}$/.test(hash);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate a block hash format
|
|
36
|
+
*/
|
|
37
|
+
export function isValidBlockHash(hash: string): boolean {
|
|
38
|
+
return isValidExtrinsicHash(hash); // Same format
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Compare two SS58 addresses (case-sensitive)
|
|
43
|
+
*/
|
|
44
|
+
export function compareAddresses(addr1: string, addr2: string): boolean {
|
|
45
|
+
return addr1 === addr2;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Format an amount with decimals for display
|
|
50
|
+
*/
|
|
51
|
+
export function formatAmount(amount: string, decimals: number): string {
|
|
52
|
+
const amountBigInt = BigInt(amount);
|
|
53
|
+
const divisor = BigInt(10 ** decimals);
|
|
54
|
+
const wholePart = amountBigInt / divisor;
|
|
55
|
+
const fractionalPart = amountBigInt % divisor;
|
|
56
|
+
|
|
57
|
+
if (fractionalPart === 0n) {
|
|
58
|
+
return wholePart.toString();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
62
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
63
|
+
return `${wholePart}.${trimmedFractional}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Parse an amount string to the smallest unit (with decimals applied)
|
|
68
|
+
*/
|
|
69
|
+
export function parseAmount(amount: string, decimals: number): string {
|
|
70
|
+
const parts = amount.split(".");
|
|
71
|
+
const wholePart = parts[0] || "0";
|
|
72
|
+
const fractionalPart = (parts[1] || "").padEnd(decimals, "0").slice(0, decimals);
|
|
73
|
+
return BigInt(wholePart + fractionalPart).toString();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Extract asset transfer details from an extrinsic result
|
|
78
|
+
*/
|
|
79
|
+
export function extractAssetTransfer(
|
|
80
|
+
result: PolkadotExtrinsicResult,
|
|
81
|
+
): ParsedAssetTransfer | null {
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check if this is an assets.transfer or assets.transferKeepAlive call
|
|
87
|
+
if (result.module !== "assets") {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (result.call !== "transfer" && result.call !== "transferKeepAlive") {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Extract transfer details from args
|
|
96
|
+
const assetId = result.args.id as number | undefined;
|
|
97
|
+
const to = result.args.target as string | undefined;
|
|
98
|
+
const amount = result.args.amount as string | undefined;
|
|
99
|
+
|
|
100
|
+
if (assetId === undefined || !to || !amount) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
assetId,
|
|
106
|
+
from: result.signer,
|
|
107
|
+
to,
|
|
108
|
+
amount: amount.toString(),
|
|
109
|
+
success: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract asset transfer from events (alternative method)
|
|
115
|
+
*/
|
|
116
|
+
export function extractAssetTransferFromEvents(
|
|
117
|
+
result: PolkadotExtrinsicResult,
|
|
118
|
+
): ParsedAssetTransfer | null {
|
|
119
|
+
if (!result.success) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Look for assets.Transferred event
|
|
124
|
+
const transferEvent = result.events.find(
|
|
125
|
+
(e) => e.module === "assets" && e.name === "Transferred",
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (!transferEvent) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const assetId = transferEvent.data.assetId as number | undefined;
|
|
133
|
+
const from = transferEvent.data.from as string | undefined;
|
|
134
|
+
const to = transferEvent.data.to as string | undefined;
|
|
135
|
+
const amount = transferEvent.data.amount as string | undefined;
|
|
136
|
+
|
|
137
|
+
if (assetId === undefined || !from || !to || !amount) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
assetId,
|
|
143
|
+
from,
|
|
144
|
+
to,
|
|
145
|
+
amount: amount.toString(),
|
|
146
|
+
success: true,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Build a unique extrinsic identifier from block hash and index
|
|
152
|
+
*/
|
|
153
|
+
export function buildExtrinsicId(blockHash: string, extrinsicIndex: number): string {
|
|
154
|
+
return `${blockHash}-${extrinsicIndex}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Parse an extrinsic identifier back to components
|
|
159
|
+
*/
|
|
160
|
+
export function parseExtrinsicId(
|
|
161
|
+
extrinsicId: string,
|
|
162
|
+
): { blockHash: string; extrinsicIndex: number } | null {
|
|
163
|
+
const lastDashIndex = extrinsicId.lastIndexOf("-");
|
|
164
|
+
if (lastDashIndex === -1) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const blockHash = extrinsicId.slice(0, lastDashIndex);
|
|
169
|
+
const extrinsicIndex = parseInt(extrinsicId.slice(lastDashIndex + 1), 10);
|
|
170
|
+
|
|
171
|
+
if (!isValidBlockHash(blockHash) || isNaN(extrinsicIndex)) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return { blockHash, extrinsicIndex };
|
|
176
|
+
}
|