moltspay 1.5.0 → 1.6.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 +142 -0
- package/dist/cli/index.js +476 -144
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +473 -141
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/index.d.mts +5 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +235 -108
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +231 -106
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/web/index.d.mts +418 -0
- package/dist/client/web/index.mjs +1289 -0
- package/dist/client/web/index.mjs.map +1 -0
- package/dist/facilitators/index.d.mts +24 -2
- package/dist/facilitators/index.d.ts +24 -2
- package/dist/facilitators/index.js +127 -13
- package/dist/facilitators/index.js.map +1 -1
- package/dist/facilitators/index.mjs +127 -13
- package/dist/facilitators/index.mjs.map +1 -1
- package/dist/index.js +453 -141
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +450 -138
- package/dist/index.mjs.map +1 -1
- package/dist/mcp/index.js +234 -109
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/index.mjs +231 -106
- package/dist/mcp/index.mjs.map +1 -1
- package/dist/server/index.d.mts +43 -1
- package/dist/server/index.d.ts +43 -1
- package/dist/server/index.js +205 -18
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +205 -18
- package/dist/server/index.mjs.map +1 -1
- package/package.json +9 -2
package/dist/client/index.d.mts
CHANGED
|
@@ -90,6 +90,7 @@ declare class MoltsPayClient {
|
|
|
90
90
|
private config;
|
|
91
91
|
private walletData;
|
|
92
92
|
private wallet;
|
|
93
|
+
private signer;
|
|
93
94
|
private todaySpending;
|
|
94
95
|
private lastSpendingReset;
|
|
95
96
|
constructor(options?: MoltsPayClientOptions);
|
|
@@ -161,6 +162,10 @@ declare class MoltsPayClient {
|
|
|
161
162
|
* Sign EIP-3009 transferWithAuthorization (GASLESS)
|
|
162
163
|
* This only signs - no on-chain transaction, no gas needed.
|
|
163
164
|
* Supports both USDC and USDT.
|
|
165
|
+
*
|
|
166
|
+
* Delegates typed-data construction to `core/eip3009.ts` and the signature
|
|
167
|
+
* itself to `this.signer`. That way Web Client (Phase 4) can reuse the same
|
|
168
|
+
* flow with an EIP-1193 signer without duplicating typed-data layout.
|
|
164
169
|
*/
|
|
165
170
|
private signEIP3009;
|
|
166
171
|
/**
|
package/dist/client/index.d.ts
CHANGED
|
@@ -90,6 +90,7 @@ declare class MoltsPayClient {
|
|
|
90
90
|
private config;
|
|
91
91
|
private walletData;
|
|
92
92
|
private wallet;
|
|
93
|
+
private signer;
|
|
93
94
|
private todaySpending;
|
|
94
95
|
private lastSpendingReset;
|
|
95
96
|
constructor(options?: MoltsPayClientOptions);
|
|
@@ -161,6 +162,10 @@ declare class MoltsPayClient {
|
|
|
161
162
|
* Sign EIP-3009 transferWithAuthorization (GASLESS)
|
|
162
163
|
* This only signs - no on-chain transaction, no gas needed.
|
|
163
164
|
* Supports both USDC and USDT.
|
|
165
|
+
*
|
|
166
|
+
* Delegates typed-data construction to `core/eip3009.ts` and the signature
|
|
167
|
+
* itself to `this.signer`. That way Web Client (Phase 4) can reuse the same
|
|
168
|
+
* flow with an EIP-1193 signer without duplicating typed-data layout.
|
|
164
169
|
*/
|
|
165
170
|
private signEIP3009;
|
|
166
171
|
/**
|
package/dist/client/index.js
CHANGED
|
@@ -33,10 +33,12 @@ __export(client_exports, {
|
|
|
33
33
|
MoltsPayClient: () => MoltsPayClient
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(client_exports);
|
|
36
|
+
|
|
37
|
+
// src/client/node/index.ts
|
|
36
38
|
var import_fs2 = require("fs");
|
|
37
39
|
var import_os2 = require("os");
|
|
38
40
|
var import_path2 = require("path");
|
|
39
|
-
var
|
|
41
|
+
var import_ethers2 = require("ethers");
|
|
40
42
|
|
|
41
43
|
// src/chains/index.ts
|
|
42
44
|
var CHAINS = {
|
|
@@ -277,16 +279,16 @@ function loadSolanaWallet(configDir = DEFAULT_CONFIG_DIR) {
|
|
|
277
279
|
// src/facilitators/solana.ts
|
|
278
280
|
var import_web33 = require("@solana/web3.js");
|
|
279
281
|
var import_spl_token2 = require("@solana/spl-token");
|
|
280
|
-
async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey) {
|
|
282
|
+
async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amount, chain, feePayerPubkey, connection) {
|
|
281
283
|
const chainConfig = SOLANA_CHAINS[chain];
|
|
282
|
-
const
|
|
284
|
+
const conn = connection ?? new import_web33.Connection(chainConfig.rpc, "confirmed");
|
|
283
285
|
const mint = new import_web33.PublicKey(chainConfig.tokens.USDC.mint);
|
|
284
286
|
const actualFeePayer = feePayerPubkey || senderPubkey;
|
|
285
287
|
const senderATA = await (0, import_spl_token2.getAssociatedTokenAddress)(mint, senderPubkey);
|
|
286
288
|
const recipientATA = await (0, import_spl_token2.getAssociatedTokenAddress)(mint, recipientPubkey);
|
|
287
289
|
const transaction = new import_web33.Transaction();
|
|
288
290
|
try {
|
|
289
|
-
await (0, import_spl_token2.getAccount)(
|
|
291
|
+
await (0, import_spl_token2.getAccount)(conn, recipientATA);
|
|
290
292
|
} catch {
|
|
291
293
|
transaction.add(
|
|
292
294
|
(0, import_spl_token2.createAssociatedTokenAccountInstruction)(
|
|
@@ -317,17 +319,178 @@ async function createSolanaPaymentTransaction(senderPubkey, recipientPubkey, amo
|
|
|
317
319
|
// decimals
|
|
318
320
|
)
|
|
319
321
|
);
|
|
320
|
-
const { blockhash, lastValidBlockHeight } = await
|
|
322
|
+
const { blockhash, lastValidBlockHeight } = await conn.getLatestBlockhash();
|
|
321
323
|
transaction.recentBlockhash = blockhash;
|
|
322
324
|
transaction.feePayer = actualFeePayer;
|
|
323
325
|
return transaction;
|
|
324
326
|
}
|
|
325
327
|
|
|
326
|
-
// src/client/index.ts
|
|
327
|
-
var
|
|
328
|
+
// src/client/node/index.ts
|
|
329
|
+
var import_web35 = require("@solana/web3.js");
|
|
330
|
+
|
|
331
|
+
// src/client/core/types.ts
|
|
328
332
|
var X402_VERSION = 2;
|
|
329
333
|
var PAYMENT_REQUIRED_HEADER = "x-payment-required";
|
|
330
334
|
var PAYMENT_HEADER = "x-payment";
|
|
335
|
+
|
|
336
|
+
// src/client/core/chain-map.ts
|
|
337
|
+
var NETWORK_TO_CHAIN = {
|
|
338
|
+
"eip155:8453": "base",
|
|
339
|
+
"eip155:137": "polygon",
|
|
340
|
+
"eip155:84532": "base_sepolia",
|
|
341
|
+
"eip155:42431": "tempo_moderato",
|
|
342
|
+
"eip155:56": "bnb",
|
|
343
|
+
"eip155:97": "bnb_testnet",
|
|
344
|
+
"solana:mainnet": "solana",
|
|
345
|
+
"solana:devnet": "solana_devnet"
|
|
346
|
+
};
|
|
347
|
+
var CHAIN_TO_NETWORK = Object.fromEntries(
|
|
348
|
+
Object.entries(NETWORK_TO_CHAIN).map(([network, chain]) => [chain, network])
|
|
349
|
+
);
|
|
350
|
+
function networkToChainName(network) {
|
|
351
|
+
return NETWORK_TO_CHAIN[network] ?? null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/client/core/base64.ts
|
|
355
|
+
var BufferCtor = globalThis.Buffer;
|
|
356
|
+
|
|
357
|
+
// src/client/core/eip3009.ts
|
|
358
|
+
var EIP3009_TYPES = {
|
|
359
|
+
TransferWithAuthorization: [
|
|
360
|
+
{ name: "from", type: "address" },
|
|
361
|
+
{ name: "to", type: "address" },
|
|
362
|
+
{ name: "value", type: "uint256" },
|
|
363
|
+
{ name: "validAfter", type: "uint256" },
|
|
364
|
+
{ name: "validBefore", type: "uint256" },
|
|
365
|
+
{ name: "nonce", type: "bytes32" }
|
|
366
|
+
]
|
|
367
|
+
};
|
|
368
|
+
function buildEIP3009TypedData(args) {
|
|
369
|
+
const validAfter = args.validAfter ?? "0";
|
|
370
|
+
const validBefore = args.validBefore ?? (Math.floor(Date.now() / 1e3) + 3600).toString();
|
|
371
|
+
const authorization = {
|
|
372
|
+
from: args.from,
|
|
373
|
+
to: args.to,
|
|
374
|
+
value: args.value,
|
|
375
|
+
validAfter,
|
|
376
|
+
validBefore,
|
|
377
|
+
nonce: args.nonce
|
|
378
|
+
};
|
|
379
|
+
return {
|
|
380
|
+
domain: {
|
|
381
|
+
name: args.tokenName,
|
|
382
|
+
version: args.tokenVersion,
|
|
383
|
+
chainId: args.chainId,
|
|
384
|
+
verifyingContract: args.tokenAddress
|
|
385
|
+
},
|
|
386
|
+
types: EIP3009_TYPES,
|
|
387
|
+
primaryType: "TransferWithAuthorization",
|
|
388
|
+
message: authorization
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/client/core/bnb-intent.ts
|
|
393
|
+
var BNB_INTENT_TYPES = {
|
|
394
|
+
PaymentIntent: [
|
|
395
|
+
{ name: "from", type: "address" },
|
|
396
|
+
{ name: "to", type: "address" },
|
|
397
|
+
{ name: "amount", type: "uint256" },
|
|
398
|
+
{ name: "token", type: "address" },
|
|
399
|
+
{ name: "service", type: "string" },
|
|
400
|
+
{ name: "nonce", type: "uint256" },
|
|
401
|
+
{ name: "deadline", type: "uint256" }
|
|
402
|
+
]
|
|
403
|
+
};
|
|
404
|
+
var BNB_DOMAIN_NAME = "MoltsPay";
|
|
405
|
+
var BNB_DOMAIN_VERSION = "1";
|
|
406
|
+
function buildBnbIntentTypedData(args) {
|
|
407
|
+
const intent = {
|
|
408
|
+
from: args.from,
|
|
409
|
+
to: args.to,
|
|
410
|
+
amount: args.amount,
|
|
411
|
+
token: args.tokenAddress,
|
|
412
|
+
service: args.service,
|
|
413
|
+
nonce: args.nonce,
|
|
414
|
+
deadline: args.deadline
|
|
415
|
+
};
|
|
416
|
+
return {
|
|
417
|
+
domain: {
|
|
418
|
+
name: BNB_DOMAIN_NAME,
|
|
419
|
+
version: BNB_DOMAIN_VERSION,
|
|
420
|
+
chainId: args.chainId
|
|
421
|
+
},
|
|
422
|
+
types: BNB_INTENT_TYPES,
|
|
423
|
+
primaryType: "PaymentIntent",
|
|
424
|
+
message: intent
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// src/client/node/signer.ts
|
|
429
|
+
var import_ethers = require("ethers");
|
|
430
|
+
var import_web34 = require("@solana/web3.js");
|
|
431
|
+
var NodeSigner = class {
|
|
432
|
+
evmWallet;
|
|
433
|
+
getSolanaKeypair;
|
|
434
|
+
constructor(evmWallet, options = {}) {
|
|
435
|
+
this.evmWallet = evmWallet;
|
|
436
|
+
this.getSolanaKeypair = options.getSolanaKeypair ?? (() => null);
|
|
437
|
+
}
|
|
438
|
+
async getEvmAddress() {
|
|
439
|
+
return this.evmWallet.address;
|
|
440
|
+
}
|
|
441
|
+
async getSolanaAddress() {
|
|
442
|
+
const kp = this.getSolanaKeypair();
|
|
443
|
+
return kp ? kp.publicKey.toBase58() : null;
|
|
444
|
+
}
|
|
445
|
+
async signTypedData(envelope) {
|
|
446
|
+
const mutableTypes = {};
|
|
447
|
+
for (const [key, fields] of Object.entries(envelope.types)) {
|
|
448
|
+
mutableTypes[key] = [...fields];
|
|
449
|
+
}
|
|
450
|
+
return this.evmWallet.signTypedData(
|
|
451
|
+
envelope.domain,
|
|
452
|
+
mutableTypes,
|
|
453
|
+
envelope.message
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
async sendEvmTransaction(args) {
|
|
457
|
+
const chain = findChainByChainId(args.chainId);
|
|
458
|
+
if (!chain) {
|
|
459
|
+
throw new Error(`sendEvmTransaction: unknown chainId ${args.chainId}`);
|
|
460
|
+
}
|
|
461
|
+
const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
|
|
462
|
+
const connected = this.evmWallet.connect(provider);
|
|
463
|
+
const tx = await connected.sendTransaction({
|
|
464
|
+
to: args.to,
|
|
465
|
+
data: args.data,
|
|
466
|
+
value: args.value ? BigInt(args.value) : 0n
|
|
467
|
+
});
|
|
468
|
+
return tx.hash;
|
|
469
|
+
}
|
|
470
|
+
async signSolanaTransaction(args) {
|
|
471
|
+
const kp = this.getSolanaKeypair();
|
|
472
|
+
if (!kp) {
|
|
473
|
+
throw new Error("signSolanaTransaction: no Solana wallet configured");
|
|
474
|
+
}
|
|
475
|
+
const tx = import_web34.Transaction.from(Buffer.from(args.transactionBase64, "base64"));
|
|
476
|
+
if (args.partialSign) {
|
|
477
|
+
tx.partialSign(kp);
|
|
478
|
+
} else {
|
|
479
|
+
tx.sign(kp);
|
|
480
|
+
}
|
|
481
|
+
return tx.serialize({ requireAllSignatures: false }).toString("base64");
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
function findChainByChainId(chainId) {
|
|
485
|
+
for (const cfg of Object.values(CHAINS)) {
|
|
486
|
+
if (cfg.chainId === chainId) {
|
|
487
|
+
return cfg;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return void 0;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// src/client/node/index.ts
|
|
331
494
|
var DEFAULT_CONFIG = {
|
|
332
495
|
chain: "base",
|
|
333
496
|
limits: {
|
|
@@ -340,6 +503,7 @@ var MoltsPayClient = class {
|
|
|
340
503
|
config;
|
|
341
504
|
walletData = null;
|
|
342
505
|
wallet = null;
|
|
506
|
+
signer = null;
|
|
343
507
|
todaySpending = 0;
|
|
344
508
|
lastSpendingReset = 0;
|
|
345
509
|
constructor(options = {}) {
|
|
@@ -348,7 +512,11 @@ var MoltsPayClient = class {
|
|
|
348
512
|
this.walletData = this.loadWallet();
|
|
349
513
|
this.loadSpending();
|
|
350
514
|
if (this.walletData) {
|
|
351
|
-
this.wallet = new
|
|
515
|
+
this.wallet = new import_ethers2.Wallet(this.walletData.privateKey);
|
|
516
|
+
const configDir = this.configDir;
|
|
517
|
+
this.signer = new NodeSigner(this.wallet, {
|
|
518
|
+
getSolanaKeypair: () => loadSolanaWallet(configDir)
|
|
519
|
+
});
|
|
352
520
|
}
|
|
353
521
|
}
|
|
354
522
|
/**
|
|
@@ -476,20 +644,6 @@ var MoltsPayClient = class {
|
|
|
476
644
|
} catch {
|
|
477
645
|
throw new Error("Invalid x-payment-required header");
|
|
478
646
|
}
|
|
479
|
-
const networkToChainName = (network2) => {
|
|
480
|
-
if (network2 === "solana:mainnet") return "solana";
|
|
481
|
-
if (network2 === "solana:devnet") return "solana_devnet";
|
|
482
|
-
const match = network2.match(/^eip155:(\d+)$/);
|
|
483
|
-
if (!match) return null;
|
|
484
|
-
const chainId = parseInt(match[1]);
|
|
485
|
-
if (chainId === 8453) return "base";
|
|
486
|
-
if (chainId === 137) return "polygon";
|
|
487
|
-
if (chainId === 84532) return "base_sepolia";
|
|
488
|
-
if (chainId === 42431) return "tempo_moderato";
|
|
489
|
-
if (chainId === 56) return "bnb";
|
|
490
|
-
if (chainId === 97) return "bnb_testnet";
|
|
491
|
-
return null;
|
|
492
|
-
};
|
|
493
647
|
const serverChains = requirements.map((r) => networkToChainName(r.network)).filter((c) => c !== null);
|
|
494
648
|
const userSpecifiedChain = options.chain;
|
|
495
649
|
let selectedChain;
|
|
@@ -718,14 +872,14 @@ Please specify: --chain <chain_name>`
|
|
|
718
872
|
async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
|
|
719
873
|
const { to, amount, token, chainName, chain, spender } = paymentDetails;
|
|
720
874
|
const tokenConfig = chain.tokens[token];
|
|
721
|
-
const provider = new
|
|
875
|
+
const provider = new import_ethers2.ethers.JsonRpcProvider(chain.rpc);
|
|
722
876
|
const allowance = await this.checkAllowance(tokenConfig.address, spender, provider);
|
|
723
877
|
const amountWeiCheck = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals));
|
|
724
878
|
if (allowance < amountWeiCheck) {
|
|
725
879
|
const nativeBalance = await provider.getBalance(this.wallet.address);
|
|
726
|
-
const minGasBalance =
|
|
880
|
+
const minGasBalance = import_ethers2.ethers.parseEther("0.0005");
|
|
727
881
|
if (nativeBalance < minGasBalance) {
|
|
728
|
-
const nativeBNB = parseFloat(
|
|
882
|
+
const nativeBNB = parseFloat(import_ethers2.ethers.formatEther(nativeBalance)).toFixed(4);
|
|
729
883
|
const isTestnet = chainName === "bnb_testnet";
|
|
730
884
|
if (isTestnet) {
|
|
731
885
|
throw new Error(
|
|
@@ -759,35 +913,21 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
759
913
|
);
|
|
760
914
|
}
|
|
761
915
|
const amountWei = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
|
|
762
|
-
const
|
|
916
|
+
const intentNonce = Date.now();
|
|
917
|
+
const intentDeadline = Date.now() + 36e5;
|
|
918
|
+
const envelope = buildBnbIntentTypedData({
|
|
763
919
|
from: this.wallet.address,
|
|
764
920
|
to,
|
|
765
921
|
amount: amountWei,
|
|
766
|
-
|
|
922
|
+
tokenAddress: tokenConfig.address,
|
|
767
923
|
service,
|
|
768
|
-
nonce:
|
|
769
|
-
|
|
770
|
-
deadline: Date.now() + 36e5
|
|
771
|
-
// 1 hour
|
|
772
|
-
};
|
|
773
|
-
const domain = {
|
|
774
|
-
name: "MoltsPay",
|
|
775
|
-
version: "1",
|
|
924
|
+
nonce: intentNonce,
|
|
925
|
+
deadline: intentDeadline,
|
|
776
926
|
chainId: chain.chainId
|
|
777
|
-
};
|
|
778
|
-
const types = {
|
|
779
|
-
PaymentIntent: [
|
|
780
|
-
{ name: "from", type: "address" },
|
|
781
|
-
{ name: "to", type: "address" },
|
|
782
|
-
{ name: "amount", type: "uint256" },
|
|
783
|
-
{ name: "token", type: "address" },
|
|
784
|
-
{ name: "service", type: "string" },
|
|
785
|
-
{ name: "nonce", type: "uint256" },
|
|
786
|
-
{ name: "deadline", type: "uint256" }
|
|
787
|
-
]
|
|
788
|
-
};
|
|
927
|
+
});
|
|
789
928
|
console.log(`[MoltsPay] Signing BNB payment intent...`);
|
|
790
|
-
const signature = await this.
|
|
929
|
+
const signature = await this.signer.signTypedData(envelope);
|
|
930
|
+
const intent = envelope.message;
|
|
791
931
|
const network = `eip155:${chain.chainId}`;
|
|
792
932
|
const payload = {
|
|
793
933
|
x402Version: 2,
|
|
@@ -848,11 +988,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
848
988
|
throw new Error("Missing payTo address in payment requirements");
|
|
849
989
|
}
|
|
850
990
|
const solanaFeePayer = requirements.extra?.solanaFeePayer;
|
|
851
|
-
const feePayerPubkey = solanaFeePayer ? new
|
|
991
|
+
const feePayerPubkey = solanaFeePayer ? new import_web35.PublicKey(solanaFeePayer) : void 0;
|
|
852
992
|
if (feePayerPubkey) {
|
|
853
993
|
console.log(`[MoltsPay] Gasless mode: server pays fees`);
|
|
854
994
|
}
|
|
855
|
-
const recipientPubkey = new
|
|
995
|
+
const recipientPubkey = new import_web35.PublicKey(requirements.payTo);
|
|
856
996
|
const transaction = await createSolanaPaymentTransaction(
|
|
857
997
|
solanaWallet.publicKey,
|
|
858
998
|
recipientPubkey,
|
|
@@ -861,12 +1001,11 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
861
1001
|
feePayerPubkey
|
|
862
1002
|
// Optional fee payer for gasless mode
|
|
863
1003
|
);
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
}
|
|
869
|
-
const signedTx = transaction.serialize({ requireAllSignatures: false }).toString("base64");
|
|
1004
|
+
const unsignedBase64 = transaction.serialize({ requireAllSignatures: false, verifySignatures: false }).toString("base64");
|
|
1005
|
+
const signedTx = await this.signer.signSolanaTransaction({
|
|
1006
|
+
transactionBase64: unsignedBase64,
|
|
1007
|
+
partialSign: !!feePayerPubkey
|
|
1008
|
+
});
|
|
870
1009
|
console.log(`[MoltsPay] Transaction signed, sending to server...`);
|
|
871
1010
|
const network = chain === "solana" ? "solana:mainnet" : "solana:devnet";
|
|
872
1011
|
const payload = {
|
|
@@ -913,7 +1052,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
913
1052
|
* Check ERC20 allowance for a spender
|
|
914
1053
|
*/
|
|
915
1054
|
async checkAllowance(tokenAddress, spender, provider) {
|
|
916
|
-
const contract = new
|
|
1055
|
+
const contract = new import_ethers2.ethers.Contract(
|
|
917
1056
|
tokenAddress,
|
|
918
1057
|
["function allowance(address owner, address spender) view returns (uint256)"],
|
|
919
1058
|
provider
|
|
@@ -924,41 +1063,29 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
924
1063
|
* Sign EIP-3009 transferWithAuthorization (GASLESS)
|
|
925
1064
|
* This only signs - no on-chain transaction, no gas needed.
|
|
926
1065
|
* Supports both USDC and USDT.
|
|
1066
|
+
*
|
|
1067
|
+
* Delegates typed-data construction to `core/eip3009.ts` and the signature
|
|
1068
|
+
* itself to `this.signer`. That way Web Client (Phase 4) can reuse the same
|
|
1069
|
+
* flow with an EIP-1193 signer without duplicating typed-data layout.
|
|
927
1070
|
*/
|
|
928
1071
|
async signEIP3009(to, amount, chain, token = "USDC", domainOverride) {
|
|
929
|
-
const validAfter = 0;
|
|
930
|
-
const validBefore = Math.floor(Date.now() / 1e3) + 3600;
|
|
931
|
-
const nonce = import_ethers.ethers.hexlify(import_ethers.ethers.randomBytes(32));
|
|
932
1072
|
const tokenConfig = chain.tokens[token];
|
|
933
1073
|
const value = BigInt(Math.floor(amount * 10 ** tokenConfig.decimals)).toString();
|
|
934
|
-
const
|
|
1074
|
+
const nonce = import_ethers2.ethers.hexlify(import_ethers2.ethers.randomBytes(32));
|
|
1075
|
+
const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
|
|
1076
|
+
const tokenVersion = domainOverride?.version || "2";
|
|
1077
|
+
const envelope = buildEIP3009TypedData({
|
|
935
1078
|
from: this.wallet.address,
|
|
936
1079
|
to,
|
|
937
1080
|
value,
|
|
938
|
-
|
|
939
|
-
validBefore: validBefore.toString(),
|
|
940
|
-
nonce
|
|
941
|
-
};
|
|
942
|
-
const tokenName = domainOverride?.name || tokenConfig.eip712Name || (token === "USDC" ? "USD Coin" : "Tether USD");
|
|
943
|
-
const tokenVersion = domainOverride?.version || "2";
|
|
944
|
-
const domain = {
|
|
945
|
-
name: tokenName,
|
|
946
|
-
version: tokenVersion,
|
|
1081
|
+
nonce,
|
|
947
1082
|
chainId: chain.chainId,
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
{ name: "value", type: "uint256" },
|
|
955
|
-
{ name: "validAfter", type: "uint256" },
|
|
956
|
-
{ name: "validBefore", type: "uint256" },
|
|
957
|
-
{ name: "nonce", type: "bytes32" }
|
|
958
|
-
]
|
|
959
|
-
};
|
|
960
|
-
const signature = await this.wallet.signTypedData(domain, types, authorization);
|
|
961
|
-
return { authorization, signature };
|
|
1083
|
+
tokenAddress: tokenConfig.address,
|
|
1084
|
+
tokenName,
|
|
1085
|
+
tokenVersion
|
|
1086
|
+
});
|
|
1087
|
+
const signature = await this.signer.signTypedData(envelope);
|
|
1088
|
+
return { authorization: envelope.message, signature };
|
|
962
1089
|
}
|
|
963
1090
|
/**
|
|
964
1091
|
* Check spending limits
|
|
@@ -1062,7 +1189,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
1062
1189
|
*/
|
|
1063
1190
|
static init(configDir, options) {
|
|
1064
1191
|
(0, import_fs2.mkdirSync)(configDir, { recursive: true });
|
|
1065
|
-
const wallet =
|
|
1192
|
+
const wallet = import_ethers2.Wallet.createRandom();
|
|
1066
1193
|
const walletData = {
|
|
1067
1194
|
address: wallet.address,
|
|
1068
1195
|
privateKey: wallet.privateKey,
|
|
@@ -1094,17 +1221,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
1094
1221
|
} catch {
|
|
1095
1222
|
throw new Error(`Unknown chain: ${this.config.chain}`);
|
|
1096
1223
|
}
|
|
1097
|
-
const provider = new
|
|
1224
|
+
const provider = new import_ethers2.ethers.JsonRpcProvider(chain.rpc);
|
|
1098
1225
|
const tokenAbi = ["function balanceOf(address) view returns (uint256)"];
|
|
1099
1226
|
const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
|
|
1100
1227
|
provider.getBalance(this.wallet.address),
|
|
1101
|
-
new
|
|
1102
|
-
new
|
|
1228
|
+
new import_ethers2.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
1229
|
+
new import_ethers2.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
|
|
1103
1230
|
]);
|
|
1104
1231
|
return {
|
|
1105
|
-
usdc: parseFloat(
|
|
1106
|
-
usdt: parseFloat(
|
|
1107
|
-
native: parseFloat(
|
|
1232
|
+
usdc: parseFloat(import_ethers2.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
|
|
1233
|
+
usdt: parseFloat(import_ethers2.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
|
|
1234
|
+
native: parseFloat(import_ethers2.ethers.formatEther(nativeBalance))
|
|
1108
1235
|
};
|
|
1109
1236
|
}
|
|
1110
1237
|
/**
|
|
@@ -1127,38 +1254,38 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
|
|
|
1127
1254
|
supportedChains.map(async (chainName) => {
|
|
1128
1255
|
try {
|
|
1129
1256
|
const chain = getChain(chainName);
|
|
1130
|
-
const provider = new
|
|
1257
|
+
const provider = new import_ethers2.ethers.JsonRpcProvider(chain.rpc);
|
|
1131
1258
|
if (chainName === "tempo_moderato") {
|
|
1132
1259
|
const [nativeBalance, pathUSD, alphaUSD, betaUSD, thetaUSD] = await Promise.all([
|
|
1133
1260
|
provider.getBalance(this.wallet.address),
|
|
1134
|
-
new
|
|
1135
|
-
new
|
|
1136
|
-
new
|
|
1137
|
-
new
|
|
1261
|
+
new import_ethers2.ethers.Contract(tempoTokens.pathUSD, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
1262
|
+
new import_ethers2.ethers.Contract(tempoTokens.alphaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
1263
|
+
new import_ethers2.ethers.Contract(tempoTokens.betaUSD, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
1264
|
+
new import_ethers2.ethers.Contract(tempoTokens.thetaUSD, tokenAbi, provider).balanceOf(this.wallet.address)
|
|
1138
1265
|
]);
|
|
1139
1266
|
results[chainName] = {
|
|
1140
|
-
usdc: parseFloat(
|
|
1267
|
+
usdc: parseFloat(import_ethers2.ethers.formatUnits(pathUSD, 6)),
|
|
1141
1268
|
// pathUSD as default USDC
|
|
1142
|
-
usdt: parseFloat(
|
|
1269
|
+
usdt: parseFloat(import_ethers2.ethers.formatUnits(alphaUSD, 6)),
|
|
1143
1270
|
// alphaUSD as default USDT
|
|
1144
|
-
native: parseFloat(
|
|
1271
|
+
native: parseFloat(import_ethers2.ethers.formatEther(nativeBalance)),
|
|
1145
1272
|
tempo: {
|
|
1146
|
-
pathUSD: parseFloat(
|
|
1147
|
-
alphaUSD: parseFloat(
|
|
1148
|
-
betaUSD: parseFloat(
|
|
1149
|
-
thetaUSD: parseFloat(
|
|
1273
|
+
pathUSD: parseFloat(import_ethers2.ethers.formatUnits(pathUSD, 6)),
|
|
1274
|
+
alphaUSD: parseFloat(import_ethers2.ethers.formatUnits(alphaUSD, 6)),
|
|
1275
|
+
betaUSD: parseFloat(import_ethers2.ethers.formatUnits(betaUSD, 6)),
|
|
1276
|
+
thetaUSD: parseFloat(import_ethers2.ethers.formatUnits(thetaUSD, 6))
|
|
1150
1277
|
}
|
|
1151
1278
|
};
|
|
1152
1279
|
} else {
|
|
1153
1280
|
const [nativeBalance, usdcBalance, usdtBalance] = await Promise.all([
|
|
1154
1281
|
provider.getBalance(this.wallet.address),
|
|
1155
|
-
new
|
|
1156
|
-
new
|
|
1282
|
+
new import_ethers2.ethers.Contract(chain.tokens.USDC.address, tokenAbi, provider).balanceOf(this.wallet.address),
|
|
1283
|
+
new import_ethers2.ethers.Contract(chain.tokens.USDT.address, tokenAbi, provider).balanceOf(this.wallet.address)
|
|
1157
1284
|
]);
|
|
1158
1285
|
results[chainName] = {
|
|
1159
|
-
usdc: parseFloat(
|
|
1160
|
-
usdt: parseFloat(
|
|
1161
|
-
native: parseFloat(
|
|
1286
|
+
usdc: parseFloat(import_ethers2.ethers.formatUnits(usdcBalance, chain.tokens.USDC.decimals)),
|
|
1287
|
+
usdt: parseFloat(import_ethers2.ethers.formatUnits(usdtBalance, chain.tokens.USDT.decimals)),
|
|
1288
|
+
native: parseFloat(import_ethers2.ethers.formatEther(nativeBalance))
|
|
1162
1289
|
};
|
|
1163
1290
|
}
|
|
1164
1291
|
} catch (err) {
|