@t402/cosmos 2.4.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 (38) hide show
  1. package/README.md +175 -0
  2. package/dist/cjs/exact-direct/client/index.d.ts +50 -0
  3. package/dist/cjs/exact-direct/client/index.js +122 -0
  4. package/dist/cjs/exact-direct/client/index.js.map +1 -0
  5. package/dist/cjs/exact-direct/facilitator/index.d.ts +70 -0
  6. package/dist/cjs/exact-direct/facilitator/index.js +300 -0
  7. package/dist/cjs/exact-direct/facilitator/index.js.map +1 -0
  8. package/dist/cjs/exact-direct/server/index.d.ts +87 -0
  9. package/dist/cjs/exact-direct/server/index.js +241 -0
  10. package/dist/cjs/exact-direct/server/index.js.map +1 -0
  11. package/dist/cjs/index.d.ts +178 -0
  12. package/dist/cjs/index.js +619 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/types-CgseoctH.d.ts +155 -0
  15. package/dist/esm/chunk-2UTEGIZ2.mjs +101 -0
  16. package/dist/esm/chunk-2UTEGIZ2.mjs.map +1 -0
  17. package/dist/esm/chunk-57DBLYIR.mjs +58 -0
  18. package/dist/esm/chunk-57DBLYIR.mjs.map +1 -0
  19. package/dist/esm/chunk-JV6LXL2U.mjs +158 -0
  20. package/dist/esm/chunk-JV6LXL2U.mjs.map +1 -0
  21. package/dist/esm/chunk-QP5VO3IO.mjs +68 -0
  22. package/dist/esm/chunk-QP5VO3IO.mjs.map +1 -0
  23. package/dist/esm/chunk-XUMAS5DJ.mjs +223 -0
  24. package/dist/esm/chunk-XUMAS5DJ.mjs.map +1 -0
  25. package/dist/esm/exact-direct/client/index.d.mts +50 -0
  26. package/dist/esm/exact-direct/client/index.mjs +8 -0
  27. package/dist/esm/exact-direct/client/index.mjs.map +1 -0
  28. package/dist/esm/exact-direct/facilitator/index.d.mts +70 -0
  29. package/dist/esm/exact-direct/facilitator/index.mjs +9 -0
  30. package/dist/esm/exact-direct/facilitator/index.mjs.map +1 -0
  31. package/dist/esm/exact-direct/server/index.d.mts +87 -0
  32. package/dist/esm/exact-direct/server/index.mjs +9 -0
  33. package/dist/esm/exact-direct/server/index.mjs.map +1 -0
  34. package/dist/esm/index.d.mts +178 -0
  35. package/dist/esm/index.mjs +83 -0
  36. package/dist/esm/index.mjs.map +1 -0
  37. package/dist/esm/types-CgseoctH.d.mts +155 -0
  38. package/package.json +96 -0
