moltspay 0.1.3 → 0.2.1

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/dist/index.mjs CHANGED
@@ -779,8 +779,372 @@ var SecureWallet = class {
779
779
  }
780
780
  };
781
781
 
782
- // src/permit/Permit.ts
782
+ // src/wallet/createWallet.ts
783
783
  import { ethers as ethers3 } from "ethers";
784
+ import { writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
785
+ import { join as join2, dirname } from "path";
786
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
787
+ var DEFAULT_STORAGE_DIR = join2(process.env.HOME || "~", ".moltspay");
788
+ var DEFAULT_STORAGE_FILE = "wallet.json";
789
+ function encryptPrivateKey(privateKey, password) {
790
+ const salt = randomBytes(16);
791
+ const key = scryptSync(password, salt, 32);
792
+ const iv = randomBytes(16);
793
+ const cipher = createCipheriv("aes-256-cbc", key, iv);
794
+ let encrypted = cipher.update(privateKey, "utf8", "hex");
795
+ encrypted += cipher.final("hex");
796
+ return {
797
+ encrypted,
798
+ iv: iv.toString("hex"),
799
+ salt: salt.toString("hex")
800
+ };
801
+ }
802
+ function decryptPrivateKey(encrypted, password, iv, salt) {
803
+ const key = scryptSync(password, Buffer.from(salt, "hex"), 32);
804
+ const decipher = createDecipheriv("aes-256-cbc", key, Buffer.from(iv, "hex"));
805
+ let decrypted = decipher.update(encrypted, "hex", "utf8");
806
+ decrypted += decipher.final("utf8");
807
+ return decrypted;
808
+ }
809
+ function createWallet(options = {}) {
810
+ const storagePath = options.storagePath || join2(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
811
+ if (existsSync2(storagePath) && !options.overwrite) {
812
+ try {
813
+ const existing = JSON.parse(readFileSync2(storagePath, "utf8"));
814
+ return {
815
+ success: true,
816
+ address: existing.address,
817
+ storagePath,
818
+ isNew: false
819
+ };
820
+ } catch (error) {
821
+ return {
822
+ success: false,
823
+ error: `Failed to load existing wallet: ${error.message}`
824
+ };
825
+ }
826
+ }
827
+ try {
828
+ const wallet = ethers3.Wallet.createRandom();
829
+ const walletData = {
830
+ address: wallet.address,
831
+ label: options.label,
832
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
833
+ encrypted: !!options.password,
834
+ privateKey: ""
835
+ };
836
+ if (options.password) {
837
+ const { encrypted, iv, salt } = encryptPrivateKey(wallet.privateKey, options.password);
838
+ walletData.privateKey = encrypted;
839
+ walletData.iv = iv;
840
+ walletData.salt = salt;
841
+ } else {
842
+ walletData.privateKey = wallet.privateKey;
843
+ }
844
+ const dir = dirname(storagePath);
845
+ if (!existsSync2(dir)) {
846
+ mkdirSync2(dir, { recursive: true });
847
+ }
848
+ writeFileSync(storagePath, JSON.stringify(walletData, null, 2), { mode: 384 });
849
+ return {
850
+ success: true,
851
+ address: wallet.address,
852
+ storagePath,
853
+ isNew: true
854
+ };
855
+ } catch (error) {
856
+ return {
857
+ success: false,
858
+ error: error.message
859
+ };
860
+ }
861
+ }
862
+ function loadWallet(options = {}) {
863
+ const storagePath = options.storagePath || join2(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
864
+ if (!existsSync2(storagePath)) {
865
+ return { success: false, error: "Wallet not found. Run createWallet() first." };
866
+ }
867
+ try {
868
+ const data = JSON.parse(readFileSync2(storagePath, "utf8"));
869
+ if (data.encrypted) {
870
+ if (!options.password) {
871
+ return { success: false, error: "Wallet is encrypted. Password required." };
872
+ }
873
+ const privateKey = decryptPrivateKey(data.privateKey, options.password, data.iv, data.salt);
874
+ return { success: true, address: data.address, privateKey };
875
+ } else {
876
+ return { success: true, address: data.address, privateKey: data.privateKey };
877
+ }
878
+ } catch (error) {
879
+ return { success: false, error: error.message };
880
+ }
881
+ }
882
+ function getWalletAddress(storagePath) {
883
+ const path2 = storagePath || join2(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
884
+ if (!existsSync2(path2)) {
885
+ return null;
886
+ }
887
+ try {
888
+ const data = JSON.parse(readFileSync2(path2, "utf8"));
889
+ return data.address;
890
+ } catch {
891
+ return null;
892
+ }
893
+ }
894
+ function walletExists(storagePath) {
895
+ const path2 = storagePath || join2(DEFAULT_STORAGE_DIR, DEFAULT_STORAGE_FILE);
896
+ return existsSync2(path2);
897
+ }
898
+
899
+ // src/wallet/PermitWallet.ts
900
+ import { ethers as ethers4 } from "ethers";
901
+ var PERMIT_ABI = [
902
+ ...ERC20_ABI,
903
+ "function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)",
904
+ "function transferFrom(address from, address to, uint256 amount) returns (bool)",
905
+ "function allowance(address owner, address spender) view returns (uint256)"
906
+ ];
907
+ var PermitWallet = class {
908
+ chain;
909
+ chainConfig;
910
+ address;
911
+ wallet;
912
+ provider;
913
+ usdcContract;
914
+ constructor(config = {}) {
915
+ this.chain = config.chain || "base_sepolia";
916
+ this.chainConfig = getChain(this.chain);
917
+ let privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
918
+ if (!privateKey && config.walletPath) {
919
+ const loaded = loadWallet({
920
+ storagePath: config.walletPath,
921
+ password: config.walletPassword
922
+ });
923
+ if (!loaded.success || !loaded.privateKey) {
924
+ throw new Error(loaded.error || "Failed to load wallet");
925
+ }
926
+ privateKey = loaded.privateKey;
927
+ }
928
+ if (!privateKey) {
929
+ throw new Error("privateKey is required. Set via config, env var, or walletPath.");
930
+ }
931
+ const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
932
+ this.provider = new ethers4.JsonRpcProvider(rpcUrl);
933
+ this.wallet = new ethers4.Wallet(privateKey, this.provider);
934
+ this.address = this.wallet.address;
935
+ this.usdcContract = new ethers4.Contract(
936
+ this.chainConfig.usdc,
937
+ PERMIT_ABI,
938
+ this.wallet
939
+ );
940
+ }
941
+ /**
942
+ * Check if Permit is valid (current allowance)
943
+ */
944
+ async checkPermitAllowance(owner) {
945
+ const allowance = await this.usdcContract.allowance(owner, this.address);
946
+ return (Number(allowance) / 1e6).toFixed(2);
947
+ }
948
+ /**
949
+ * Pay using Permit authorization
950
+ *
951
+ * Flow:
952
+ * 1. Call permit() to record Boss's authorization in the contract
953
+ * 2. Call transferFrom() to transfer from Boss's wallet to recipient
954
+ *
955
+ * @example
956
+ * ```typescript
957
+ * const wallet = new PermitWallet({ chain: 'base' });
958
+ *
959
+ * // Boss-signed permit data
960
+ * const permit = {
961
+ * owner: '0xBOSS...',
962
+ * spender: wallet.address,
963
+ * value: '10000000', // 10 USDC
964
+ * deadline: 1234567890,
965
+ * v: 27,
966
+ * r: '0x...',
967
+ * s: '0x...'
968
+ * };
969
+ *
970
+ * const result = await wallet.transferWithPermit({
971
+ * to: '0xSELLER...',
972
+ * amount: 3.99,
973
+ * permit
974
+ * });
975
+ * ```
976
+ */
977
+ async transferWithPermit(params) {
978
+ const { to, amount, permit } = params;
979
+ try {
980
+ const toAddress = ethers4.getAddress(to);
981
+ const ownerAddress = ethers4.getAddress(permit.owner);
982
+ if (ethers4.getAddress(permit.spender).toLowerCase() !== this.address.toLowerCase()) {
983
+ return {
984
+ success: false,
985
+ error: `Permit spender (${permit.spender}) doesn't match wallet address (${this.address})`
986
+ };
987
+ }
988
+ const now = Math.floor(Date.now() / 1e3);
989
+ if (permit.deadline < now) {
990
+ return {
991
+ success: false,
992
+ error: `Permit expired at ${new Date(permit.deadline * 1e3).toISOString()}`
993
+ };
994
+ }
995
+ const amountWei = BigInt(Math.floor(amount * 1e6));
996
+ const permitValue = BigInt(permit.value);
997
+ if (amountWei > permitValue) {
998
+ return {
999
+ success: false,
1000
+ error: `Permit value (${Number(permitValue) / 1e6} USDC) < transfer amount (${amount} USDC)`
1001
+ };
1002
+ }
1003
+ const currentAllowance = await this.usdcContract.allowance(ownerAddress, this.address);
1004
+ let permitTxHash;
1005
+ if (BigInt(currentAllowance) < amountWei) {
1006
+ console.log("Executing permit...");
1007
+ const permitTx = await this.usdcContract.permit(
1008
+ ownerAddress,
1009
+ this.address,
1010
+ permitValue,
1011
+ permit.deadline,
1012
+ permit.v,
1013
+ permit.r,
1014
+ permit.s
1015
+ );
1016
+ const permitReceipt = await permitTx.wait();
1017
+ if (permitReceipt.status !== 1) {
1018
+ return {
1019
+ success: false,
1020
+ error: "Permit transaction failed",
1021
+ permitTxHash: permitTx.hash
1022
+ };
1023
+ }
1024
+ permitTxHash = permitTx.hash;
1025
+ console.log("Permit executed:", permitTxHash);
1026
+ }
1027
+ console.log("Executing transferFrom...");
1028
+ const transferTx = await this.usdcContract.transferFrom(
1029
+ ownerAddress,
1030
+ toAddress,
1031
+ amountWei
1032
+ );
1033
+ const transferReceipt = await transferTx.wait();
1034
+ if (transferReceipt.status === 1) {
1035
+ return {
1036
+ success: true,
1037
+ tx_hash: transferTx.hash,
1038
+ permitTxHash,
1039
+ transferTxHash: transferTx.hash,
1040
+ from: ownerAddress,
1041
+ to: toAddress,
1042
+ amount,
1043
+ gas_used: Number(transferReceipt.gasUsed),
1044
+ block_number: transferReceipt.blockNumber,
1045
+ explorer_url: `${this.chainConfig.explorerTx}${transferTx.hash}`
1046
+ };
1047
+ } else {
1048
+ return {
1049
+ success: false,
1050
+ error: "TransferFrom transaction failed",
1051
+ tx_hash: transferTx.hash,
1052
+ permitTxHash
1053
+ };
1054
+ }
1055
+ } catch (error) {
1056
+ const message = error.message;
1057
+ if (message.includes("ERC20InsufficientAllowance")) {
1058
+ return {
1059
+ success: false,
1060
+ error: "Insufficient allowance. Permit may have been used or expired."
1061
+ };
1062
+ }
1063
+ if (message.includes("ERC20InsufficientBalance")) {
1064
+ return {
1065
+ success: false,
1066
+ error: "Boss wallet has insufficient USDC balance."
1067
+ };
1068
+ }
1069
+ if (message.includes("InvalidSignature") || message.includes("invalid signature")) {
1070
+ return {
1071
+ success: false,
1072
+ error: "Invalid permit signature. Ask Boss to re-sign."
1073
+ };
1074
+ }
1075
+ return {
1076
+ success: false,
1077
+ error: message
1078
+ };
1079
+ }
1080
+ }
1081
+ /**
1082
+ * Get ETH balance (for gas)
1083
+ */
1084
+ async getGasBalance() {
1085
+ const balance = await this.provider.getBalance(this.address);
1086
+ return ethers4.formatEther(balance);
1087
+ }
1088
+ /**
1089
+ * Check if there's enough gas
1090
+ */
1091
+ async hasEnoughGas(minEth = 1e-3) {
1092
+ const balance = await this.getGasBalance();
1093
+ return parseFloat(balance) >= minEth;
1094
+ }
1095
+ };
1096
+ function formatPermitRequest(params) {
1097
+ const { agentAddress, amount, deadlineHours = 24, chain = "base", reason } = params;
1098
+ const chainConfig = getChain(chain);
1099
+ const deadline = Math.floor(Date.now() / 1e3) + deadlineHours * 3600;
1100
+ const value = BigInt(Math.floor(amount * 1e6)).toString();
1101
+ return `\u{1F510} **USDC Spending Allowance Request**
1102
+
1103
+ ${reason ? `**Purpose:** ${reason}
1104
+ ` : ""}
1105
+ **Authorization Details:**
1106
+ - Authorized address (Agent): \`${agentAddress}\`
1107
+ - Amount: ${amount} USDC
1108
+ - Valid for: ${deadlineHours} hours
1109
+ - Chain: ${chainConfig.name}
1110
+
1111
+ **Please sign the following EIP-2612 Permit with your wallet:**
1112
+
1113
+ \`\`\`json
1114
+ {
1115
+ "types": {
1116
+ "Permit": [
1117
+ { "name": "owner", "type": "address" },
1118
+ { "name": "spender", "type": "address" },
1119
+ { "name": "value", "type": "uint256" },
1120
+ { "name": "nonce", "type": "uint256" },
1121
+ { "name": "deadline", "type": "uint256" }
1122
+ ]
1123
+ },
1124
+ "primaryType": "Permit",
1125
+ "domain": {
1126
+ "name": "USD Coin",
1127
+ "version": "2",
1128
+ "chainId": ${chainConfig.chainId},
1129
+ "verifyingContract": "${chainConfig.usdc}"
1130
+ },
1131
+ "message": {
1132
+ "owner": "<YOUR_WALLET_ADDRESS>",
1133
+ "spender": "${agentAddress}",
1134
+ "value": "${value}",
1135
+ "nonce": "<GET_FROM_CONTRACT>",
1136
+ "deadline": ${deadline}
1137
+ }
1138
+ }
1139
+ \`\`\`
1140
+
1141
+ After signing, send { v, r, s, deadline } to the Agent.
1142
+
1143
+ \u26A0\uFE0F Note: This authorization only allows the Agent to spend up to ${amount} USDC from your wallet. Your private key is never exposed.`;
1144
+ }
1145
+
1146
+ // src/permit/Permit.ts
1147
+ import { ethers as ethers5 } from "ethers";
784
1148
  var PermitPayment = class {
785
1149
  chain;
786
1150
  chainConfig;
@@ -793,13 +1157,13 @@ var PermitPayment = class {
793
1157
  this.chainConfig = getChain(this.chain);
794
1158
  this.spenderAddress = config.spenderAddress || process.env.PAYMENT_AGENT_WALLET || "";
795
1159
  const rpcUrl = config.rpcUrl || this.chainConfig.rpc;
796
- this.provider = new ethers3.JsonRpcProvider(rpcUrl);
1160
+ this.provider = new ethers5.JsonRpcProvider(rpcUrl);
797
1161
  const privateKey = config.privateKey || process.env.PAYMENT_AGENT_PRIVATE_KEY;
798
1162
  if (privateKey) {
799
- this.wallet = new ethers3.Wallet(privateKey, this.provider);
1163
+ this.wallet = new ethers5.Wallet(privateKey, this.provider);
800
1164
  this.spenderAddress = this.wallet.address;
801
1165
  }
802
- this.usdcContract = new ethers3.Contract(
1166
+ this.usdcContract = new ethers5.Contract(
803
1167
  this.chainConfig.usdc,
804
1168
  ERC20_ABI,
805
1169
  this.wallet || this.provider
@@ -950,7 +1314,7 @@ ${JSON.stringify(typed_data, null, 2)}
950
1314
  };
951
1315
 
952
1316
  // src/orders/index.ts
953
- import { randomBytes } from "crypto";
1317
+ import { randomBytes as randomBytes2 } from "crypto";
954
1318
  var MemoryOrderStore = class {
955
1319
  orders = /* @__PURE__ */ new Map();
956
1320
  async get(orderId) {
@@ -989,7 +1353,7 @@ var OrderManager = class {
989
1353
  * 生成订单ID
990
1354
  */
991
1355
  generateOrderId() {
992
- return "vo_" + randomBytes(4).toString("hex");
1356
+ return "vo_" + randomBytes2(4).toString("hex");
993
1357
  }
994
1358
  /**
995
1359
  * 创建订单
@@ -1082,8 +1446,8 @@ var OrderManager = class {
1082
1446
  };
1083
1447
 
1084
1448
  // src/verify/index.ts
1085
- import { ethers as ethers4 } from "ethers";
1086
- var TRANSFER_EVENT_TOPIC = ethers4.id("Transfer(address,address,uint256)");
1449
+ import { ethers as ethers6 } from "ethers";
1450
+ var TRANSFER_EVENT_TOPIC = ethers6.id("Transfer(address,address,uint256)");
1087
1451
  async function verifyPayment(params) {
1088
1452
  const { txHash, expectedAmount, expectedTo } = params;
1089
1453
  let chain;
@@ -1100,7 +1464,7 @@ async function verifyPayment(params) {
1100
1464
  return { verified: false, error: `\u4E0D\u652F\u6301\u7684\u94FE: ${params.chain}` };
1101
1465
  }
1102
1466
  try {
1103
- const provider = new ethers4.JsonRpcProvider(chain.rpc);
1467
+ const provider = new ethers6.JsonRpcProvider(chain.rpc);
1104
1468
  const receipt = await provider.getTransactionReceipt(txHash);
1105
1469
  if (!receipt) {
1106
1470
  return { verified: false, error: "\u4EA4\u6613\u672A\u627E\u5230\u6216\u672A\u786E\u8BA4" };
@@ -1160,7 +1524,7 @@ async function getTransactionStatus(txHash, chain = "base") {
1160
1524
  return { status: "not_found" };
1161
1525
  }
1162
1526
  try {
1163
- const provider = new ethers4.JsonRpcProvider(chainConfig.rpc);
1527
+ const provider = new ethers6.JsonRpcProvider(chainConfig.rpc);
1164
1528
  const receipt = await provider.getTransactionReceipt(txHash);
1165
1529
  if (!receipt) {
1166
1530
  const tx = await provider.getTransaction(txHash);
@@ -1197,7 +1561,7 @@ async function waitForTransaction(txHash, chain = "base", confirmations = 1, tim
1197
1561
  } catch (e) {
1198
1562
  return { verified: false, confirmed: false, error: `\u4E0D\u652F\u6301\u7684\u94FE: ${chain}` };
1199
1563
  }
1200
- const provider = new ethers4.JsonRpcProvider(chainConfig.rpc);
1564
+ const provider = new ethers6.JsonRpcProvider(chainConfig.rpc);
1201
1565
  try {
1202
1566
  const receipt = await provider.waitForTransaction(txHash, confirmations, timeoutMs);
1203
1567
  if (!receipt) {
@@ -1225,131 +1589,131 @@ function generatePaymentGuide(params) {
1225
1589
  price,
1226
1590
  recipientAddress,
1227
1591
  chain = "base",
1228
- serviceName = "\u89C6\u9891\u751F\u6210\u670D\u52A1"
1592
+ serviceName = "Video Generation Service"
1229
1593
  } = params;
1230
1594
  const chainId = chain === "base" ? 8453 : 84532;
1231
- const chainName = chain === "base" ? "Base \u4E3B\u7F51" : "Base Sepolia \u6D4B\u8BD5\u7F51";
1595
+ const chainName = chain === "base" ? "Base Mainnet" : "Base Sepolia Testnet";
1232
1596
  const explorer = chain === "base" ? "https://basescan.org/tx/" : "https://sepolia.basescan.org/tx/";
1233
1597
  return `\u{1F3AC} **${serviceName}**
1234
1598
 
1235
- \u{1F4CB} **\u8BA2\u5355\u4FE1\u606F:**
1236
- - \u8BA2\u5355\u53F7: \`${orderId}\`
1237
- - \u5185\u5BB9: ${prompt.length > 50 ? prompt.slice(0, 50) + "..." : prompt}
1238
- - \u4EF7\u683C: **${price} USDC**
1239
- - \u94FE: ${chainName}
1599
+ \u{1F4CB} **Order Details:**
1600
+ - Order ID: \`${orderId}\`
1601
+ - Content: ${prompt.length > 50 ? prompt.slice(0, 50) + "..." : prompt}
1602
+ - Price: **${price} USDC**
1603
+ - Chain: ${chainName}
1240
1604
 
1241
1605
  ---
1242
1606
 
1243
- ## \u{1F4B3} \u5982\u4F55\u652F\u4ED8
1607
+ ## \u{1F4B3} How to Pay
1244
1608
 
1245
- ### \u65B9\u5F0F\u4E00\uFF1A\u5B89\u88C5 moltspay SDK\uFF08\u63A8\u8350\uFF09
1609
+ ### Option 1: Install moltspay SDK (Recommended)
1246
1610
 
1247
1611
  \`\`\`bash
1248
1612
  npm install moltspay ethers
1249
1613
  \`\`\`
1250
1614
 
1251
- **\u521B\u5EFA\u94B1\u5305\u5E76\u652F\u4ED8\uFF1A**
1615
+ **Create wallet and pay:**
1252
1616
 
1253
1617
  \`\`\`javascript
1254
1618
  const { SecureWallet, getChain } = require('moltspay');
1255
1619
  const { Wallet } = require('ethers');
1256
1620
 
1257
- // 1. \u521B\u5EFA\u65B0\u94B1\u5305\uFF08\u9996\u6B21\u4F7F\u7528\uFF09
1621
+ // 1. Create new wallet (first time)
1258
1622
  const newWallet = Wallet.createRandom();
1259
- console.log('\u94B1\u5305\u5730\u5740:', newWallet.address);
1260
- console.log('\u79C1\u94A5\uFF08\u8BF7\u5B89\u5168\u4FDD\u5B58\uFF09:', newWallet.privateKey);
1623
+ console.log('Wallet address:', newWallet.address);
1624
+ console.log('Private key (save securely):', newWallet.privateKey);
1261
1625
 
1262
- // 2. \u6216\u4F7F\u7528\u5DF2\u6709\u94B1\u5305
1626
+ // 2. Or use existing wallet
1263
1627
  const wallet = new SecureWallet({
1264
1628
  chainId: ${chainId},
1265
- privateKey: process.env.WALLET_KEY, // \u4F60\u7684\u79C1\u94A5
1266
- limits: { requireWhitelist: false } // \u5141\u8BB8\u5411\u4EFB\u610F\u5730\u5740\u8F6C\u8D26
1629
+ privateKey: process.env.WALLET_KEY, // Your private key
1630
+ limits: { requireWhitelist: false } // Allow transfers to any address
1267
1631
  });
1268
1632
 
1269
- // 3. \u67E5\u770B\u4F59\u989D
1633
+ // 3. Check balance
1270
1634
  const balance = await wallet.getBalance();
1271
- console.log('USDC\u4F59\u989D:', balance.usdc);
1635
+ console.log('USDC balance:', balance.usdc);
1272
1636
 
1273
- // 4. \u652F\u4ED8
1637
+ // 4. Pay
1274
1638
  const result = await wallet.transfer('${recipientAddress}', '${price}');
1275
1639
  if (result.success) {
1276
- console.log('\u2705 \u652F\u4ED8\u6210\u529F\uFF01');
1277
- console.log('\u4EA4\u6613hash:', result.hash);
1278
- // \u628A\u4EA4\u6613hash\u53D1\u7ED9\u6211
1640
+ console.log('\u2705 Payment successful!');
1641
+ console.log('Tx hash:', result.hash);
1642
+ // Send the tx hash to me
1279
1643
  }
1280
1644
  \`\`\`
1281
1645
 
1282
- ### \u65B9\u5F0F\u4E8C\uFF1A\u76F4\u63A5\u8F6C\u8D26
1646
+ ### Option 2: Direct Transfer
1283
1647
 
1284
- \u4ECE\u4EFB\u610F\u94B1\u5305\u8F6C\u8D26 USDC\uFF1A
1285
- - **\u94FE:** ${chainName} (Chain ID: ${chainId})
1648
+ Transfer USDC from any wallet:
1649
+ - **Chain:** ${chainName} (Chain ID: ${chainId})
1286
1650
  - **Token:** USDC
1287
- - **\u6536\u6B3E\u5730\u5740:** \`${recipientAddress}\`
1288
- - **\u91D1\u989D:** ${price} USDC
1651
+ - **Recipient:** \`${recipientAddress}\`
1652
+ - **Amount:** ${price} USDC
1289
1653
 
1290
1654
  ---
1291
1655
 
1292
- ## \u2705 \u652F\u4ED8\u5B8C\u6210\u540E
1656
+ ## \u2705 After Payment
1293
1657
 
1294
- \u8BF7\u53D1\u9001**\u4EA4\u6613hash**\u7ED9\u6211\uFF0C\u683C\u5F0F\uFF1A
1658
+ Send me the **transaction hash**, format:
1295
1659
  \`\`\`
1296
1660
  tx: 0x1234...
1297
1661
  \`\`\`
1298
1662
 
1299
- \u6216\u76F4\u63A5\u53D1\u9001hash\uFF1A
1663
+ Or just send the hash directly:
1300
1664
  \`\`\`
1301
1665
  0x1234abcd...
1302
1666
  \`\`\`
1303
1667
 
1304
- \u6211\u4F1A\u9A8C\u8BC1\u94FE\u4E0A\u4EA4\u6613\u540E\u5F00\u59CB\u5904\u7406\u4F60\u7684\u8BF7\u6C42\u3002
1668
+ I'll verify the on-chain transaction and start processing your request.
1305
1669
 
1306
1670
  ---
1307
1671
 
1308
1672
  \u{1F4E6} **moltspay:** https://www.npmjs.com/package/moltspay
1309
- \u{1F517} **\u533A\u5757\u6D4F\u89C8\u5668:** ${explorer}`;
1673
+ \u{1F517} **Block Explorer:** ${explorer}`;
1310
1674
  }
1311
1675
  function generatePaymentReminder(params) {
1312
1676
  const { orderId, price, recipientAddress, chain = "base" } = params;
1313
1677
  const chainName = chain === "base" ? "Base" : "Base Sepolia";
1314
- return `\u23F3 **\u5F85\u652F\u4ED8\u8BA2\u5355: ${orderId}**
1678
+ return `\u23F3 **Pending Order: ${orderId}**
1315
1679
 
1316
- \u8BF7\u652F\u4ED8 **${price} USDC** \u5230:
1317
- - \u94FE: ${chainName}
1318
- - \u5730\u5740: \`${recipientAddress}\`
1680
+ Please pay **${price} USDC** to:
1681
+ - Chain: ${chainName}
1682
+ - Address: \`${recipientAddress}\`
1319
1683
 
1320
- \u652F\u4ED8\u540E\u53D1\u9001\u4EA4\u6613hash\u7ED9\u6211\u3002
1684
+ Send the tx hash after payment.
1321
1685
 
1322
1686
  ---
1323
- \u{1F4A1} \u6CA1\u6709\u652F\u4ED8\u80FD\u529B\uFF1F\u8FD0\u884C \`npm install moltspay\` \u83B7\u53D6\u652F\u4ED8SDK`;
1687
+ \u{1F4A1} No payment capability? Run \`npm install moltspay\` to get the payment SDK`;
1324
1688
  }
1325
1689
  function generateWalletGuide() {
1326
- return `## \u{1F510} \u521B\u5EFA\u652F\u4ED8\u94B1\u5305
1690
+ return `## \u{1F510} Create Payment Wallet
1327
1691
 
1328
- **1. \u5B89\u88C5 moltspay:**
1692
+ **1. Install moltspay:**
1329
1693
  \`\`\`bash
1330
1694
  npm install moltspay ethers
1331
1695
  \`\`\`
1332
1696
 
1333
- **2. \u521B\u5EFA\u65B0\u94B1\u5305:**
1697
+ **2. Create new wallet:**
1334
1698
  \`\`\`javascript
1335
1699
  const { Wallet } = require('ethers');
1336
1700
 
1337
1701
  const wallet = Wallet.createRandom();
1338
- console.log('\u5730\u5740:', wallet.address);
1339
- console.log('\u79C1\u94A5:', wallet.privateKey);
1340
- // \u26A0\uFE0F \u8BF7\u5B89\u5168\u4FDD\u5B58\u79C1\u94A5\uFF01
1702
+ console.log('Address:', wallet.address);
1703
+ console.log('Private key:', wallet.privateKey);
1704
+ // \u26A0\uFE0F Save the private key securely!
1341
1705
  \`\`\`
1342
1706
 
1343
- **3. \u83B7\u53D6 USDC:**
1344
- - \u4E3B\u7F51: \u4ECE\u4EA4\u6613\u6240\u63D0\u5E01\u5230\u4F60\u7684\u94B1\u5305\u5730\u5740
1345
- - \u6D4B\u8BD5\u7F51: \u4F7F\u7528 Base Sepolia faucet
1707
+ **3. Get USDC:**
1708
+ - Mainnet: Withdraw from exchange to your wallet
1709
+ - Testnet: Use Base Sepolia faucet
1346
1710
 
1347
- **4. \u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF:**
1711
+ **4. Set environment variable:**
1348
1712
  \`\`\`bash
1349
- export WALLET_KEY="\u4F60\u7684\u79C1\u94A5"
1713
+ export WALLET_KEY="your_private_key"
1350
1714
  \`\`\`
1351
1715
 
1352
- \u5B8C\u6210\u540E\u5373\u53EF\u4F7F\u7528 moltspay \u8FDB\u884C\u94FE\u4E0A\u652F\u4ED8\uFF01`;
1716
+ You're now ready to make on-chain payments with moltspay!`;
1353
1717
  }
1354
1718
  function extractTransactionHash(message) {
1355
1719
  const match = message.match(/0x[a-fA-F0-9]{64}/);
@@ -1361,26 +1725,458 @@ function extractTransactionHash(message) {
1361
1725
  function hasTransactionHash(message) {
1362
1726
  return extractTransactionHash(message) !== null;
1363
1727
  }
1728
+
1729
+ // src/receipt/index.ts
1730
+ function generateInvoiceId() {
1731
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
1732
+ const random = Math.random().toString(36).slice(2, 8).toUpperCase();
1733
+ return `INV-${date}-${random}`;
1734
+ }
1735
+ function generateReceipt(params) {
1736
+ const chainConfig = getChain(params.chain);
1737
+ return {
1738
+ type: "receipt",
1739
+ version: "1.0",
1740
+ invoiceId: params.invoiceId || generateInvoiceId(),
1741
+ orderId: params.orderId,
1742
+ service: params.service,
1743
+ description: params.description,
1744
+ amount: params.amount.toFixed(2),
1745
+ token: params.token || "USDC",
1746
+ chain: chainConfig.name,
1747
+ chainId: chainConfig.chainId,
1748
+ txHash: params.txHash,
1749
+ txUrl: `${chainConfig.explorerTx}${params.txHash}`,
1750
+ payer: params.payerAddress,
1751
+ recipient: params.recipientAddress,
1752
+ paidAt: (/* @__PURE__ */ new Date()).toISOString(),
1753
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
1754
+ delivery: params.delivery,
1755
+ metadata: params.metadata
1756
+ };
1757
+ }
1758
+ function generateReceiptFromInvoice(invoice, verifyResult, delivery) {
1759
+ if (!verifyResult.verified || !verifyResult.tx_hash) {
1760
+ throw new Error("Cannot generate receipt: payment not verified");
1761
+ }
1762
+ return generateReceipt({
1763
+ orderId: invoice.order_id,
1764
+ service: invoice.service,
1765
+ description: invoice.description,
1766
+ amount: parseFloat(invoice.amount),
1767
+ token: invoice.token,
1768
+ chain: invoice.chain,
1769
+ txHash: verifyResult.tx_hash,
1770
+ payerAddress: verifyResult.from || "unknown",
1771
+ recipientAddress: invoice.recipient,
1772
+ delivery
1773
+ });
1774
+ }
1775
+ function formatReceiptMessage(receipt) {
1776
+ let msg = `\u{1F9FE} **Transaction Receipt**
1777
+
1778
+ **Invoice:** \`${receipt.invoiceId}\`
1779
+ **Order:** \`${receipt.orderId}\`
1780
+
1781
+ ---
1782
+
1783
+ **Service:** ${receipt.service}
1784
+ ${receipt.description ? `**Description:** ${receipt.description}
1785
+ ` : ""}
1786
+ **Amount:** ${receipt.amount} ${receipt.token}
1787
+ **Chain:** ${receipt.chain} (Chain ID: ${receipt.chainId})
1788
+
1789
+ ---
1790
+
1791
+ **Payer:** \`${receipt.payer}\`
1792
+ **Recipient:** \`${receipt.recipient}\`
1793
+ **Transaction:** [\`${receipt.txHash.slice(0, 10)}...${receipt.txHash.slice(-8)}\`](${receipt.txUrl})
1794
+ **Paid at:** ${receipt.paidAt}`;
1795
+ if (receipt.delivery) {
1796
+ msg += `
1797
+
1798
+ ---
1799
+
1800
+ **Delivery Info:**`;
1801
+ if (receipt.delivery.url) {
1802
+ msg += `
1803
+ - Download: ${receipt.delivery.url}`;
1804
+ }
1805
+ if (receipt.delivery.fileHash) {
1806
+ msg += `
1807
+ - Checksum: \`${receipt.delivery.fileHash}\``;
1808
+ }
1809
+ if (receipt.delivery.deliveredAt) {
1810
+ msg += `
1811
+ - Delivered at: ${receipt.delivery.deliveredAt}`;
1812
+ }
1813
+ }
1814
+ msg += `
1815
+
1816
+ ---
1817
+
1818
+ _Receipt issued: ${receipt.issuedAt}_`;
1819
+ return msg;
1820
+ }
1821
+ function formatReceiptText(receipt) {
1822
+ let msg = `\u{1F9FE} Transaction Receipt
1823
+
1824
+ Invoice: ${receipt.invoiceId}
1825
+ Order: ${receipt.orderId}
1826
+
1827
+ Service: ${receipt.service}
1828
+ Amount: ${receipt.amount} ${receipt.token}
1829
+ Chain: ${receipt.chain}
1830
+
1831
+ Payer: ${receipt.payer}
1832
+ Recipient: ${receipt.recipient}
1833
+ Tx: ${receipt.txHash}
1834
+ Explorer: ${receipt.txUrl}
1835
+ Paid at: ${receipt.paidAt}`;
1836
+ if (receipt.delivery) {
1837
+ msg += `
1838
+
1839
+ Delivery:`;
1840
+ if (receipt.delivery.url) {
1841
+ msg += `
1842
+ Download: ${receipt.delivery.url}`;
1843
+ }
1844
+ if (receipt.delivery.fileHash) {
1845
+ msg += `
1846
+ Checksum: ${receipt.delivery.fileHash}`;
1847
+ }
1848
+ }
1849
+ return msg;
1850
+ }
1851
+ function formatReceiptJson(receipt) {
1852
+ return JSON.stringify(receipt, null, 2);
1853
+ }
1854
+
1855
+ // src/templates/index.ts
1856
+ var SellerTemplates = {
1857
+ /**
1858
+ * 1. Capability Check - Ask if buyer has payment capability
1859
+ */
1860
+ askPaymentCapability() {
1861
+ return `Sure. First, let me confirm: do you currently have the ability to pay USDC on-chain (have a wallet and available funds/allowance)?`;
1862
+ },
1863
+ /**
1864
+ * 2. Settlement Method - Explain how we settle
1865
+ */
1866
+ explainSettlement(chain = "base") {
1867
+ const chainConfig = getChain(chain);
1868
+ return `Our settlement method is: USDC (on-chain, ${chainConfig.name}). You just need two things:
1869
+ 1) A wallet address;
1870
+ 2) USDC in your wallet, or a spending allowance from your owner (allowance is recommended).
1871
+ Can you follow my guidance to set this up?`;
1872
+ },
1873
+ /**
1874
+ * 3.1 Installation Guide - First step: install SDK
1875
+ */
1876
+ guideInstall() {
1877
+ return `Step 1: Install the payment module in your environment. Run: \`npm install moltspay ethers\`.
1878
+ After installation, create a wallet address and send it to me (address only, never send private key/mnemonic).`;
1879
+ },
1880
+ /**
1881
+ * 3.2 Confirm Wallet Address
1882
+ */
1883
+ confirmWalletAddress(address) {
1884
+ return `Got it. Reminder: never send your private key/mnemonic to anyone (including me).
1885
+ Your wallet address is \`${address}\`.
1886
+ Step 2: You need your owner (Boss) to give you payment capability.`;
1887
+ },
1888
+ /**
1889
+ * 3.3 Funding Options - Choose method
1890
+ */
1891
+ guideFunding() {
1892
+ return `You now have two ways to get payment capability:
1893
+ A) Have Boss transfer USDC directly to your wallet (simple, but you'll hold the funds);
1894
+ B) Have Boss give you a "USDC spending allowance" (recommended: you don't hold funds, can only pay within the allowance).
1895
+ Which works better for you?`;
1896
+ },
1897
+ /**
1898
+ * 3.4 Guide Permit Authorization
1899
+ */
1900
+ guidePermit(agentAddress, suggestedAmount = 10) {
1901
+ return `OK. Please send this message to your Boss (you can copy it directly):
1902
+ "I need a USDC spending allowance (Permit) for purchasing services. Please authorize ${suggestedAmount} USDC to my wallet ${agentAddress}, valid for 24 hours."
1903
+ Let me know when you've received the allowance.`;
1904
+ },
1905
+ /**
1906
+ * 3.5 Confirm Capability Ready
1907
+ */
1908
+ confirmCapabilityReady() {
1909
+ return `Great. You now have the payment capability to complete transactions. Let's confirm your service requirements.`;
1910
+ },
1911
+ /**
1912
+ * 4. Ask Service Requirements
1913
+ */
1914
+ askServiceRequirements(serviceName = "video") {
1915
+ return `Please tell me your ${serviceName} specifications: theme/style, duration, resolution, subtitles or voiceover needed?`;
1916
+ },
1917
+ /**
1918
+ * 5. Quote
1919
+ */
1920
+ quote(params) {
1921
+ const { service, price, recipientAddress, chain = "base" } = params;
1922
+ const chainConfig = getChain(chain);
1923
+ return `Quote:
1924
+ - Service: ${service}
1925
+ - Price: ${price} USDC
1926
+ - Chain: ${chainConfig.name}
1927
+ - Recipient: \`${recipientAddress}\`
1928
+ - After payment, send me the transaction hash (tx hash) for on-chain verification
1929
+
1930
+ Do you confirm the purchase and pay now?`;
1931
+ },
1932
+ /**
1933
+ * 7. Verifying
1934
+ */
1935
+ verifying() {
1936
+ return `I'm verifying the transaction on-chain to confirm receipt and amount.`;
1937
+ },
1938
+ /**
1939
+ * 7. Verification Passed
1940
+ */
1941
+ verificationPassed(amount) {
1942
+ return `Verification passed: received ${amount} USDC. Starting to process your request now.
1943
+ [status:payment_confirmed]`;
1944
+ },
1945
+ /**
1946
+ * 7. Verification Failed
1947
+ */
1948
+ verificationFailed(error) {
1949
+ return `Verification failed: ${error}
1950
+ Please check if the transaction is correct, or resend the correct tx hash.`;
1951
+ },
1952
+ /**
1953
+ * 8. Delivery
1954
+ */
1955
+ deliver(params) {
1956
+ const { downloadUrl, fileHash } = params;
1957
+ let msg = `Service completed. Delivery details:
1958
+ - Download link: ${downloadUrl}`;
1959
+ if (fileHash) {
1960
+ msg += `
1961
+ - File checksum: SHA256=${fileHash}`;
1962
+ }
1963
+ msg += `
1964
+
1965
+ Let me know if you have any download issues, I'll provide a backup link.
1966
+ [status:delivered]`;
1967
+ return msg;
1968
+ },
1969
+ /**
1970
+ * 9. Receipt
1971
+ */
1972
+ receipt(receipt) {
1973
+ return `Here's your transaction receipt (you can forward to Boss for reconciliation):
1974
+ - Invoice: ${receipt.invoiceId}
1975
+ - Service: ${receipt.service}
1976
+ - Amount: ${receipt.amount} ${receipt.token}
1977
+ - Tx Hash: ${receipt.txHash}
1978
+ - Recipient: ${receipt.recipient}
1979
+ ${receipt.delivery?.url ? `- Delivery: ${receipt.delivery.url}` : ""}
1980
+ [status:receipt_issued invoice=${receipt.invoiceId}]`;
1981
+ },
1982
+ /**
1983
+ * 10. End
1984
+ */
1985
+ end() {
1986
+ return `Thank you for your purchase. This transaction is complete. Feel free to reach out if you need modifications or new services.`;
1987
+ }
1988
+ };
1989
+ var BuyerTemplates = {
1990
+ /**
1991
+ * 0. Request Service
1992
+ */
1993
+ requestService(service) {
1994
+ return `I'd like to purchase a ${service} service.`;
1995
+ },
1996
+ /**
1997
+ * 1. No Capability
1998
+ */
1999
+ noCapability() {
2000
+ return `I'm not sure. I currently don't have any payment tools installed, and no wallet.`;
2001
+ },
2002
+ /**
2003
+ * 1. Has Capability
2004
+ */
2005
+ hasCapability(balance) {
2006
+ return `Yes, I have a wallet and available USDC${balance ? ` (balance: ${balance})` : ""}.`;
2007
+ },
2008
+ /**
2009
+ * 2. Agree to Guide
2010
+ */
2011
+ agreeToGuide() {
2012
+ return `Sure, please guide me.`;
2013
+ },
2014
+ /**
2015
+ * 3.1 Report Wallet Created
2016
+ */
2017
+ walletCreated(address) {
2018
+ return `I've created my wallet address: ${address}.
2019
+ [status:wallet_ready]`;
2020
+ },
2021
+ /**
2022
+ * 3.3 Choose Permit Method
2023
+ */
2024
+ choosePermit() {
2025
+ return `I choose B: have Boss give me an allowance (Permit).`;
2026
+ },
2027
+ /**
2028
+ * 3.3 Choose Direct Transfer
2029
+ */
2030
+ chooseDirectTransfer() {
2031
+ return `I choose A: have Boss transfer USDC directly to my wallet.`;
2032
+ },
2033
+ /**
2034
+ * 3.4 Report Permit Received
2035
+ */
2036
+ permitReceived(amount) {
2037
+ return `Boss has completed the authorization. I now have ${amount} USDC allowance.
2038
+ [status:permit_ready USDC=${amount}]`;
2039
+ },
2040
+ /**
2041
+ * 4. Submit Requirements
2042
+ */
2043
+ submitRequirements(requirements) {
2044
+ return `Requirements:
2045
+ ${requirements}`;
2046
+ },
2047
+ /**
2048
+ * 5. Confirm Purchase
2049
+ */
2050
+ confirmPurchase() {
2051
+ return `Confirmed. I'll pay now.`;
2052
+ },
2053
+ /**
2054
+ * 6. Report Payment Sent
2055
+ */
2056
+ paymentSent(txHash, amount) {
2057
+ return `Payment complete. Transaction hash: ${txHash}.
2058
+ [status:payment_sent tx=${txHash} amount=${amount} USDC]`;
2059
+ },
2060
+ /**
2061
+ * 8. Confirm Delivery Received
2062
+ */
2063
+ deliveryReceived() {
2064
+ return `Received, I'm downloading and checking now.`;
2065
+ },
2066
+ /**
2067
+ * 9. Confirm Receipt
2068
+ */
2069
+ receiptReceived() {
2070
+ return `Receipt received, service complete. Thanks!`;
2071
+ },
2072
+ /**
2073
+ * Request Permit from Boss
2074
+ */
2075
+ requestPermitFromBoss(params) {
2076
+ const { amount, agentAddress, deadlineHours = 24, reason } = params;
2077
+ return `Boss, I need a USDC spending allowance (Permit) for ${reason || "purchasing services"}.
2078
+ Please authorize ${amount} USDC to my wallet ${agentAddress}, valid for ${deadlineHours} hours.`;
2079
+ }
2080
+ };
2081
+ var StatusMarkers = {
2082
+ walletReady: "[status:wallet_ready]",
2083
+ permitReady: (amount) => `[status:permit_ready USDC=${amount}]`,
2084
+ paymentSent: (txHash, amount) => `[status:payment_sent tx=${txHash} amount=${amount} USDC]`,
2085
+ paymentConfirmed: (txHash) => `[status:payment_confirmed tx=${txHash}]`,
2086
+ delivered: (url, hash) => `[status:delivered url=${url}${hash ? ` hash=${hash}` : ""}]`,
2087
+ receiptIssued: (invoiceId, txHash) => `[status:receipt_issued invoice=${invoiceId} tx=${txHash}]`
2088
+ };
2089
+ function parseStatusMarker(message) {
2090
+ const match = message.match(/\[status:([^\]]+)\]/);
2091
+ if (!match) return null;
2092
+ const content = match[1];
2093
+ if (content === "wallet_ready") {
2094
+ return { type: "wallet_ready", data: {} };
2095
+ }
2096
+ if (content.startsWith("permit_ready")) {
2097
+ const amountMatch = content.match(/USDC=(\d+(?:\.\d+)?)/);
2098
+ return {
2099
+ type: "permit_ready",
2100
+ data: { amount: amountMatch?.[1] || "0" }
2101
+ };
2102
+ }
2103
+ if (content.startsWith("payment_sent")) {
2104
+ const txMatch = content.match(/tx=(\S+)/);
2105
+ const amountMatch = content.match(/amount=(\d+(?:\.\d+)?)/);
2106
+ return {
2107
+ type: "payment_sent",
2108
+ data: {
2109
+ txHash: txMatch?.[1] || "",
2110
+ amount: amountMatch?.[1] || "0"
2111
+ }
2112
+ };
2113
+ }
2114
+ if (content.startsWith("payment_confirmed")) {
2115
+ const txMatch = content.match(/tx=(\S+)/);
2116
+ return {
2117
+ type: "payment_confirmed",
2118
+ data: { txHash: txMatch?.[1] || "" }
2119
+ };
2120
+ }
2121
+ if (content.startsWith("delivered")) {
2122
+ const urlMatch = content.match(/url=(\S+)/);
2123
+ const hashMatch = content.match(/hash=(\S+)/);
2124
+ return {
2125
+ type: "delivered",
2126
+ data: {
2127
+ url: urlMatch?.[1] || "",
2128
+ hash: hashMatch?.[1] || ""
2129
+ }
2130
+ };
2131
+ }
2132
+ if (content.startsWith("receipt_issued")) {
2133
+ const invoiceMatch = content.match(/invoice=(\S+)/);
2134
+ const txMatch = content.match(/tx=(\S+)/);
2135
+ return {
2136
+ type: "receipt_issued",
2137
+ data: {
2138
+ invoiceId: invoiceMatch?.[1] || "",
2139
+ txHash: txMatch?.[1] || ""
2140
+ }
2141
+ };
2142
+ }
2143
+ return { type: "unknown", data: { raw: content } };
2144
+ }
1364
2145
  export {
1365
2146
  AuditLog,
2147
+ BuyerTemplates,
1366
2148
  CHAINS,
1367
2149
  ERC20_ABI,
1368
2150
  MemoryOrderStore,
1369
2151
  OrderManager,
1370
2152
  PaymentAgent,
1371
2153
  PermitPayment,
2154
+ PermitWallet,
1372
2155
  SecureWallet,
2156
+ SellerTemplates,
2157
+ StatusMarkers,
1373
2158
  Wallet,
2159
+ createWallet,
1374
2160
  extractTransactionHash,
2161
+ formatPermitRequest,
2162
+ formatReceiptJson,
2163
+ formatReceiptMessage,
2164
+ formatReceiptText,
1375
2165
  generatePaymentGuide,
1376
2166
  generatePaymentReminder,
2167
+ generateReceipt,
2168
+ generateReceiptFromInvoice,
1377
2169
  generateWalletGuide,
1378
2170
  getChain,
1379
2171
  getChainById,
1380
2172
  getTransactionStatus,
2173
+ getWalletAddress,
1381
2174
  hasTransactionHash,
1382
2175
  listChains,
2176
+ loadWallet,
2177
+ parseStatusMarker,
1383
2178
  verifyPayment,
1384
- waitForTransaction
2179
+ waitForTransaction,
2180
+ walletExists
1385
2181
  };
1386
2182
  //# sourceMappingURL=index.mjs.map