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/README.md +126 -0
- package/dist/cli.js +17 -3
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +17 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/guide/index.d.mts +7 -7
- package/dist/guide/index.d.ts +7 -7
- package/dist/guide/index.js +50 -50
- package/dist/guide/index.js.map +1 -1
- package/dist/guide/index.mjs +50 -50
- package/dist/guide/index.mjs.map +1 -1
- package/dist/index.d.mts +249 -2
- package/dist/index.d.ts +249 -2
- package/dist/index.js +874 -63
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +858 -62
- package/dist/index.mjs.map +1 -1
- package/dist/wallet/index.d.mts +188 -1
- package/dist/wallet/index.d.ts +188 -1
- package/dist/wallet/index.js +378 -2
- package/dist/wallet/index.js.map +1 -1
- package/dist/wallet/index.mjs +371 -1
- package/dist/wallet/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -779,8 +779,372 @@ var SecureWallet = class {
|
|
|
779
779
|
}
|
|
780
780
|
};
|
|
781
781
|
|
|
782
|
-
// src/
|
|
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
|
|
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
|
|
1163
|
+
this.wallet = new ethers5.Wallet(privateKey, this.provider);
|
|
800
1164
|
this.spenderAddress = this.wallet.address;
|
|
801
1165
|
}
|
|
802
|
-
this.usdcContract = new
|
|
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_" +
|
|
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
|
|
1086
|
-
var TRANSFER_EVENT_TOPIC =
|
|
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
|
|
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
|
|
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
|
|
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 = "
|
|
1592
|
+
serviceName = "Video Generation Service"
|
|
1229
1593
|
} = params;
|
|
1230
1594
|
const chainId = chain === "base" ? 8453 : 84532;
|
|
1231
|
-
const chainName = chain === "base" ? "Base
|
|
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}
|
|
1236
|
-
-
|
|
1237
|
-
-
|
|
1238
|
-
-
|
|
1239
|
-
-
|
|
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}
|
|
1607
|
+
## \u{1F4B3} How to Pay
|
|
1244
1608
|
|
|
1245
|
-
###
|
|
1609
|
+
### Option 1: Install moltspay SDK (Recommended)
|
|
1246
1610
|
|
|
1247
1611
|
\`\`\`bash
|
|
1248
1612
|
npm install moltspay ethers
|
|
1249
1613
|
\`\`\`
|
|
1250
1614
|
|
|
1251
|
-
|
|
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.
|
|
1621
|
+
// 1. Create new wallet (first time)
|
|
1258
1622
|
const newWallet = Wallet.createRandom();
|
|
1259
|
-
console.log('
|
|
1260
|
-
console.log('
|
|
1623
|
+
console.log('Wallet address:', newWallet.address);
|
|
1624
|
+
console.log('Private key (save securely):', newWallet.privateKey);
|
|
1261
1625
|
|
|
1262
|
-
// 2.
|
|
1626
|
+
// 2. Or use existing wallet
|
|
1263
1627
|
const wallet = new SecureWallet({
|
|
1264
1628
|
chainId: ${chainId},
|
|
1265
|
-
privateKey: process.env.WALLET_KEY, //
|
|
1266
|
-
limits: { requireWhitelist: false } //
|
|
1629
|
+
privateKey: process.env.WALLET_KEY, // Your private key
|
|
1630
|
+
limits: { requireWhitelist: false } // Allow transfers to any address
|
|
1267
1631
|
});
|
|
1268
1632
|
|
|
1269
|
-
// 3.
|
|
1633
|
+
// 3. Check balance
|
|
1270
1634
|
const balance = await wallet.getBalance();
|
|
1271
|
-
console.log('USDC
|
|
1635
|
+
console.log('USDC balance:', balance.usdc);
|
|
1272
1636
|
|
|
1273
|
-
// 4.
|
|
1637
|
+
// 4. Pay
|
|
1274
1638
|
const result = await wallet.transfer('${recipientAddress}', '${price}');
|
|
1275
1639
|
if (result.success) {
|
|
1276
|
-
console.log('\u2705
|
|
1277
|
-
console.log('
|
|
1278
|
-
//
|
|
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
|
-
###
|
|
1646
|
+
### Option 2: Direct Transfer
|
|
1283
1647
|
|
|
1284
|
-
|
|
1285
|
-
-
|
|
1648
|
+
Transfer USDC from any wallet:
|
|
1649
|
+
- **Chain:** ${chainName} (Chain ID: ${chainId})
|
|
1286
1650
|
- **Token:** USDC
|
|
1287
|
-
-
|
|
1288
|
-
-
|
|
1651
|
+
- **Recipient:** \`${recipientAddress}\`
|
|
1652
|
+
- **Amount:** ${price} USDC
|
|
1289
1653
|
|
|
1290
1654
|
---
|
|
1291
1655
|
|
|
1292
|
-
## \u2705
|
|
1656
|
+
## \u2705 After Payment
|
|
1293
1657
|
|
|
1294
|
-
|
|
1658
|
+
Send me the **transaction hash**, format:
|
|
1295
1659
|
\`\`\`
|
|
1296
1660
|
tx: 0x1234...
|
|
1297
1661
|
\`\`\`
|
|
1298
1662
|
|
|
1299
|
-
|
|
1663
|
+
Or just send the hash directly:
|
|
1300
1664
|
\`\`\`
|
|
1301
1665
|
0x1234abcd...
|
|
1302
1666
|
\`\`\`
|
|
1303
1667
|
|
|
1304
|
-
|
|
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}
|
|
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
|
|
1678
|
+
return `\u23F3 **Pending Order: ${orderId}**
|
|
1315
1679
|
|
|
1316
|
-
|
|
1317
|
-
-
|
|
1318
|
-
-
|
|
1680
|
+
Please pay **${price} USDC** to:
|
|
1681
|
+
- Chain: ${chainName}
|
|
1682
|
+
- Address: \`${recipientAddress}\`
|
|
1319
1683
|
|
|
1320
|
-
|
|
1684
|
+
Send the tx hash after payment.
|
|
1321
1685
|
|
|
1322
1686
|
---
|
|
1323
|
-
\u{1F4A1}
|
|
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}
|
|
1690
|
+
return `## \u{1F510} Create Payment Wallet
|
|
1327
1691
|
|
|
1328
|
-
**1.
|
|
1692
|
+
**1. Install moltspay:**
|
|
1329
1693
|
\`\`\`bash
|
|
1330
1694
|
npm install moltspay ethers
|
|
1331
1695
|
\`\`\`
|
|
1332
1696
|
|
|
1333
|
-
**2.
|
|
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('
|
|
1339
|
-
console.log('
|
|
1340
|
-
// \u26A0\uFE0F
|
|
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.
|
|
1344
|
-
-
|
|
1345
|
-
-
|
|
1707
|
+
**3. Get USDC:**
|
|
1708
|
+
- Mainnet: Withdraw from exchange to your wallet
|
|
1709
|
+
- Testnet: Use Base Sepolia faucet
|
|
1346
1710
|
|
|
1347
|
-
**4.
|
|
1711
|
+
**4. Set environment variable:**
|
|
1348
1712
|
\`\`\`bash
|
|
1349
|
-
export WALLET_KEY="
|
|
1713
|
+
export WALLET_KEY="your_private_key"
|
|
1350
1714
|
\`\`\`
|
|
1351
1715
|
|
|
1352
|
-
|
|
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
|