@@ -0,0 +1,300 @@
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/facilitator/index.ts
21
+ var facilitator_exports = {};
22
+ __export(facilitator_exports, {
23
+ ExactDirectCosmosFacilitator: () => ExactDirectCosmosFacilitator
24
+ });
25
+ module.exports = __toCommonJS(facilitator_exports);
26
+
27
+ // src/constants.ts
28
+ var NOBLE_MAINNET_CAIP2 = "cosmos:noble-1";
29
+ var NOBLE_TESTNET_CAIP2 = "cosmos:grand-1";
30
+ var NOBLE_MAINNET_RPC = "https://noble-rpc.polkachu.com";
31
+ var NOBLE_TESTNET_RPC = "https://rpc.testnet.noble.strange.love";
32
+ var NOBLE_MAINNET_REST = "https://noble-api.polkachu.com";
33
+ var NOBLE_TESTNET_REST = "https://api.testnet.noble.strange.love";
34
+ var NETWORK_RPC_ENDPOINTS = {
35
+ [NOBLE_MAINNET_CAIP2]: NOBLE_MAINNET_RPC,
36
+ [NOBLE_TESTNET_CAIP2]: NOBLE_TESTNET_RPC
37
+ };
38
+ var NETWORK_REST_ENDPOINTS = {
39
+ [NOBLE_MAINNET_CAIP2]: NOBLE_MAINNET_REST,
40
+ [NOBLE_TESTNET_CAIP2]: NOBLE_TESTNET_REST
41
+ };
42
+ var NOBLE_BECH32_PREFIX = "noble";
43
+ var USDC_DENOM = "uusdc";
44
+ var MSG_TYPE_SEND = "/cosmos.bank.v1beta1.MsgSend";
45
+ var SCHEME_EXACT_DIRECT = "exact-direct";
46
+ var MAX_TRANSACTION_AGE = 5 * 60 * 1e3;
47
+ var COSMOS_CAIP2_NAMESPACE = "cosmos";
48
+
49
+ // src/utils.ts
50
+ function normalizeNetwork(network) {
51
+ if (network.startsWith(`${COSMOS_CAIP2_NAMESPACE}:`)) {
52
+ return network;
53
+ }
54
+ return `${COSMOS_CAIP2_NAMESPACE}:${network}`;
55
+ }
56
+ function isValidAddress(address) {
57
+ if (!address || address.length < 10) {
58
+ return false;
59
+ }
60
+ return address.startsWith(`${NOBLE_BECH32_PREFIX}1`);
61
+ }
62
+
63
+ // src/tokens.ts
64
+ var TOKEN_REGISTRY = {
65
+ [NOBLE_MAINNET_CAIP2]: [
66
+ {
67
+ denom: USDC_DENOM,
68
+ symbol: "USDC",
69
+ name: "USD Coin",
70
+ decimals: 6,
71
+ priority: 1
72
+ }
73
+ ],
74
+ [NOBLE_TESTNET_CAIP2]: [
75
+ {
76
+ denom: USDC_DENOM,
77
+ symbol: "USDC",
78
+ name: "USD Coin (Testnet)",
79
+ decimals: 6,
80
+ priority: 1
81
+ }
82
+ ]
83
+ };
84
+ function getDefaultToken(network) {
85
+ const tokens = TOKEN_REGISTRY[network];
86
+ if (!tokens || tokens.length === 0) return void 0;
87
+ return [...tokens].sort((a, b) => a.priority - b.priority)[0];
88
+ }
89
+
90
+ // src/exact-direct/facilitator/scheme.ts
91
+ var ExactDirectCosmosFacilitator = class {
92
+ constructor(signer, config) {
93
+ this.signer = signer;
94
+ this.scheme = SCHEME_EXACT_DIRECT;
95
+ this.caipFamily = `${COSMOS_CAIP2_NAMESPACE}:*`;
96
+ this.usedTxs = /* @__PURE__ */ new Map();
97
+ this.config = {
98
+ maxTransactionAge: config?.maxTransactionAge ?? MAX_TRANSACTION_AGE,
99
+ usedTxCacheDuration: config?.usedTxCacheDuration ?? 24 * 60 * 60 * 1e3
100
+ // 24 hours
101
+ };
102
+ this.startCleanupInterval();
103
+ }
104
+ /**
105
+ * Get extra data for a supported kind
106
+ */
107
+ getExtra(network) {
108
+ const token = getDefaultToken(network);
109
+ if (!token) {
110
+ return void 0;
111
+ }
112
+ return {
113
+ assetSymbol: token.symbol,
114
+ assetDecimals: token.decimals,
115
+ assetDenom: token.denom
116
+ };
117
+ }
118
+ /**
119
+ * Get signer addresses for a network
120
+ */
121
+ getSigners(network) {
122
+ return this.signer.getAddresses(network);
123
+ }
124
+ /**
125
+ * Verify a payment payload
126
+ */
127
+ async verify(payload, requirements) {
128
+ const network = normalizeNetwork(requirements.network);
129
+ if (payload.accepted.scheme !== SCHEME_EXACT_DIRECT) {
130
+ return {
131
+ isValid: false,
132
+ invalidReason: "invalid_scheme"
133
+ };
134
+ }
135
+ if (normalizeNetwork(payload.accepted.network) !== network) {
136
+ return {
137
+ isValid: false,
138
+ invalidReason: "network_mismatch"
139
+ };
140
+ }
141
+ const cosmosPayload = payload.payload;
142
+ if (!cosmosPayload.txHash) {
143
+ return {
144
+ isValid: false,
145
+ invalidReason: "missing_tx_hash"
146
+ };
147
+ }
148
+ if (!cosmosPayload.from || !isValidAddress(cosmosPayload.from)) {
149
+ return {
150
+ isValid: false,
151
+ invalidReason: "invalid_from_address"
152
+ };
153
+ }
154
+ if (this.isTxUsed(cosmosPayload.txHash)) {
155
+ return {
156
+ isValid: false,
157
+ invalidReason: "transaction_already_used",
158
+ payer: cosmosPayload.from
159
+ };
160
+ }
161
+ try {
162
+ const tx = await this.signer.queryTransaction(network, cosmosPayload.txHash);
163
+ if (tx.code !== 0) {
164
+ return {
165
+ isValid: false,
166
+ invalidReason: "transaction_failed",
167
+ payer: cosmosPayload.from
168
+ };
169
+ }
170
+ const msgSend = this.findMsgSend(tx.tx.body.messages);
171
+ if (!msgSend) {
172
+ return {
173
+ isValid: false,
174
+ invalidReason: "no_msg_send_found",
175
+ payer: cosmosPayload.from
176
+ };
177
+ }
178
+ if (msgSend.toAddress !== requirements.payTo) {
179
+ return {
180
+ isValid: false,
181
+ invalidReason: "wrong_recipient",
182
+ payer: cosmosPayload.from
183
+ };
184
+ }
185
+ if (msgSend.fromAddress !== cosmosPayload.from) {
186
+ return {
187
+ isValid: false,
188
+ invalidReason: "sender_mismatch",
189
+ payer: cosmosPayload.from
190
+ };
191
+ }
192
+ const expectedDenom = requirements.extra?.denom || requirements.asset;
193
+ const coin = this.getAmountByDenom(msgSend, expectedDenom);
194
+ if (!coin) {
195
+ return {
196
+ isValid: false,
197
+ invalidReason: "wrong_denomination",
198
+ payer: cosmosPayload.from
199
+ };
200
+ }
201
+ const txAmount = BigInt(coin.amount);
202
+ const requiredAmount = BigInt(requirements.amount);
203
+ if (txAmount < requiredAmount) {
204
+ return {
205
+ isValid: false,
206
+ invalidReason: "insufficient_amount",
207
+ payer: cosmosPayload.from
208
+ };
209
+ }
210
+ this.markTxUsed(cosmosPayload.txHash);
211
+ return {
212
+ isValid: true,
213
+ payer: cosmosPayload.from
214
+ };
215
+ } catch {
216
+ return {
217
+ isValid: false,
218
+ invalidReason: "transaction_not_found",
219
+ payer: cosmosPayload.from
220
+ };
221
+ }
222
+ }
223
+ /**
224
+ * Settle a payment - for exact-direct, the transfer is already complete
225
+ */
226
+ async settle(payload, requirements) {
227
+ const verifyResult = await this.verify(payload, requirements);
228
+ if (!verifyResult.isValid) {
229
+ return {
230
+ success: false,
231
+ errorReason: verifyResult.invalidReason || "verification_failed",
232
+ payer: verifyResult.payer,
233
+ transaction: "",
234
+ network: normalizeNetwork(requirements.network)
235
+ };
236
+ }
237
+ const cosmosPayload = payload.payload;
238
+ return {
239
+ success: true,
240
+ transaction: cosmosPayload.txHash,
241
+ network: normalizeNetwork(requirements.network),
242
+ payer: cosmosPayload.from
243
+ };
244
+ }
245
+ /**
246
+ * Find a MsgSend in transaction messages
247
+ */
248
+ findMsgSend(messages) {
249
+ for (const msg of messages) {
250
+ if (msg["@type"] === MSG_TYPE_SEND || !msg["@type"] && msg.fromAddress && msg.toAddress) {
251
+ return msg;
252
+ }
253
+ }
254
+ return null;
255
+ }
256
+ /**
257
+ * Get a specific coin amount from a MsgSend by denomination
258
+ */
259
+ getAmountByDenom(msg, denom) {
260
+ for (const coin of msg.amount) {
261
+ if (coin.denom === denom) {
262
+ return coin;
263
+ }
264
+ }
265
+ return null;
266
+ }
267
+ /**
268
+ * Check if a transaction has been used
269
+ */
270
+ isTxUsed(txHash) {
271
+ return this.usedTxs.has(txHash);
272
+ }
273
+ /**
274
+ * Mark a transaction as used
275
+ */
276
+ markTxUsed(txHash) {
277
+ this.usedTxs.set(txHash, Date.now());
278
+ }
279
+ /**
280
+ * Start the cleanup interval for used transactions
281
+ */
282
+ startCleanupInterval() {
283
+ setInterval(
284
+ () => {
285
+ const cutoff = Date.now() - this.config.usedTxCacheDuration;
286
+ for (const [txHash, usedAt] of this.usedTxs.entries()) {
287
+ if (usedAt < cutoff) {
288
+ this.usedTxs.delete(txHash);
289
+ }
290
+ }
291
+ },
292
+ 60 * 60 * 1e3
293
+ );
294
+ }
295
+ };
296
+ // Annotate the CommonJS export names for ESM import in node:
297
+ 0 && (module.exports = {
298
+ ExactDirectCosmosFacilitator
299
+ });
300
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/exact-direct/facilitator/index.ts","../../../../src/constants.ts","../../../../src/utils.ts","../../../../src/tokens.ts","../../../../src/exact-direct/facilitator/scheme.ts"],"sourcesContent":["/**\n * Cosmos Exact-Direct Facilitator Exports\n */\n\nexport { ExactDirectCosmosFacilitator, type ExactDirectCosmosFacilitatorConfig } from \"./scheme.js\";\n","/**\n * Cosmos Network Constants\n *\n * This module provides constants for Cosmos blockchain integration including:\n * - CAIP-2 network identifiers (Noble chain)\n * - RPC and REST endpoints\n * - Bech32 prefix configuration\n * - USDC denomination and gas parameters\n */\n\n/**\n * CAIP-2 Network Identifiers for Cosmos (Noble)\n */\nexport const NOBLE_MAINNET_CAIP2 = \"cosmos:noble-1\";\nexport const NOBLE_TESTNET_CAIP2 = \"cosmos:grand-1\";\n\n/**\n * Supported Cosmos networks\n */\nexport const COSMOS_NETWORKS = [NOBLE_MAINNET_CAIP2, NOBLE_TESTNET_CAIP2] as const;\n\nexport type CosmosNetwork = (typeof COSMOS_NETWORKS)[number];\n\n/**\n * Default RPC endpoints\n */\nexport const NOBLE_MAINNET_RPC = \"https://noble-rpc.polkachu.com\";\nexport const NOBLE_TESTNET_RPC = \"https://rpc.testnet.noble.strange.love\";\n\n/**\n * Default REST endpoints\n */\nexport const NOBLE_MAINNET_REST = \"https://noble-api.polkachu.com\";\nexport const NOBLE_TESTNET_REST = \"https://api.testnet.noble.strange.love\";\n\n/**\n * Network RPC endpoint mapping\n */\nexport const NETWORK_RPC_ENDPOINTS: Record<string, string> = {\n [NOBLE_MAINNET_CAIP2]: NOBLE_MAINNET_RPC,\n [NOBLE_TESTNET_CAIP2]: NOBLE_TESTNET_RPC,\n};\n\n/**\n * Network REST endpoint mapping\n */\nexport const NETWORK_REST_ENDPOINTS: Record<string, string> = {\n [NOBLE_MAINNET_CAIP2]: NOBLE_MAINNET_REST,\n [NOBLE_TESTNET_CAIP2]: NOBLE_TESTNET_REST,\n};\n\n/**\n * Bech32 prefix for Noble addresses\n */\nexport const NOBLE_BECH32_PREFIX = \"noble\";\n\n/**\n * USDC denomination on Noble\n * uusdc = micro-USDC (1 USDC = 1,000,000 uusdc)\n */\nexport const USDC_DENOM = \"uusdc\";\n\n/**\n * Default gas parameters\n */\nexport const DEFAULT_GAS_LIMIT = 200000;\nexport const DEFAULT_GAS_PRICE = \"0.025uusdc\";\nexport const DEFAULT_FEE_AMOUNT = \"5000\"; // 0.005 USDC\n\n/**\n * Cosmos message type for bank send\n */\nexport const MSG_TYPE_SEND = \"/cosmos.bank.v1beta1.MsgSend\";\n\n/**\n * Scheme identifier for exact-direct payments\n */\nexport const SCHEME_EXACT_DIRECT = \"exact-direct\";\n\n/**\n * Maximum transaction age to accept (in milliseconds)\n */\nexport const MAX_TRANSACTION_AGE = 5 * 60 * 1000; // 5 minutes\n\n/**\n * CAIP-2 namespace for Cosmos\n */\nexport const COSMOS_CAIP2_NAMESPACE = \"cosmos\";\n","/**\n * Cosmos Utility Functions\n *\n * Helper functions for Cosmos address validation, network normalization,\n * and amount conversion.\n */\n\nimport {\n COSMOS_CAIP2_NAMESPACE,\n NOBLE_BECH32_PREFIX,\n NETWORK_RPC_ENDPOINTS,\n NETWORK_REST_ENDPOINTS,\n type CosmosNetwork,\n} from \"./constants.js\";\n\n/**\n * Normalize a network identifier to CAIP-2 format\n * @param network - Network identifier (e.g., \"noble-1\", \"cosmos:noble-1\")\n * @returns CAIP-2 formatted network identifier\n */\nexport function normalizeNetwork(network: string): CosmosNetwork {\n // Already in CAIP-2 format\n if (network.startsWith(`${COSMOS_CAIP2_NAMESPACE}:`)) {\n return network as CosmosNetwork;\n }\n // Convert shorthand to CAIP-2\n return `${COSMOS_CAIP2_NAMESPACE}:${network}` as CosmosNetwork;\n}\n\n/**\n * Extract network ID from CAIP-2 identifier\n * @param network - CAIP-2 network identifier\n * @returns Network ID (e.g., \"noble-1\")\n */\nexport function extractNetworkId(network: string): string {\n if (network.includes(\":\")) {\n return network.split(\":\")[1];\n }\n return network;\n}\n\n/**\n * Validate a Cosmos bech32 address with Noble prefix\n * Checks that the address starts with the \"noble\" prefix followed by \"1\"\n * @param address - Address to validate\n * @returns Whether the address is valid\n */\nexport function isValidAddress(address: string): boolean {\n if (!address || address.length < 10) {\n return false;\n }\n return address.startsWith(`${NOBLE_BECH32_PREFIX}1`);\n}\n\n/**\n * Get RPC endpoint for a network\n * @param network - CAIP-2 network identifier\n * @returns RPC endpoint URL\n */\nexport function getRpcEndpoint(network: string): string {\n const normalizedNetwork = normalizeNetwork(network);\n return NETWORK_RPC_ENDPOINTS[normalizedNetwork] || NETWORK_RPC_ENDPOINTS[\"cosmos:noble-1\"];\n}\n\n/**\n * Get REST endpoint for a network\n * @param network - CAIP-2 network identifier\n * @returns REST endpoint URL\n */\nexport function getRestEndpoint(network: string): string {\n const normalizedNetwork = normalizeNetwork(network);\n return NETWORK_REST_ENDPOINTS[normalizedNetwork] || NETWORK_REST_ENDPOINTS[\"cosmos:noble-1\"];\n}\n\n/**\n * Convert decimal amount to token units (atomic units)\n * @param decimalAmount - Amount with decimals (e.g., \"1.50\")\n * @param decimals - Token decimals\n * @returns Amount in smallest units\n */\nexport function toAtomicUnits(decimalAmount: string | number, decimals: number): bigint {\n const amount = typeof decimalAmount === \"string\" ? parseFloat(decimalAmount) : decimalAmount;\n if (isNaN(amount)) {\n throw new Error(`Invalid amount: ${decimalAmount}`);\n }\n const multiplier = 10 ** decimals;\n return BigInt(Math.floor(amount * multiplier));\n}\n\n/**\n * Format amount for display (with decimals)\n * @param amount - Amount in smallest units\n * @param decimals - Number of decimal places\n * @returns Formatted amount string\n */\nexport function formatAmount(amount: bigint, decimals: number): string {\n const divisor = BigInt(10 ** decimals);\n const whole = amount / divisor;\n const remainder = amount % divisor;\n const decimal = remainder.toString().padStart(decimals, \"0\").slice(0, 2);\n return `${whole}.${decimal}`;\n}\n\n/**\n * Get the list of supported networks\n * @returns Array of CAIP-2 network identifiers\n */\nexport function getSupportedNetworks(): string[] {\n return Object.keys(NETWORK_RPC_ENDPOINTS);\n}\n","/**\n * Cosmos Token Registry\n *\n * Defines supported tokens for each Cosmos network (currently Noble USDC).\n */\n\nimport { NOBLE_MAINNET_CAIP2, NOBLE_TESTNET_CAIP2, USDC_DENOM } from \"./constants.js\";\nimport type { TokenConfig } from \"./types.js\";\n\n/**\n * Token registry by network\n */\nexport const TOKEN_REGISTRY: Record<string, TokenConfig[]> = {\n [NOBLE_MAINNET_CAIP2]: [\n {\n denom: USDC_DENOM,\n symbol: \"USDC\",\n name: \"USD Coin\",\n decimals: 6,\n priority: 1,\n },\n ],\n [NOBLE_TESTNET_CAIP2]: [\n {\n denom: USDC_DENOM,\n symbol: \"USDC\",\n name: \"USD Coin (Testnet)\",\n decimals: 6,\n priority: 1,\n },\n ],\n};\n\n/**\n * Get token configuration by symbol\n * @param network - CAIP-2 network identifier\n * @param symbol - Token symbol (e.g., \"USDC\")\n * @returns Token configuration or undefined\n */\nexport function getTokenConfig(network: string, symbol: string): TokenConfig | undefined {\n const tokens = TOKEN_REGISTRY[network];\n if (!tokens) return undefined;\n return tokens.find((t) => t.symbol.toUpperCase() === symbol.toUpperCase());\n}\n\n/**\n * Get token configuration by denomination\n * @param network - CAIP-2 network identifier\n * @param denom - Token denomination (e.g., \"uusdc\")\n * @returns Token configuration or undefined\n */\nexport function getTokenByDenom(network: string, denom: string): TokenConfig | undefined {\n const tokens = TOKEN_REGISTRY[network];\n if (!tokens) return undefined;\n return tokens.find((t) => t.denom === denom);\n}\n\n/**\n * Get the default token for a network\n * Returns the token with highest priority (lowest priority number)\n * @param network - CAIP-2 network identifier\n * @returns Default token configuration or undefined\n */\nexport function getDefaultToken(network: string): TokenConfig | undefined {\n const tokens = TOKEN_REGISTRY[network];\n if (!tokens || tokens.length === 0) return undefined;\n return [...tokens].sort((a, b) => a.priority - b.priority)[0];\n}\n\n/**\n * Get all tokens for a network\n * @param network - CAIP-2 network identifier\n * @returns Array of token configurations\n */\nexport function getNetworkTokens(network: string): TokenConfig[] {\n return TOKEN_REGISTRY[network] || [];\n}\n\n/**\n * Check if a network is supported\n * @param network - CAIP-2 network identifier\n */\nexport function isNetworkSupported(network: string): boolean {\n return network in TOKEN_REGISTRY;\n}\n","/**\n * Cosmos Facilitator Scheme Implementation - Exact Direct\n *\n * Verifies and settles Cosmos (Noble USDC) payments using the exact-direct scheme.\n * The facilitator verifies that the client's transaction was successful\n * and matches the payment requirements.\n */\n\nimport type {\n Network,\n PaymentPayload,\n PaymentRequirements,\n SchemeNetworkFacilitator,\n SettleResponse,\n VerifyResponse,\n} from \"@t402/core/types\";\nimport type { FacilitatorCosmosSigner, ExactDirectCosmosPayload, MsgSend } from \"../../types.js\";\nimport { SCHEME_EXACT_DIRECT, COSMOS_CAIP2_NAMESPACE, MAX_TRANSACTION_AGE, MSG_TYPE_SEND } from \"../../constants.js\";\nimport { normalizeNetwork, isValidAddress } from \"../../utils.js\";\nimport { getDefaultToken } from \"../../tokens.js\";\n\n/**\n * Configuration for ExactDirectCosmosFacilitator\n */\nexport interface ExactDirectCosmosFacilitatorConfig {\n /** Maximum age of a transaction to accept (in milliseconds) */\n maxTransactionAge?: number;\n /** Duration to cache used transaction hashes (in milliseconds) */\n usedTxCacheDuration?: number;\n}\n\n/**\n * Cosmos facilitator implementation for the Exact-Direct payment scheme.\n * Verifies transaction proofs and confirms payments.\n */\nexport class ExactDirectCosmosFacilitator implements SchemeNetworkFacilitator {\n readonly scheme = SCHEME_EXACT_DIRECT;\n readonly caipFamily = `${COSMOS_CAIP2_NAMESPACE}:*`;\n\n private readonly config: Required<ExactDirectCosmosFacilitatorConfig>;\n private usedTxs: Map<string, number> = new Map();\n\n constructor(\n private readonly signer: FacilitatorCosmosSigner,\n config?: ExactDirectCosmosFacilitatorConfig,\n ) {\n this.config = {\n maxTransactionAge: config?.maxTransactionAge ?? MAX_TRANSACTION_AGE,\n usedTxCacheDuration: config?.usedTxCacheDuration ?? 24 * 60 * 60 * 1000, // 24 hours\n };\n\n // Start cleanup interval\n this.startCleanupInterval();\n }\n\n /**\n * Get extra data for a supported kind\n */\n getExtra(network: Network): Record<string, unknown> | undefined {\n const token = getDefaultToken(network);\n if (!token) {\n return undefined;\n }\n return {\n assetSymbol: token.symbol,\n assetDecimals: token.decimals,\n assetDenom: token.denom,\n };\n }\n\n /**\n * Get signer addresses for a network\n */\n getSigners(network: Network): string[] {\n return this.signer.getAddresses(network);\n }\n\n /**\n * Verify a payment payload\n */\n async verify(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<VerifyResponse> {\n const network = normalizeNetwork(requirements.network);\n\n // Validate scheme\n if (payload.accepted.scheme !== SCHEME_EXACT_DIRECT) {\n return {\n isValid: false,\n invalidReason: \"invalid_scheme\",\n };\n }\n\n // Validate network\n if (normalizeNetwork(payload.accepted.network) !== network) {\n return {\n isValid: false,\n invalidReason: \"network_mismatch\",\n };\n }\n\n // Parse payload\n const cosmosPayload = payload.payload as ExactDirectCosmosPayload;\n if (!cosmosPayload.txHash) {\n return {\n isValid: false,\n invalidReason: \"missing_tx_hash\",\n };\n }\n if (!cosmosPayload.from || !isValidAddress(cosmosPayload.from)) {\n return {\n isValid: false,\n invalidReason: \"invalid_from_address\",\n };\n }\n\n // Check for replay attack\n if (this.isTxUsed(cosmosPayload.txHash)) {\n return {\n isValid: false,\n invalidReason: \"transaction_already_used\",\n payer: cosmosPayload.from,\n };\n }\n\n try {\n // Query the transaction\n const tx = await this.signer.queryTransaction(network, cosmosPayload.txHash);\n\n // Check transaction succeeded (code 0 = success)\n if (tx.code !== 0) {\n return {\n isValid: false,\n invalidReason: \"transaction_failed\",\n payer: cosmosPayload.from,\n };\n }\n\n // Find the MsgSend in the transaction messages\n const msgSend = this.findMsgSend(tx.tx.body.messages);\n if (!msgSend) {\n return {\n isValid: false,\n invalidReason: \"no_msg_send_found\",\n payer: cosmosPayload.from,\n };\n }\n\n // Verify recipient\n if (msgSend.toAddress !== requirements.payTo) {\n return {\n isValid: false,\n invalidReason: \"wrong_recipient\",\n payer: cosmosPayload.from,\n };\n }\n\n // Verify sender matches payload\n if (msgSend.fromAddress !== cosmosPayload.from) {\n return {\n isValid: false,\n invalidReason: \"sender_mismatch\",\n payer: cosmosPayload.from,\n };\n }\n\n // Determine expected denom\n const expectedDenom = (requirements.extra?.denom as string) || requirements.asset;\n\n // Verify amount and denomination\n const coin = this.getAmountByDenom(msgSend, expectedDenom);\n if (!coin) {\n return {\n isValid: false,\n invalidReason: \"wrong_denomination\",\n payer: cosmosPayload.from,\n };\n }\n\n const txAmount = BigInt(coin.amount);\n const requiredAmount = BigInt(requirements.amount);\n if (txAmount < requiredAmount) {\n return {\n isValid: false,\n invalidReason: \"insufficient_amount\",\n payer: cosmosPayload.from,\n };\n }\n\n // Mark transaction as used\n this.markTxUsed(cosmosPayload.txHash);\n\n return {\n isValid: true,\n payer: cosmosPayload.from,\n };\n } catch {\n return {\n isValid: false,\n invalidReason: \"transaction_not_found\",\n payer: cosmosPayload.from,\n };\n }\n }\n\n /**\n * Settle a payment - for exact-direct, the transfer is already complete\n */\n async settle(\n payload: PaymentPayload,\n requirements: PaymentRequirements,\n ): Promise<SettleResponse> {\n // Verify first\n const verifyResult = await this.verify(payload, requirements);\n if (!verifyResult.isValid) {\n return {\n success: false,\n errorReason: verifyResult.invalidReason || \"verification_failed\",\n payer: verifyResult.payer,\n transaction: \"\",\n network: normalizeNetwork(requirements.network),\n };\n }\n\n const cosmosPayload = payload.payload as ExactDirectCosmosPayload;\n\n // For exact-direct, settlement is already complete\n return {\n success: true,\n transaction: cosmosPayload.txHash,\n network: normalizeNetwork(requirements.network),\n payer: cosmosPayload.from,\n };\n }\n\n /**\n * Find a MsgSend in transaction messages\n */\n private findMsgSend(messages: MsgSend[]): MsgSend | null {\n for (const msg of messages) {\n if (msg[\"@type\"] === MSG_TYPE_SEND || (!msg[\"@type\"] && msg.fromAddress && msg.toAddress)) {\n return msg;\n }\n }\n return null;\n }\n\n /**\n * Get a specific coin amount from a MsgSend by denomination\n */\n private getAmountByDenom(msg: MsgSend, denom: string): { denom: string; amount: string } | null {\n for (const coin of msg.amount) {\n if (coin.denom === denom) {\n return coin;\n }\n }\n return null;\n }\n\n /**\n * Check if a transaction has been used\n */\n private isTxUsed(txHash: string): boolean {\n return this.usedTxs.has(txHash);\n }\n\n /**\n * Mark a transaction as used\n */\n private markTxUsed(txHash: string): void {\n this.usedTxs.set(txHash, Date.now());\n }\n\n /**\n * Start the cleanup interval for used transactions\n */\n private startCleanupInterval(): void {\n setInterval(\n () => {\n const cutoff = Date.now() - this.config.usedTxCacheDuration;\n for (const [txHash, usedAt] of this.usedTxs.entries()) {\n if (usedAt < cutoff) {\n this.usedTxs.delete(txHash);\n }\n }\n },\n 60 * 60 * 1000,\n ); // Cleanup every hour\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,87 @@
1
+ import { SchemeNetworkServer, MoneyParser, Price, Network, AssetAmount, PaymentRequirements } from '@t402/core/types';
2
+
3
+ /**
4
+ * Cosmos Server Scheme Implementation - Exact Direct
5
+ *
6
+ * Handles price parsing and payment requirement enhancement for
7
+ * Cosmos (Noble USDC) payments using the exact-direct scheme.
8
+ */
9
+
10
+ /**
11
+ * Configuration options for ExactDirectCosmosServer
12
+ */
13
+ interface ExactDirectCosmosServerConfig {
14
+ /** Preferred token symbol (e.g., "USDC"). Defaults to network's highest priority token. */
15
+ preferredToken?: string;
16
+ }
17
+ /**
18
+ * Cosmos server implementation for the Exact-Direct payment scheme.
19
+ * Handles price parsing and converts user-friendly amounts to token amounts.
20
+ */
21
+ declare class ExactDirectCosmosServer implements SchemeNetworkServer {
22
+ readonly scheme = "exact-direct";
23
+ private moneyParsers;
24
+ private config;
25
+ constructor(config?: ExactDirectCosmosServerConfig);
26
+ /**
27
+ * Register a custom money parser in the parser chain.
28
+ * Multiple parsers can be registered - they will be tried in registration order.
29
+ * Each parser receives a decimal amount (e.g., 1.50 for $1.50).
30
+ * If a parser returns null, the next parser in the chain will be tried.
31
+ * The default parser is always the final fallback.
32
+ *
33
+ * @param parser - Custom function to convert amount to AssetAmount (or null to skip)
34
+ * @returns The server instance for chaining
35
+ */
36
+ registerMoneyParser(parser: MoneyParser): ExactDirectCosmosServer;
37
+ /**
38
+ * Parses a price into an asset amount.
39
+ * If price is already an AssetAmount, returns it directly.
40
+ * If price is Money (string | number), parses to decimal and tries custom parsers.
41
+ * Falls back to default conversion if all custom parsers return null.
42
+ *
43
+ * @param price - The price to parse
44
+ * @param network - The network to use
45
+ * @returns Promise that resolves to the parsed asset amount
46
+ */
47
+ parsePrice(price: Price, network: Network): Promise<AssetAmount>;
48
+ /**
49
+ * Build payment requirements for this scheme/network combination.
50
+ *
51
+ * @param paymentRequirements - Base payment requirements with amount/asset already set
52
+ * @param supportedKind - The supported kind from facilitator's /supported endpoint
53
+ * @param extensionKeys - Extensions supported by the facilitator
54
+ * @returns Enhanced payment requirements ready to be sent to clients
55
+ */
56
+ enhancePaymentRequirements(paymentRequirements: PaymentRequirements, supportedKind: {
57
+ t402Version: number;
58
+ scheme: string;
59
+ network: Network;
60
+ extra?: Record<string, unknown>;
61
+ }, extensionKeys: string[]): Promise<PaymentRequirements>;
62
+ /**
63
+ * Parse Money (string | number) to a decimal number.
64
+ * Handles formats like "$1.50", "1.50", 1.50, etc.
65
+ */
66
+ private parseMoneyToDecimal;
67
+ /**
68
+ * Default money conversion implementation.
69
+ * Converts decimal amount to the preferred token on the specified network.
70
+ */
71
+ private defaultMoneyConversion;
72
+ /**
73
+ * Get the default asset info for a network.
74
+ * Priority: configured preferredToken > network default
75
+ */
76
+ private getDefaultAsset;
77
+ /**
78
+ * Get all supported networks
79
+ */
80
+ static getSupportedNetworks(): string[];
81
+ /**
82
+ * Check if a network is supported
83
+ */
84
+ static isNetworkSupported(network: string): boolean;
85
+ }
86
+
87
+ export { ExactDirectCosmosServer, type ExactDirectCosmosServerConfig };
@@ -0,0 +1,241 @@
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/server/index.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ ExactDirectCosmosServer: () => ExactDirectCosmosServer
24
+ });
25
+ module.exports = __toCommonJS(server_exports);
26
+
27
+ // src/constants.ts
28
+ var NOBLE_MAINNET_CAIP2 = "cosmos:noble-1";
29
+ var NOBLE_TESTNET_CAIP2 = "cosmos:grand-1";
30
+ var NOBLE_MAINNET_RPC = "https://noble-rpc.polkachu.com";
31
+ var NOBLE_TESTNET_RPC = "https://rpc.testnet.noble.strange.love";
32
+ var NOBLE_MAINNET_REST = "https://noble-api.polkachu.com";
33
+ var NOBLE_TESTNET_REST = "https://api.testnet.noble.strange.love";
34
+ var NETWORK_RPC_ENDPOINTS = {
35
+ [NOBLE_MAINNET_CAIP2]: NOBLE_MAINNET_RPC,
36
+ [NOBLE_TESTNET_CAIP2]: NOBLE_TESTNET_RPC
37
+ };
38
+ var NETWORK_REST_ENDPOINTS = {
39
+ [NOBLE_MAINNET_CAIP2]: NOBLE_MAINNET_REST,
40
+ [NOBLE_TESTNET_CAIP2]: NOBLE_TESTNET_REST
41
+ };
42
+ var NOBLE_BECH32_PREFIX = "noble";
43
+ var USDC_DENOM = "uusdc";
44
+ var SCHEME_EXACT_DIRECT = "exact-direct";
45
+ var MAX_TRANSACTION_AGE = 5 * 60 * 1e3;
46
+ var COSMOS_CAIP2_NAMESPACE = "cosmos";
47
+
48
+ // src/tokens.ts
49
+ var TOKEN_REGISTRY = {
50
+ [NOBLE_MAINNET_CAIP2]: [
51
+ {
52
+ denom: USDC_DENOM,
53
+ symbol: "USDC",
54
+ name: "USD Coin",
55
+ decimals: 6,
56
+ priority: 1
57
+ }
58
+ ],
59
+ [NOBLE_TESTNET_CAIP2]: [
60
+ {
61
+ denom: USDC_DENOM,
62
+ symbol: "USDC",
63
+ name: "USD Coin (Testnet)",
64
+ decimals: 6,
65
+ priority: 1
66
+ }
67
+ ]
68
+ };
69
+ function getTokenConfig(network, symbol) {
70
+ const tokens = TOKEN_REGISTRY[network];
71
+ if (!tokens) return void 0;
72
+ return tokens.find((t) => t.symbol.toUpperCase() === symbol.toUpperCase());
73
+ }
74
+ function getDefaultToken(network) {
75
+ const tokens = TOKEN_REGISTRY[network];
76
+ if (!tokens || tokens.length === 0) return void 0;
77
+ return [...tokens].sort((a, b) => a.priority - b.priority)[0];
78
+ }
79
+
80
+ // src/utils.ts
81
+ function normalizeNetwork(network) {
82
+ if (network.startsWith(`${COSMOS_CAIP2_NAMESPACE}:`)) {
83
+ return network;
84
+ }
85
+ return `${COSMOS_CAIP2_NAMESPACE}:${network}`;
86
+ }
87
+ function toAtomicUnits(decimalAmount, decimals) {
88
+ const amount = typeof decimalAmount === "string" ? parseFloat(decimalAmount) : decimalAmount;
89
+ if (isNaN(amount)) {
90
+ throw new Error(`Invalid amount: ${decimalAmount}`);
91
+ }
92
+ const multiplier = 10 ** decimals;
93
+ return BigInt(Math.floor(amount * multiplier));
94
+ }
95
+
96
+ // src/exact-direct/server/scheme.ts
97
+ var ExactDirectCosmosServer = class {
98
+ constructor(config = {}) {
99
+ this.scheme = SCHEME_EXACT_DIRECT;
100
+ this.moneyParsers = [];
101
+ this.config = config;
102
+ }
103
+ /**
104
+ * Register a custom money parser in the parser chain.
105
+ * Multiple parsers can be registered - they will be tried in registration order.
106
+ * Each parser receives a decimal amount (e.g., 1.50 for $1.50).
107
+ * If a parser returns null, the next parser in the chain will be tried.
108
+ * The default parser is always the final fallback.
109
+ *
110
+ * @param parser - Custom function to convert amount to AssetAmount (or null to skip)
111
+ * @returns The server instance for chaining
112
+ */
113
+ registerMoneyParser(parser) {
114
+ this.moneyParsers.push(parser);
115
+ return this;
116
+ }
117
+ /**
118
+ * Parses a price into an asset amount.
119
+ * If price is already an AssetAmount, returns it directly.
120
+ * If price is Money (string | number), parses to decimal and tries custom parsers.
121
+ * Falls back to default conversion if all custom parsers return null.
122
+ *
123
+ * @param price - The price to parse
124
+ * @param network - The network to use
125
+ * @returns Promise that resolves to the parsed asset amount
126
+ */
127
+ async parsePrice(price, network) {
128
+ const normalizedNetwork = normalizeNetwork(network);
129
+ if (typeof price === "object" && price !== null && "amount" in price) {
130
+ if (!price.asset) {
131
+ throw new Error(`Asset address must be specified for AssetAmount on network ${network}`);
132
+ }
133
+ return {
134
+ amount: price.amount,
135
+ asset: price.asset,
136
+ extra: price.extra || {}
137
+ };
138
+ }
139
+ const amount = this.parseMoneyToDecimal(price);
140
+ for (const parser of this.moneyParsers) {
141
+ const result = await parser(amount, normalizedNetwork);
142
+ if (result !== null) {
143
+ return result;
144
+ }
145
+ }
146
+ return this.defaultMoneyConversion(amount, normalizedNetwork);
147
+ }
148
+ /**
149
+ * Build payment requirements for this scheme/network combination.
150
+ *
151
+ * @param paymentRequirements - Base payment requirements with amount/asset already set
152
+ * @param supportedKind - The supported kind from facilitator's /supported endpoint
153
+ * @param extensionKeys - Extensions supported by the facilitator
154
+ * @returns Enhanced payment requirements ready to be sent to clients
155
+ */
156
+ async enhancePaymentRequirements(paymentRequirements, supportedKind, extensionKeys) {
157
+ void extensionKeys;
158
+ const extra = { ...paymentRequirements.extra };
159
+ const normalizedNetwork = normalizeNetwork(paymentRequirements.network);
160
+ const token = getDefaultToken(normalizedNetwork);
161
+ if (token) {
162
+ extra.bech32Prefix = NOBLE_BECH32_PREFIX;
163
+ extra.denom = token.denom;
164
+ }
165
+ if (supportedKind.extra?.assetSymbol) {
166
+ extra.assetSymbol = supportedKind.extra.assetSymbol;
167
+ }
168
+ if (supportedKind.extra?.assetDecimals) {
169
+ extra.assetDecimals = supportedKind.extra.assetDecimals;
170
+ }
171
+ if (supportedKind.extra?.assetDenom) {
172
+ extra.assetDenom = supportedKind.extra.assetDenom;
173
+ }
174
+ return {
175
+ ...paymentRequirements,
176
+ extra
177
+ };
178
+ }
179
+ /**
180
+ * Parse Money (string | number) to a decimal number.
181
+ * Handles formats like "$1.50", "1.50", 1.50, etc.
182
+ */
183
+ parseMoneyToDecimal(money) {
184
+ if (typeof money === "number") {
185
+ return money;
186
+ }
187
+ const cleanMoney = money.replace(/^\$/, "").trim();
188
+ const amount = parseFloat(cleanMoney);
189
+ if (isNaN(amount)) {
190
+ throw new Error(`Invalid money format: ${money}`);
191
+ }
192
+ return amount;
193
+ }
194
+ /**
195
+ * Default money conversion implementation.
196
+ * Converts decimal amount to the preferred token on the specified network.
197
+ */
198
+ defaultMoneyConversion(amount, network) {
199
+ const token = this.getDefaultAsset(network);
200
+ const tokenAmount = toAtomicUnits(amount, token.decimals);
201
+ return {
202
+ amount: tokenAmount.toString(),
203
+ asset: token.denom,
204
+ extra: {
205
+ symbol: token.symbol,
206
+ name: token.name,
207
+ decimals: token.decimals
208
+ }
209
+ };
210
+ }
211
+ /**
212
+ * Get the default asset info for a network.
213
+ * Priority: configured preferredToken > network default
214
+ */
215
+ getDefaultAsset(network) {
216
+ if (this.config.preferredToken) {
217
+ const preferred = getTokenConfig(network, this.config.preferredToken);
218
+ if (preferred) return preferred;
219
+ }
220
+ const defaultToken = getDefaultToken(network);
221
+ if (defaultToken) return defaultToken;
222
+ throw new Error(`No tokens configured for network ${network}`);
223
+ }
224
+ /**
225
+ * Get all supported networks
226
+ */
227
+ static getSupportedNetworks() {
228
+ return Object.keys(TOKEN_REGISTRY);
229
+ }
230
+ /**
231
+ * Check if a network is supported
232
+ */
233
+ static isNetworkSupported(network) {
234
+ return network in TOKEN_REGISTRY;
235
+ }
236
+ };
237
+ // Annotate the CommonJS export names for ESM import in node:
238
+ 0 && (module.exports = {
239
+ ExactDirectCosmosServer
240
+ });
241
+ //# sourceMappingURL=index.js.map