@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.
- package/README.md +175 -0
- package/dist/cjs/exact-direct/client/index.d.ts +50 -0
- package/dist/cjs/exact-direct/client/index.js +122 -0
- package/dist/cjs/exact-direct/client/index.js.map +1 -0
- package/dist/cjs/exact-direct/facilitator/index.d.ts +70 -0
- package/dist/cjs/exact-direct/facilitator/index.js +300 -0
- package/dist/cjs/exact-direct/facilitator/index.js.map +1 -0
- package/dist/cjs/exact-direct/server/index.d.ts +87 -0
- package/dist/cjs/exact-direct/server/index.js +241 -0
- package/dist/cjs/exact-direct/server/index.js.map +1 -0
- package/dist/cjs/index.d.ts +178 -0
- package/dist/cjs/index.js +619 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/types-CgseoctH.d.ts +155 -0
- package/dist/esm/chunk-2UTEGIZ2.mjs +101 -0
- package/dist/esm/chunk-2UTEGIZ2.mjs.map +1 -0
- package/dist/esm/chunk-57DBLYIR.mjs +58 -0
- package/dist/esm/chunk-57DBLYIR.mjs.map +1 -0
- package/dist/esm/chunk-JV6LXL2U.mjs +158 -0
- package/dist/esm/chunk-JV6LXL2U.mjs.map +1 -0
- package/dist/esm/chunk-QP5VO3IO.mjs +68 -0
- package/dist/esm/chunk-QP5VO3IO.mjs.map +1 -0
- package/dist/esm/chunk-XUMAS5DJ.mjs +223 -0
- package/dist/esm/chunk-XUMAS5DJ.mjs.map +1 -0
- package/dist/esm/exact-direct/client/index.d.mts +50 -0
- package/dist/esm/exact-direct/client/index.mjs +8 -0
- package/dist/esm/exact-direct/client/index.mjs.map +1 -0
- package/dist/esm/exact-direct/facilitator/index.d.mts +70 -0
- package/dist/esm/exact-direct/facilitator/index.mjs +9 -0
- package/dist/esm/exact-direct/facilitator/index.mjs.map +1 -0
- package/dist/esm/exact-direct/server/index.d.mts +87 -0
- package/dist/esm/exact-direct/server/index.mjs +9 -0
- package/dist/esm/exact-direct/server/index.mjs.map +1 -0
- package/dist/esm/index.d.mts +178 -0
- package/dist/esm/index.mjs +83 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/types-CgseoctH.d.mts +155 -0
- package/package.json +96 -0
|
@@ -0,0 +1,619 @@
|
|
|
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/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
COSMOS_CAIP2_NAMESPACE: () => COSMOS_CAIP2_NAMESPACE,
|
|
24
|
+
COSMOS_NETWORKS: () => COSMOS_NETWORKS,
|
|
25
|
+
DEFAULT_FEE_AMOUNT: () => DEFAULT_FEE_AMOUNT,
|
|
26
|
+
DEFAULT_GAS_LIMIT: () => DEFAULT_GAS_LIMIT,
|
|
27
|
+
DEFAULT_GAS_PRICE: () => DEFAULT_GAS_PRICE,
|
|
28
|
+
ExactDirectCosmosClient: () => ExactDirectCosmosClient,
|
|
29
|
+
ExactDirectCosmosFacilitator: () => ExactDirectCosmosFacilitator,
|
|
30
|
+
ExactDirectCosmosServer: () => ExactDirectCosmosServer,
|
|
31
|
+
MAX_TRANSACTION_AGE: () => MAX_TRANSACTION_AGE,
|
|
32
|
+
MSG_TYPE_SEND: () => MSG_TYPE_SEND,
|
|
33
|
+
NETWORK_REST_ENDPOINTS: () => NETWORK_REST_ENDPOINTS,
|
|
34
|
+
NETWORK_RPC_ENDPOINTS: () => NETWORK_RPC_ENDPOINTS,
|
|
35
|
+
NOBLE_BECH32_PREFIX: () => NOBLE_BECH32_PREFIX,
|
|
36
|
+
NOBLE_MAINNET_CAIP2: () => NOBLE_MAINNET_CAIP2,
|
|
37
|
+
NOBLE_MAINNET_REST: () => NOBLE_MAINNET_REST,
|
|
38
|
+
NOBLE_MAINNET_RPC: () => NOBLE_MAINNET_RPC,
|
|
39
|
+
NOBLE_TESTNET_CAIP2: () => NOBLE_TESTNET_CAIP2,
|
|
40
|
+
NOBLE_TESTNET_REST: () => NOBLE_TESTNET_REST,
|
|
41
|
+
NOBLE_TESTNET_RPC: () => NOBLE_TESTNET_RPC,
|
|
42
|
+
SCHEME_EXACT_DIRECT: () => SCHEME_EXACT_DIRECT,
|
|
43
|
+
TOKEN_REGISTRY: () => TOKEN_REGISTRY,
|
|
44
|
+
USDC_DENOM: () => USDC_DENOM,
|
|
45
|
+
extractNetworkId: () => extractNetworkId,
|
|
46
|
+
formatAmount: () => formatAmount,
|
|
47
|
+
getDefaultToken: () => getDefaultToken,
|
|
48
|
+
getNetworkTokens: () => getNetworkTokens,
|
|
49
|
+
getRestEndpoint: () => getRestEndpoint,
|
|
50
|
+
getRpcEndpoint: () => getRpcEndpoint,
|
|
51
|
+
getSupportedNetworks: () => getSupportedNetworks,
|
|
52
|
+
getTokenByDenom: () => getTokenByDenom,
|
|
53
|
+
getTokenConfig: () => getTokenConfig,
|
|
54
|
+
isNetworkSupported: () => isNetworkSupported,
|
|
55
|
+
isValidAddress: () => isValidAddress,
|
|
56
|
+
normalizeNetwork: () => normalizeNetwork,
|
|
57
|
+
toAtomicUnits: () => toAtomicUnits
|
|
58
|
+
});
|
|
59
|
+
module.exports = __toCommonJS(src_exports);
|
|
60
|
+
|
|
61
|
+
// src/constants.ts
|
|
62
|
+
var NOBLE_MAINNET_CAIP2 = "cosmos:noble-1";
|
|
63
|
+
var NOBLE_TESTNET_CAIP2 = "cosmos:grand-1";
|
|
64
|
+
var COSMOS_NETWORKS = [NOBLE_MAINNET_CAIP2, NOBLE_TESTNET_CAIP2];
|
|
65
|
+
var NOBLE_MAINNET_RPC = "https://noble-rpc.polkachu.com";
|
|
66
|
+
var NOBLE_TESTNET_RPC = "https://rpc.testnet.noble.strange.love";
|
|
67
|
+
var NOBLE_MAINNET_REST = "https://noble-api.polkachu.com";
|
|
68
|
+
var NOBLE_TESTNET_REST = "https://api.testnet.noble.strange.love";
|
|
69
|
+
var NETWORK_RPC_ENDPOINTS = {
|
|
70
|
+
[NOBLE_MAINNET_CAIP2]: NOBLE_MAINNET_RPC,
|
|
71
|
+
[NOBLE_TESTNET_CAIP2]: NOBLE_TESTNET_RPC
|
|
72
|
+
};
|
|
73
|
+
var NETWORK_REST_ENDPOINTS = {
|
|
74
|
+
[NOBLE_MAINNET_CAIP2]: NOBLE_MAINNET_REST,
|
|
75
|
+
[NOBLE_TESTNET_CAIP2]: NOBLE_TESTNET_REST
|
|
76
|
+
};
|
|
77
|
+
var NOBLE_BECH32_PREFIX = "noble";
|
|
78
|
+
var USDC_DENOM = "uusdc";
|
|
79
|
+
var DEFAULT_GAS_LIMIT = 2e5;
|
|
80
|
+
var DEFAULT_GAS_PRICE = "0.025uusdc";
|
|
81
|
+
var DEFAULT_FEE_AMOUNT = "5000";
|
|
82
|
+
var MSG_TYPE_SEND = "/cosmos.bank.v1beta1.MsgSend";
|
|
83
|
+
var SCHEME_EXACT_DIRECT = "exact-direct";
|
|
84
|
+
var MAX_TRANSACTION_AGE = 5 * 60 * 1e3;
|
|
85
|
+
var COSMOS_CAIP2_NAMESPACE = "cosmos";
|
|
86
|
+
|
|
87
|
+
// src/tokens.ts
|
|
88
|
+
var TOKEN_REGISTRY = {
|
|
89
|
+
[NOBLE_MAINNET_CAIP2]: [
|
|
90
|
+
{
|
|
91
|
+
denom: USDC_DENOM,
|
|
92
|
+
symbol: "USDC",
|
|
93
|
+
name: "USD Coin",
|
|
94
|
+
decimals: 6,
|
|
95
|
+
priority: 1
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
[NOBLE_TESTNET_CAIP2]: [
|
|
99
|
+
{
|
|
100
|
+
denom: USDC_DENOM,
|
|
101
|
+
symbol: "USDC",
|
|
102
|
+
name: "USD Coin (Testnet)",
|
|
103
|
+
decimals: 6,
|
|
104
|
+
priority: 1
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
};
|
|
108
|
+
function getTokenConfig(network, symbol) {
|
|
109
|
+
const tokens = TOKEN_REGISTRY[network];
|
|
110
|
+
if (!tokens) return void 0;
|
|
111
|
+
return tokens.find((t) => t.symbol.toUpperCase() === symbol.toUpperCase());
|
|
112
|
+
}
|
|
113
|
+
function getTokenByDenom(network, denom) {
|
|
114
|
+
const tokens = TOKEN_REGISTRY[network];
|
|
115
|
+
if (!tokens) return void 0;
|
|
116
|
+
return tokens.find((t) => t.denom === denom);
|
|
117
|
+
}
|
|
118
|
+
function getDefaultToken(network) {
|
|
119
|
+
const tokens = TOKEN_REGISTRY[network];
|
|
120
|
+
if (!tokens || tokens.length === 0) return void 0;
|
|
121
|
+
return [...tokens].sort((a, b) => a.priority - b.priority)[0];
|
|
122
|
+
}
|
|
123
|
+
function getNetworkTokens(network) {
|
|
124
|
+
return TOKEN_REGISTRY[network] || [];
|
|
125
|
+
}
|
|
126
|
+
function isNetworkSupported(network) {
|
|
127
|
+
return network in TOKEN_REGISTRY;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/utils.ts
|
|
131
|
+
function normalizeNetwork(network) {
|
|
132
|
+
if (network.startsWith(`${COSMOS_CAIP2_NAMESPACE}:`)) {
|
|
133
|
+
return network;
|
|
134
|
+
}
|
|
135
|
+
return `${COSMOS_CAIP2_NAMESPACE}:${network}`;
|
|
136
|
+
}
|
|
137
|
+
function extractNetworkId(network) {
|
|
138
|
+
if (network.includes(":")) {
|
|
139
|
+
return network.split(":")[1];
|
|
140
|
+
}
|
|
141
|
+
return network;
|
|
142
|
+
}
|
|
143
|
+
function isValidAddress(address) {
|
|
144
|
+
if (!address || address.length < 10) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return address.startsWith(`${NOBLE_BECH32_PREFIX}1`);
|
|
148
|
+
}
|
|
149
|
+
function getRpcEndpoint(network) {
|
|
150
|
+
const normalizedNetwork = normalizeNetwork(network);
|
|
151
|
+
return NETWORK_RPC_ENDPOINTS[normalizedNetwork] || NETWORK_RPC_ENDPOINTS["cosmos:noble-1"];
|
|
152
|
+
}
|
|
153
|
+
function getRestEndpoint(network) {
|
|
154
|
+
const normalizedNetwork = normalizeNetwork(network);
|
|
155
|
+
return NETWORK_REST_ENDPOINTS[normalizedNetwork] || NETWORK_REST_ENDPOINTS["cosmos:noble-1"];
|
|
156
|
+
}
|
|
157
|
+
function toAtomicUnits(decimalAmount, decimals) {
|
|
158
|
+
const amount = typeof decimalAmount === "string" ? parseFloat(decimalAmount) : decimalAmount;
|
|
159
|
+
if (isNaN(amount)) {
|
|
160
|
+
throw new Error(`Invalid amount: ${decimalAmount}`);
|
|
161
|
+
}
|
|
162
|
+
const multiplier = 10 ** decimals;
|
|
163
|
+
return BigInt(Math.floor(amount * multiplier));
|
|
164
|
+
}
|
|
165
|
+
function formatAmount(amount, decimals) {
|
|
166
|
+
const divisor = BigInt(10 ** decimals);
|
|
167
|
+
const whole = amount / divisor;
|
|
168
|
+
const remainder = amount % divisor;
|
|
169
|
+
const decimal = remainder.toString().padStart(decimals, "0").slice(0, 2);
|
|
170
|
+
return `${whole}.${decimal}`;
|
|
171
|
+
}
|
|
172
|
+
function getSupportedNetworks() {
|
|
173
|
+
return Object.keys(NETWORK_RPC_ENDPOINTS);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/exact-direct/client/scheme.ts
|
|
177
|
+
var ExactDirectCosmosClient = class {
|
|
178
|
+
/**
|
|
179
|
+
* Creates a new ExactDirectCosmosClient instance.
|
|
180
|
+
*
|
|
181
|
+
* @param signer - The Cosmos signer for client operations
|
|
182
|
+
* @param config - Optional configuration overrides
|
|
183
|
+
*/
|
|
184
|
+
constructor(signer, config = {}) {
|
|
185
|
+
this.signer = signer;
|
|
186
|
+
this.config = config;
|
|
187
|
+
this.scheme = SCHEME_EXACT_DIRECT;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Creates a payment payload by executing the transfer.
|
|
191
|
+
*
|
|
192
|
+
* Unlike other schemes where the client creates a signed message for
|
|
193
|
+
* the facilitator to execute, the exact-direct scheme has the client
|
|
194
|
+
* execute the transfer directly. The transaction hash is then used
|
|
195
|
+
* as proof of payment.
|
|
196
|
+
*
|
|
197
|
+
* @param t402Version - The t402 protocol version
|
|
198
|
+
* @param paymentRequirements - The payment requirements
|
|
199
|
+
* @returns Promise resolving to a payment payload with transaction hash
|
|
200
|
+
*/
|
|
201
|
+
async createPaymentPayload(t402Version, paymentRequirements) {
|
|
202
|
+
const network = normalizeNetwork(paymentRequirements.network);
|
|
203
|
+
if (!paymentRequirements.payTo) {
|
|
204
|
+
throw new Error("PayTo address is required");
|
|
205
|
+
}
|
|
206
|
+
if (!paymentRequirements.amount) {
|
|
207
|
+
throw new Error("Amount is required");
|
|
208
|
+
}
|
|
209
|
+
if (!isValidAddress(paymentRequirements.payTo)) {
|
|
210
|
+
throw new Error(`Invalid recipient address: ${paymentRequirements.payTo}`);
|
|
211
|
+
}
|
|
212
|
+
if (!isValidAddress(this.signer.address)) {
|
|
213
|
+
throw new Error(`Invalid sender address: ${this.signer.address}`);
|
|
214
|
+
}
|
|
215
|
+
const recipient = paymentRequirements.payTo;
|
|
216
|
+
const amount = paymentRequirements.amount;
|
|
217
|
+
const denom = paymentRequirements.extra?.denom || this.config.denom || USDC_DENOM;
|
|
218
|
+
const txHash = await this.signer.sendTokens(network, recipient, amount, denom);
|
|
219
|
+
const payload = {
|
|
220
|
+
txHash,
|
|
221
|
+
from: this.signer.address,
|
|
222
|
+
to: recipient,
|
|
223
|
+
amount,
|
|
224
|
+
denom
|
|
225
|
+
};
|
|
226
|
+
return {
|
|
227
|
+
t402Version,
|
|
228
|
+
payload
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// src/exact-direct/server/scheme.ts
|
|
234
|
+
var ExactDirectCosmosServer = class {
|
|
235
|
+
constructor(config = {}) {
|
|
236
|
+
this.scheme = SCHEME_EXACT_DIRECT;
|
|
237
|
+
this.moneyParsers = [];
|
|
238
|
+
this.config = config;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Register a custom money parser in the parser chain.
|
|
242
|
+
* Multiple parsers can be registered - they will be tried in registration order.
|
|
243
|
+
* Each parser receives a decimal amount (e.g., 1.50 for $1.50).
|
|
244
|
+
* If a parser returns null, the next parser in the chain will be tried.
|
|
245
|
+
* The default parser is always the final fallback.
|
|
246
|
+
*
|
|
247
|
+
* @param parser - Custom function to convert amount to AssetAmount (or null to skip)
|
|
248
|
+
* @returns The server instance for chaining
|
|
249
|
+
*/
|
|
250
|
+
registerMoneyParser(parser) {
|
|
251
|
+
this.moneyParsers.push(parser);
|
|
252
|
+
return this;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Parses a price into an asset amount.
|
|
256
|
+
* If price is already an AssetAmount, returns it directly.
|
|
257
|
+
* If price is Money (string | number), parses to decimal and tries custom parsers.
|
|
258
|
+
* Falls back to default conversion if all custom parsers return null.
|
|
259
|
+
*
|
|
260
|
+
* @param price - The price to parse
|
|
261
|
+
* @param network - The network to use
|
|
262
|
+
* @returns Promise that resolves to the parsed asset amount
|
|
263
|
+
*/
|
|
264
|
+
async parsePrice(price, network) {
|
|
265
|
+
const normalizedNetwork = normalizeNetwork(network);
|
|
266
|
+
if (typeof price === "object" && price !== null && "amount" in price) {
|
|
267
|
+
if (!price.asset) {
|
|
268
|
+
throw new Error(`Asset address must be specified for AssetAmount on network ${network}`);
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
amount: price.amount,
|
|
272
|
+
asset: price.asset,
|
|
273
|
+
extra: price.extra || {}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const amount = this.parseMoneyToDecimal(price);
|
|
277
|
+
for (const parser of this.moneyParsers) {
|
|
278
|
+
const result = await parser(amount, normalizedNetwork);
|
|
279
|
+
if (result !== null) {
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return this.defaultMoneyConversion(amount, normalizedNetwork);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Build payment requirements for this scheme/network combination.
|
|
287
|
+
*
|
|
288
|
+
* @param paymentRequirements - Base payment requirements with amount/asset already set
|
|
289
|
+
* @param supportedKind - The supported kind from facilitator's /supported endpoint
|
|
290
|
+
* @param extensionKeys - Extensions supported by the facilitator
|
|
291
|
+
* @returns Enhanced payment requirements ready to be sent to clients
|
|
292
|
+
*/
|
|
293
|
+
async enhancePaymentRequirements(paymentRequirements, supportedKind, extensionKeys) {
|
|
294
|
+
void extensionKeys;
|
|
295
|
+
const extra = { ...paymentRequirements.extra };
|
|
296
|
+
const normalizedNetwork = normalizeNetwork(paymentRequirements.network);
|
|
297
|
+
const token = getDefaultToken(normalizedNetwork);
|
|
298
|
+
if (token) {
|
|
299
|
+
extra.bech32Prefix = NOBLE_BECH32_PREFIX;
|
|
300
|
+
extra.denom = token.denom;
|
|
301
|
+
}
|
|
302
|
+
if (supportedKind.extra?.assetSymbol) {
|
|
303
|
+
extra.assetSymbol = supportedKind.extra.assetSymbol;
|
|
304
|
+
}
|
|
305
|
+
if (supportedKind.extra?.assetDecimals) {
|
|
306
|
+
extra.assetDecimals = supportedKind.extra.assetDecimals;
|
|
307
|
+
}
|
|
308
|
+
if (supportedKind.extra?.assetDenom) {
|
|
309
|
+
extra.assetDenom = supportedKind.extra.assetDenom;
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
...paymentRequirements,
|
|
313
|
+
extra
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Parse Money (string | number) to a decimal number.
|
|
318
|
+
* Handles formats like "$1.50", "1.50", 1.50, etc.
|
|
319
|
+
*/
|
|
320
|
+
parseMoneyToDecimal(money) {
|
|
321
|
+
if (typeof money === "number") {
|
|
322
|
+
return money;
|
|
323
|
+
}
|
|
324
|
+
const cleanMoney = money.replace(/^\$/, "").trim();
|
|
325
|
+
const amount = parseFloat(cleanMoney);
|
|
326
|
+
if (isNaN(amount)) {
|
|
327
|
+
throw new Error(`Invalid money format: ${money}`);
|
|
328
|
+
}
|
|
329
|
+
return amount;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Default money conversion implementation.
|
|
333
|
+
* Converts decimal amount to the preferred token on the specified network.
|
|
334
|
+
*/
|
|
335
|
+
defaultMoneyConversion(amount, network) {
|
|
336
|
+
const token = this.getDefaultAsset(network);
|
|
337
|
+
const tokenAmount = toAtomicUnits(amount, token.decimals);
|
|
338
|
+
return {
|
|
339
|
+
amount: tokenAmount.toString(),
|
|
340
|
+
asset: token.denom,
|
|
341
|
+
extra: {
|
|
342
|
+
symbol: token.symbol,
|
|
343
|
+
name: token.name,
|
|
344
|
+
decimals: token.decimals
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Get the default asset info for a network.
|
|
350
|
+
* Priority: configured preferredToken > network default
|
|
351
|
+
*/
|
|
352
|
+
getDefaultAsset(network) {
|
|
353
|
+
if (this.config.preferredToken) {
|
|
354
|
+
const preferred = getTokenConfig(network, this.config.preferredToken);
|
|
355
|
+
if (preferred) return preferred;
|
|
356
|
+
}
|
|
357
|
+
const defaultToken = getDefaultToken(network);
|
|
358
|
+
if (defaultToken) return defaultToken;
|
|
359
|
+
throw new Error(`No tokens configured for network ${network}`);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Get all supported networks
|
|
363
|
+
*/
|
|
364
|
+
static getSupportedNetworks() {
|
|
365
|
+
return Object.keys(TOKEN_REGISTRY);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Check if a network is supported
|
|
369
|
+
*/
|
|
370
|
+
static isNetworkSupported(network) {
|
|
371
|
+
return network in TOKEN_REGISTRY;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/exact-direct/facilitator/scheme.ts
|
|
376
|
+
var ExactDirectCosmosFacilitator = class {
|
|
377
|
+
constructor(signer, config) {
|
|
378
|
+
this.signer = signer;
|
|
379
|
+
this.scheme = SCHEME_EXACT_DIRECT;
|
|
380
|
+
this.caipFamily = `${COSMOS_CAIP2_NAMESPACE}:*`;
|
|
381
|
+
this.usedTxs = /* @__PURE__ */ new Map();
|
|
382
|
+
this.config = {
|
|
383
|
+
maxTransactionAge: config?.maxTransactionAge ?? MAX_TRANSACTION_AGE,
|
|
384
|
+
usedTxCacheDuration: config?.usedTxCacheDuration ?? 24 * 60 * 60 * 1e3
|
|
385
|
+
// 24 hours
|
|
386
|
+
};
|
|
387
|
+
this.startCleanupInterval();
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Get extra data for a supported kind
|
|
391
|
+
*/
|
|
392
|
+
getExtra(network) {
|
|
393
|
+
const token = getDefaultToken(network);
|
|
394
|
+
if (!token) {
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
assetSymbol: token.symbol,
|
|
399
|
+
assetDecimals: token.decimals,
|
|
400
|
+
assetDenom: token.denom
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Get signer addresses for a network
|
|
405
|
+
*/
|
|
406
|
+
getSigners(network) {
|
|
407
|
+
return this.signer.getAddresses(network);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Verify a payment payload
|
|
411
|
+
*/
|
|
412
|
+
async verify(payload, requirements) {
|
|
413
|
+
const network = normalizeNetwork(requirements.network);
|
|
414
|
+
if (payload.accepted.scheme !== SCHEME_EXACT_DIRECT) {
|
|
415
|
+
return {
|
|
416
|
+
isValid: false,
|
|
417
|
+
invalidReason: "invalid_scheme"
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
if (normalizeNetwork(payload.accepted.network) !== network) {
|
|
421
|
+
return {
|
|
422
|
+
isValid: false,
|
|
423
|
+
invalidReason: "network_mismatch"
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
const cosmosPayload = payload.payload;
|
|
427
|
+
if (!cosmosPayload.txHash) {
|
|
428
|
+
return {
|
|
429
|
+
isValid: false,
|
|
430
|
+
invalidReason: "missing_tx_hash"
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
if (!cosmosPayload.from || !isValidAddress(cosmosPayload.from)) {
|
|
434
|
+
return {
|
|
435
|
+
isValid: false,
|
|
436
|
+
invalidReason: "invalid_from_address"
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
if (this.isTxUsed(cosmosPayload.txHash)) {
|
|
440
|
+
return {
|
|
441
|
+
isValid: false,
|
|
442
|
+
invalidReason: "transaction_already_used",
|
|
443
|
+
payer: cosmosPayload.from
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
try {
|
|
447
|
+
const tx = await this.signer.queryTransaction(network, cosmosPayload.txHash);
|
|
448
|
+
if (tx.code !== 0) {
|
|
449
|
+
return {
|
|
450
|
+
isValid: false,
|
|
451
|
+
invalidReason: "transaction_failed",
|
|
452
|
+
payer: cosmosPayload.from
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
const msgSend = this.findMsgSend(tx.tx.body.messages);
|
|
456
|
+
if (!msgSend) {
|
|
457
|
+
return {
|
|
458
|
+
isValid: false,
|
|
459
|
+
invalidReason: "no_msg_send_found",
|
|
460
|
+
payer: cosmosPayload.from
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
if (msgSend.toAddress !== requirements.payTo) {
|
|
464
|
+
return {
|
|
465
|
+
isValid: false,
|
|
466
|
+
invalidReason: "wrong_recipient",
|
|
467
|
+
payer: cosmosPayload.from
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
if (msgSend.fromAddress !== cosmosPayload.from) {
|
|
471
|
+
return {
|
|
472
|
+
isValid: false,
|
|
473
|
+
invalidReason: "sender_mismatch",
|
|
474
|
+
payer: cosmosPayload.from
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
const expectedDenom = requirements.extra?.denom || requirements.asset;
|
|
478
|
+
const coin = this.getAmountByDenom(msgSend, expectedDenom);
|
|
479
|
+
if (!coin) {
|
|
480
|
+
return {
|
|
481
|
+
isValid: false,
|
|
482
|
+
invalidReason: "wrong_denomination",
|
|
483
|
+
payer: cosmosPayload.from
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
const txAmount = BigInt(coin.amount);
|
|
487
|
+
const requiredAmount = BigInt(requirements.amount);
|
|
488
|
+
if (txAmount < requiredAmount) {
|
|
489
|
+
return {
|
|
490
|
+
isValid: false,
|
|
491
|
+
invalidReason: "insufficient_amount",
|
|
492
|
+
payer: cosmosPayload.from
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
this.markTxUsed(cosmosPayload.txHash);
|
|
496
|
+
return {
|
|
497
|
+
isValid: true,
|
|
498
|
+
payer: cosmosPayload.from
|
|
499
|
+
};
|
|
500
|
+
} catch {
|
|
501
|
+
return {
|
|
502
|
+
isValid: false,
|
|
503
|
+
invalidReason: "transaction_not_found",
|
|
504
|
+
payer: cosmosPayload.from
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Settle a payment - for exact-direct, the transfer is already complete
|
|
510
|
+
*/
|
|
511
|
+
async settle(payload, requirements) {
|
|
512
|
+
const verifyResult = await this.verify(payload, requirements);
|
|
513
|
+
if (!verifyResult.isValid) {
|
|
514
|
+
return {
|
|
515
|
+
success: false,
|
|
516
|
+
errorReason: verifyResult.invalidReason || "verification_failed",
|
|
517
|
+
payer: verifyResult.payer,
|
|
518
|
+
transaction: "",
|
|
519
|
+
network: normalizeNetwork(requirements.network)
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
const cosmosPayload = payload.payload;
|
|
523
|
+
return {
|
|
524
|
+
success: true,
|
|
525
|
+
transaction: cosmosPayload.txHash,
|
|
526
|
+
network: normalizeNetwork(requirements.network),
|
|
527
|
+
payer: cosmosPayload.from
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Find a MsgSend in transaction messages
|
|
532
|
+
*/
|
|
533
|
+
findMsgSend(messages) {
|
|
534
|
+
for (const msg of messages) {
|
|
535
|
+
if (msg["@type"] === MSG_TYPE_SEND || !msg["@type"] && msg.fromAddress && msg.toAddress) {
|
|
536
|
+
return msg;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Get a specific coin amount from a MsgSend by denomination
|
|
543
|
+
*/
|
|
544
|
+
getAmountByDenom(msg, denom) {
|
|
545
|
+
for (const coin of msg.amount) {
|
|
546
|
+
if (coin.denom === denom) {
|
|
547
|
+
return coin;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Check if a transaction has been used
|
|
554
|
+
*/
|
|
555
|
+
isTxUsed(txHash) {
|
|
556
|
+
return this.usedTxs.has(txHash);
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Mark a transaction as used
|
|
560
|
+
*/
|
|
561
|
+
markTxUsed(txHash) {
|
|
562
|
+
this.usedTxs.set(txHash, Date.now());
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Start the cleanup interval for used transactions
|
|
566
|
+
*/
|
|
567
|
+
startCleanupInterval() {
|
|
568
|
+
setInterval(
|
|
569
|
+
() => {
|
|
570
|
+
const cutoff = Date.now() - this.config.usedTxCacheDuration;
|
|
571
|
+
for (const [txHash, usedAt] of this.usedTxs.entries()) {
|
|
572
|
+
if (usedAt < cutoff) {
|
|
573
|
+
this.usedTxs.delete(txHash);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
60 * 60 * 1e3
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
582
|
+
0 && (module.exports = {
|
|
583
|
+
COSMOS_CAIP2_NAMESPACE,
|
|
584
|
+
COSMOS_NETWORKS,
|
|
585
|
+
DEFAULT_FEE_AMOUNT,
|
|
586
|
+
DEFAULT_GAS_LIMIT,
|
|
587
|
+
DEFAULT_GAS_PRICE,
|
|
588
|
+
ExactDirectCosmosClient,
|
|
589
|
+
ExactDirectCosmosFacilitator,
|
|
590
|
+
ExactDirectCosmosServer,
|
|
591
|
+
MAX_TRANSACTION_AGE,
|
|
592
|
+
MSG_TYPE_SEND,
|
|
593
|
+
NETWORK_REST_ENDPOINTS,
|
|
594
|
+
NETWORK_RPC_ENDPOINTS,
|
|
595
|
+
NOBLE_BECH32_PREFIX,
|
|
596
|
+
NOBLE_MAINNET_CAIP2,
|
|
597
|
+
NOBLE_MAINNET_REST,
|
|
598
|
+
NOBLE_MAINNET_RPC,
|
|
599
|
+
NOBLE_TESTNET_CAIP2,
|
|
600
|
+
NOBLE_TESTNET_REST,
|
|
601
|
+
NOBLE_TESTNET_RPC,
|
|
602
|
+
SCHEME_EXACT_DIRECT,
|
|
603
|
+
TOKEN_REGISTRY,
|
|
604
|
+
USDC_DENOM,
|
|
605
|
+
extractNetworkId,
|
|
606
|
+
formatAmount,
|
|
607
|
+
getDefaultToken,
|
|
608
|
+
getNetworkTokens,
|
|
609
|
+
getRestEndpoint,
|
|
610
|
+
getRpcEndpoint,
|
|
611
|
+
getSupportedNetworks,
|
|
612
|
+
getTokenByDenom,
|
|
613
|
+
getTokenConfig,
|
|
614
|
+
isNetworkSupported,
|
|
615
|
+
isValidAddress,
|
|
616
|
+
normalizeNetwork,
|
|
617
|
+
toAtomicUnits
|
|
618
|
+
});
|
|
619
|
+
//# sourceMappingURL=index.js.map
|