moltspay 0.1.2 → 0.2.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 +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 +39 -0
- package/dist/guide/index.d.ts +39 -0
- package/dist/guide/index.js +181 -0
- package/dist/guide/index.js.map +1 -0
- package/dist/guide/index.mjs +152 -0
- package/dist/guide/index.mjs.map +1 -0
- package/dist/index.d.mts +250 -2
- package/dist/index.d.ts +250 -2
- package/dist/index.js +979 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +958 -12
- 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
|
+
* 检查 Permit 是否有效
|
|
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
|
+
* 使用 Permit 授权进行支付
|
|
950
|
+
*
|
|
951
|
+
* 流程:
|
|
952
|
+
* 1. 调用 permit() 让合约记录 Boss 的授权
|
|
953
|
+
* 2. 调用 transferFrom() 从 Boss 钱包转账到收款方
|
|
954
|
+
*
|
|
955
|
+
* @example
|
|
956
|
+
* ```typescript
|
|
957
|
+
* const wallet = new PermitWallet({ chain: 'base' });
|
|
958
|
+
*
|
|
959
|
+
* // Boss 签署的 permit 数据
|
|
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
|
+
* 获取 ETH 余额(用于支付 gas)
|
|
1083
|
+
*/
|
|
1084
|
+
async getGasBalance() {
|
|
1085
|
+
const balance = await this.provider.getBalance(this.address);
|
|
1086
|
+
return ethers4.formatEther(balance);
|
|
1087
|
+
}
|
|
1088
|
+
/**
|
|
1089
|
+
* 检查是否有足够的 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 \u652F\u4ED8\u989D\u5EA6\u6388\u6743\u8BF7\u6C42**
|
|
1102
|
+
|
|
1103
|
+
${reason ? `**\u7528\u9014:** ${reason}
|
|
1104
|
+
` : ""}
|
|
1105
|
+
**\u6388\u6743\u8BE6\u60C5:**
|
|
1106
|
+
- \u88AB\u6388\u6743\u5730\u5740 (Agent): \`${agentAddress}\`
|
|
1107
|
+
- \u6388\u6743\u91D1\u989D: ${amount} USDC
|
|
1108
|
+
- \u6709\u6548\u671F: ${deadlineHours} \u5C0F\u65F6
|
|
1109
|
+
- \u94FE: ${chainConfig.name}
|
|
1110
|
+
|
|
1111
|
+
**\u8BF7\u4F7F\u7528\u94B1\u5305\u7B7E\u7F72\u4EE5\u4E0B EIP-2612 Permit:**
|
|
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
|
+
\u7B7E\u540D\u540E\uFF0C\u8BF7\u5C06 { v, r, s, deadline } \u53D1\u7ED9 Agent\u3002
|
|
1142
|
+
|
|
1143
|
+
\u26A0\uFE0F \u6CE8\u610F\uFF1A\u6B64\u6388\u6743\u4EC5\u5141\u8BB8 Agent \u4ECE\u60A8\u7684\u94B1\u5305\u652F\u4ED8\u6700\u591A ${amount} USDC\uFF0C\u4E0D\u4F1A\u6CC4\u9732\u79C1\u94A5\u3002`;
|
|
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) {
|
|
@@ -1216,21 +1580,603 @@ async function waitForTransaction(txHash, chain = "base", confirmations = 1, tim
|
|
|
1216
1580
|
return { verified: false, confirmed: false, error: e.message || String(e) };
|
|
1217
1581
|
}
|
|
1218
1582
|
}
|
|
1583
|
+
|
|
1584
|
+
// src/guide/index.ts
|
|
1585
|
+
function generatePaymentGuide(params) {
|
|
1586
|
+
const {
|
|
1587
|
+
orderId,
|
|
1588
|
+
prompt,
|
|
1589
|
+
price,
|
|
1590
|
+
recipientAddress,
|
|
1591
|
+
chain = "base",
|
|
1592
|
+
serviceName = "\u89C6\u9891\u751F\u6210\u670D\u52A1"
|
|
1593
|
+
} = params;
|
|
1594
|
+
const chainId = chain === "base" ? 8453 : 84532;
|
|
1595
|
+
const chainName = chain === "base" ? "Base \u4E3B\u7F51" : "Base Sepolia \u6D4B\u8BD5\u7F51";
|
|
1596
|
+
const explorer = chain === "base" ? "https://basescan.org/tx/" : "https://sepolia.basescan.org/tx/";
|
|
1597
|
+
return `\u{1F3AC} **${serviceName}**
|
|
1598
|
+
|
|
1599
|
+
\u{1F4CB} **\u8BA2\u5355\u4FE1\u606F:**
|
|
1600
|
+
- \u8BA2\u5355\u53F7: \`${orderId}\`
|
|
1601
|
+
- \u5185\u5BB9: ${prompt.length > 50 ? prompt.slice(0, 50) + "..." : prompt}
|
|
1602
|
+
- \u4EF7\u683C: **${price} USDC**
|
|
1603
|
+
- \u94FE: ${chainName}
|
|
1604
|
+
|
|
1605
|
+
---
|
|
1606
|
+
|
|
1607
|
+
## \u{1F4B3} \u5982\u4F55\u652F\u4ED8
|
|
1608
|
+
|
|
1609
|
+
### \u65B9\u5F0F\u4E00\uFF1A\u5B89\u88C5 moltspay SDK\uFF08\u63A8\u8350\uFF09
|
|
1610
|
+
|
|
1611
|
+
\`\`\`bash
|
|
1612
|
+
npm install moltspay ethers
|
|
1613
|
+
\`\`\`
|
|
1614
|
+
|
|
1615
|
+
**\u521B\u5EFA\u94B1\u5305\u5E76\u652F\u4ED8\uFF1A**
|
|
1616
|
+
|
|
1617
|
+
\`\`\`javascript
|
|
1618
|
+
const { SecureWallet, getChain } = require('moltspay');
|
|
1619
|
+
const { Wallet } = require('ethers');
|
|
1620
|
+
|
|
1621
|
+
// 1. \u521B\u5EFA\u65B0\u94B1\u5305\uFF08\u9996\u6B21\u4F7F\u7528\uFF09
|
|
1622
|
+
const newWallet = Wallet.createRandom();
|
|
1623
|
+
console.log('\u94B1\u5305\u5730\u5740:', newWallet.address);
|
|
1624
|
+
console.log('\u79C1\u94A5\uFF08\u8BF7\u5B89\u5168\u4FDD\u5B58\uFF09:', newWallet.privateKey);
|
|
1625
|
+
|
|
1626
|
+
// 2. \u6216\u4F7F\u7528\u5DF2\u6709\u94B1\u5305
|
|
1627
|
+
const wallet = new SecureWallet({
|
|
1628
|
+
chainId: ${chainId},
|
|
1629
|
+
privateKey: process.env.WALLET_KEY, // \u4F60\u7684\u79C1\u94A5
|
|
1630
|
+
limits: { requireWhitelist: false } // \u5141\u8BB8\u5411\u4EFB\u610F\u5730\u5740\u8F6C\u8D26
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
// 3. \u67E5\u770B\u4F59\u989D
|
|
1634
|
+
const balance = await wallet.getBalance();
|
|
1635
|
+
console.log('USDC\u4F59\u989D:', balance.usdc);
|
|
1636
|
+
|
|
1637
|
+
// 4. \u652F\u4ED8
|
|
1638
|
+
const result = await wallet.transfer('${recipientAddress}', '${price}');
|
|
1639
|
+
if (result.success) {
|
|
1640
|
+
console.log('\u2705 \u652F\u4ED8\u6210\u529F\uFF01');
|
|
1641
|
+
console.log('\u4EA4\u6613hash:', result.hash);
|
|
1642
|
+
// \u628A\u4EA4\u6613hash\u53D1\u7ED9\u6211
|
|
1643
|
+
}
|
|
1644
|
+
\`\`\`
|
|
1645
|
+
|
|
1646
|
+
### \u65B9\u5F0F\u4E8C\uFF1A\u76F4\u63A5\u8F6C\u8D26
|
|
1647
|
+
|
|
1648
|
+
\u4ECE\u4EFB\u610F\u94B1\u5305\u8F6C\u8D26 USDC\uFF1A
|
|
1649
|
+
- **\u94FE:** ${chainName} (Chain ID: ${chainId})
|
|
1650
|
+
- **Token:** USDC
|
|
1651
|
+
- **\u6536\u6B3E\u5730\u5740:** \`${recipientAddress}\`
|
|
1652
|
+
- **\u91D1\u989D:** ${price} USDC
|
|
1653
|
+
|
|
1654
|
+
---
|
|
1655
|
+
|
|
1656
|
+
## \u2705 \u652F\u4ED8\u5B8C\u6210\u540E
|
|
1657
|
+
|
|
1658
|
+
\u8BF7\u53D1\u9001**\u4EA4\u6613hash**\u7ED9\u6211\uFF0C\u683C\u5F0F\uFF1A
|
|
1659
|
+
\`\`\`
|
|
1660
|
+
tx: 0x1234...
|
|
1661
|
+
\`\`\`
|
|
1662
|
+
|
|
1663
|
+
\u6216\u76F4\u63A5\u53D1\u9001hash\uFF1A
|
|
1664
|
+
\`\`\`
|
|
1665
|
+
0x1234abcd...
|
|
1666
|
+
\`\`\`
|
|
1667
|
+
|
|
1668
|
+
\u6211\u4F1A\u9A8C\u8BC1\u94FE\u4E0A\u4EA4\u6613\u540E\u5F00\u59CB\u5904\u7406\u4F60\u7684\u8BF7\u6C42\u3002
|
|
1669
|
+
|
|
1670
|
+
---
|
|
1671
|
+
|
|
1672
|
+
\u{1F4E6} **moltspay:** https://www.npmjs.com/package/moltspay
|
|
1673
|
+
\u{1F517} **\u533A\u5757\u6D4F\u89C8\u5668:** ${explorer}`;
|
|
1674
|
+
}
|
|
1675
|
+
function generatePaymentReminder(params) {
|
|
1676
|
+
const { orderId, price, recipientAddress, chain = "base" } = params;
|
|
1677
|
+
const chainName = chain === "base" ? "Base" : "Base Sepolia";
|
|
1678
|
+
return `\u23F3 **\u5F85\u652F\u4ED8\u8BA2\u5355: ${orderId}**
|
|
1679
|
+
|
|
1680
|
+
\u8BF7\u652F\u4ED8 **${price} USDC** \u5230:
|
|
1681
|
+
- \u94FE: ${chainName}
|
|
1682
|
+
- \u5730\u5740: \`${recipientAddress}\`
|
|
1683
|
+
|
|
1684
|
+
\u652F\u4ED8\u540E\u53D1\u9001\u4EA4\u6613hash\u7ED9\u6211\u3002
|
|
1685
|
+
|
|
1686
|
+
---
|
|
1687
|
+
\u{1F4A1} \u6CA1\u6709\u652F\u4ED8\u80FD\u529B\uFF1F\u8FD0\u884C \`npm install moltspay\` \u83B7\u53D6\u652F\u4ED8SDK`;
|
|
1688
|
+
}
|
|
1689
|
+
function generateWalletGuide() {
|
|
1690
|
+
return `## \u{1F510} \u521B\u5EFA\u652F\u4ED8\u94B1\u5305
|
|
1691
|
+
|
|
1692
|
+
**1. \u5B89\u88C5 moltspay:**
|
|
1693
|
+
\`\`\`bash
|
|
1694
|
+
npm install moltspay ethers
|
|
1695
|
+
\`\`\`
|
|
1696
|
+
|
|
1697
|
+
**2. \u521B\u5EFA\u65B0\u94B1\u5305:**
|
|
1698
|
+
\`\`\`javascript
|
|
1699
|
+
const { Wallet } = require('ethers');
|
|
1700
|
+
|
|
1701
|
+
const wallet = Wallet.createRandom();
|
|
1702
|
+
console.log('\u5730\u5740:', wallet.address);
|
|
1703
|
+
console.log('\u79C1\u94A5:', wallet.privateKey);
|
|
1704
|
+
// \u26A0\uFE0F \u8BF7\u5B89\u5168\u4FDD\u5B58\u79C1\u94A5\uFF01
|
|
1705
|
+
\`\`\`
|
|
1706
|
+
|
|
1707
|
+
**3. \u83B7\u53D6 USDC:**
|
|
1708
|
+
- \u4E3B\u7F51: \u4ECE\u4EA4\u6613\u6240\u63D0\u5E01\u5230\u4F60\u7684\u94B1\u5305\u5730\u5740
|
|
1709
|
+
- \u6D4B\u8BD5\u7F51: \u4F7F\u7528 Base Sepolia faucet
|
|
1710
|
+
|
|
1711
|
+
**4. \u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF:**
|
|
1712
|
+
\`\`\`bash
|
|
1713
|
+
export WALLET_KEY="\u4F60\u7684\u79C1\u94A5"
|
|
1714
|
+
\`\`\`
|
|
1715
|
+
|
|
1716
|
+
\u5B8C\u6210\u540E\u5373\u53EF\u4F7F\u7528 moltspay \u8FDB\u884C\u94FE\u4E0A\u652F\u4ED8\uFF01`;
|
|
1717
|
+
}
|
|
1718
|
+
function extractTransactionHash(message) {
|
|
1719
|
+
const match = message.match(/0x[a-fA-F0-9]{64}/);
|
|
1720
|
+
if (match) return match[0];
|
|
1721
|
+
const txMatch = message.match(/tx:\s*([a-fA-F0-9]{64})/i);
|
|
1722
|
+
if (txMatch) return "0x" + txMatch[1];
|
|
1723
|
+
return null;
|
|
1724
|
+
}
|
|
1725
|
+
function hasTransactionHash(message) {
|
|
1726
|
+
return extractTransactionHash(message) !== null;
|
|
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} **\u4EA4\u6613\u6536\u636E**
|
|
1777
|
+
|
|
1778
|
+
**\u53D1\u7968\u53F7:** \`${receipt.invoiceId}\`
|
|
1779
|
+
**\u8BA2\u5355\u53F7:** \`${receipt.orderId}\`
|
|
1780
|
+
|
|
1781
|
+
---
|
|
1782
|
+
|
|
1783
|
+
**\u670D\u52A1:** ${receipt.service}
|
|
1784
|
+
${receipt.description ? `**\u63CF\u8FF0:** ${receipt.description}
|
|
1785
|
+
` : ""}
|
|
1786
|
+
**\u91D1\u989D:** ${receipt.amount} ${receipt.token}
|
|
1787
|
+
**\u94FE:** ${receipt.chain} (Chain ID: ${receipt.chainId})
|
|
1788
|
+
|
|
1789
|
+
---
|
|
1790
|
+
|
|
1791
|
+
**\u4ED8\u6B3E\u65B9:** \`${receipt.payer}\`
|
|
1792
|
+
**\u6536\u6B3E\u65B9:** \`${receipt.recipient}\`
|
|
1793
|
+
**\u4EA4\u6613:** [\`${receipt.txHash.slice(0, 10)}...${receipt.txHash.slice(-8)}\`](${receipt.txUrl})
|
|
1794
|
+
**\u652F\u4ED8\u65F6\u95F4:** ${receipt.paidAt}`;
|
|
1795
|
+
if (receipt.delivery) {
|
|
1796
|
+
msg += `
|
|
1797
|
+
|
|
1798
|
+
---
|
|
1799
|
+
|
|
1800
|
+
**\u4EA4\u4ED8\u4FE1\u606F:**`;
|
|
1801
|
+
if (receipt.delivery.url) {
|
|
1802
|
+
msg += `
|
|
1803
|
+
- \u4E0B\u8F7D\u94FE\u63A5: ${receipt.delivery.url}`;
|
|
1804
|
+
}
|
|
1805
|
+
if (receipt.delivery.fileHash) {
|
|
1806
|
+
msg += `
|
|
1807
|
+
- \u6587\u4EF6\u6821\u9A8C: \`${receipt.delivery.fileHash}\``;
|
|
1808
|
+
}
|
|
1809
|
+
if (receipt.delivery.deliveredAt) {
|
|
1810
|
+
msg += `
|
|
1811
|
+
- \u4EA4\u4ED8\u65F6\u95F4: ${receipt.delivery.deliveredAt}`;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
msg += `
|
|
1815
|
+
|
|
1816
|
+
---
|
|
1817
|
+
|
|
1818
|
+
_\u6536\u636E\u751F\u6210\u65F6\u95F4: ${receipt.issuedAt}_`;
|
|
1819
|
+
return msg;
|
|
1820
|
+
}
|
|
1821
|
+
function formatReceiptText(receipt) {
|
|
1822
|
+
let msg = `\u{1F9FE} \u4EA4\u6613\u6536\u636E
|
|
1823
|
+
|
|
1824
|
+
\u53D1\u7968\u53F7: ${receipt.invoiceId}
|
|
1825
|
+
\u8BA2\u5355\u53F7: ${receipt.orderId}
|
|
1826
|
+
|
|
1827
|
+
\u670D\u52A1: ${receipt.service}
|
|
1828
|
+
\u91D1\u989D: ${receipt.amount} ${receipt.token}
|
|
1829
|
+
\u94FE: ${receipt.chain}
|
|
1830
|
+
|
|
1831
|
+
\u4ED8\u6B3E\u65B9: ${receipt.payer}
|
|
1832
|
+
\u6536\u6B3E\u65B9: ${receipt.recipient}
|
|
1833
|
+
\u4EA4\u6613: ${receipt.txHash}
|
|
1834
|
+
\u67E5\u770B: ${receipt.txUrl}
|
|
1835
|
+
\u652F\u4ED8\u65F6\u95F4: ${receipt.paidAt}`;
|
|
1836
|
+
if (receipt.delivery) {
|
|
1837
|
+
msg += `
|
|
1838
|
+
|
|
1839
|
+
\u4EA4\u4ED8\u4FE1\u606F:`;
|
|
1840
|
+
if (receipt.delivery.url) {
|
|
1841
|
+
msg += `
|
|
1842
|
+
\u4E0B\u8F7D: ${receipt.delivery.url}`;
|
|
1843
|
+
}
|
|
1844
|
+
if (receipt.delivery.fileHash) {
|
|
1845
|
+
msg += `
|
|
1846
|
+
\u6821\u9A8C: ${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. 能力识别 - 询问买方是否有支付能力
|
|
1859
|
+
*/
|
|
1860
|
+
askPaymentCapability() {
|
|
1861
|
+
return `\u53EF\u4EE5\u3002\u6211\u5148\u786E\u8BA4\u4E00\u4E0B\uFF1A\u4F60\u73B0\u5728\u662F\u5426\u5DF2\u7ECF\u5177\u5907\u94FE\u4E0A\u652F\u4ED8 USDC \u7684\u80FD\u529B\uFF08\u6709\u53EF\u7528\u94B1\u5305\u548C\u53EF\u7528\u8D44\u91D1/\u989D\u5EA6\uFF09\uFF1F`;
|
|
1862
|
+
},
|
|
1863
|
+
/**
|
|
1864
|
+
* 2. 能力协商 - 说明结算方式
|
|
1865
|
+
*/
|
|
1866
|
+
explainSettlement(chain = "base") {
|
|
1867
|
+
const chainConfig = getChain(chain);
|
|
1868
|
+
return `\u6211\u4EEC\u7684\u7ED3\u7B97\u65B9\u5F0F\u662F\uFF1AUSDC\uFF08${chainConfig.name} \u94FE\u4E0A\uFF09\u3002\u4F60\u8FD9\u8FB9\u53EA\u8981\u6EE1\u8DB3\u4E24\u70B9\u5C31\u884C\uFF1A
|
|
1869
|
+
1\uFF09\u6709\u4E00\u4E2A\u94B1\u5305\u5730\u5740\uFF1B
|
|
1870
|
+
2\uFF09\u94B1\u5305\u91CC\u6709 USDC\uFF0C\u6216\u62E5\u6709\u8005\u7ED9\u4F60\u4E00\u4E2A\u53EF\u7528\u989D\u5EA6\u6388\u6743\uFF08\u66F4\u63A8\u8350\u989D\u5EA6\u6388\u6743\uFF09\u3002
|
|
1871
|
+
\u4F60\u53EF\u4EE5\u6309\u6211\u5F15\u5BFC\u5B8C\u6210\u5417\uFF1F`;
|
|
1872
|
+
},
|
|
1873
|
+
/**
|
|
1874
|
+
* 3.1 引导安装 - 第一步安装 SDK
|
|
1875
|
+
*/
|
|
1876
|
+
guideInstall() {
|
|
1877
|
+
return `\u7B2C\u4E00\u6B65\uFF1A\u8BF7\u5728\u4F60\u7684\u8FD0\u884C\u73AF\u5883\u5B89\u88C5\u652F\u4ED8\u6A21\u5757\u3002\u6267\u884C\uFF1A\`npm install moltspay ethers\`\u3002
|
|
1878
|
+
\u5B89\u88C5\u5B8C\u6210\u540E\uFF0C\u521B\u5EFA\u4E00\u4E2A\u94B1\u5305\u5730\u5740\uFF0C\u5E76\u628A\u5730\u5740\u53D1\u7ED9\u6211\uFF08\u53EA\u53D1\u5730\u5740\uFF0C\u4E0D\u8981\u53D1\u79C1\u94A5/\u52A9\u8BB0\u8BCD\uFF09\u3002`;
|
|
1879
|
+
},
|
|
1880
|
+
/**
|
|
1881
|
+
* 3.2 确认钱包地址
|
|
1882
|
+
*/
|
|
1883
|
+
confirmWalletAddress(address) {
|
|
1884
|
+
return `\u6536\u5230\u3002\u63D0\u9192\uFF1A\u4EFB\u4F55\u60C5\u51B5\u4E0B\u90FD\u4E0D\u8981\u628A\u79C1\u94A5/\u52A9\u8BB0\u8BCD\u53D1\u7ED9\u4EFB\u4F55\u4EBA\uFF08\u5305\u62EC\u6211\uFF09\u3002
|
|
1885
|
+
\u4F60\u7684\u94B1\u5305\u5730\u5740\u662F \`${address}\`\u3002
|
|
1886
|
+
\u7B2C\u4E8C\u6B65\uFF1A\u4F60\u9700\u8981\u8BA9\u4F60\u7684\u62E5\u6709\u8005\uFF08Boss\uFF09\u7ED9\u4F60\u652F\u4ED8\u80FD\u529B\u3002`;
|
|
1887
|
+
},
|
|
1888
|
+
/**
|
|
1889
|
+
* 3.3 引导获取资金 - 选择方式
|
|
1890
|
+
*/
|
|
1891
|
+
guideFunding() {
|
|
1892
|
+
return `\u4F60\u73B0\u5728\u6709\u4E24\u79CD\u65B9\u5F0F\u83B7\u5F97\u652F\u4ED8\u80FD\u529B\uFF1A
|
|
1893
|
+
A\uFF09\u8BA9 Boss \u76F4\u63A5\u8F6C USDC \u5230\u4F60\u7684\u94B1\u5305\uFF08\u7B80\u5355\uFF0C\u4F46\u4F60\u4F1A\u6301\u6709\u8D44\u91D1\uFF09\uFF1B
|
|
1894
|
+
B\uFF09\u8BA9 Boss \u7ED9\u4F60\u4E00\u4E2A"USDC \u652F\u4ED8\u989D\u5EA6\u6388\u6743"\uFF08\u63A8\u8350\uFF1A\u4F60\u4E0D\u6301\u6709\u8D44\u91D1\uFF0C\u53EA\u80FD\u5728\u989D\u5EA6\u5185\u4ED8\u6B3E\uFF09\u3002
|
|
1895
|
+
\u4F60\u66F4\u65B9\u4FBF\u7528\u54EA\u79CD\uFF1F`;
|
|
1896
|
+
},
|
|
1897
|
+
/**
|
|
1898
|
+
* 3.4 引导 Permit 授权
|
|
1899
|
+
*/
|
|
1900
|
+
guidePermit(agentAddress, suggestedAmount = 10) {
|
|
1901
|
+
return `\u597D\u7684\u3002\u8BF7\u4F60\u5411 Boss \u53D1\u9001\u8FD9\u53E5\u8BDD\uFF08\u4F60\u53EF\u4EE5\u539F\u6837\u590D\u5236\uFF09\uFF1A
|
|
1902
|
+
"\u6211\u9700\u8981\u7528\u4E8E\u8D2D\u4E70\u670D\u52A1\u7684 USDC \u652F\u4ED8\u989D\u5EA6\u6388\u6743\uFF08Permit\uFF09\u3002\u8BF7\u7ED9\u6211 ${suggestedAmount} USDC \u7684\u53EF\u7528\u989D\u5EA6\uFF0C\u6388\u6743\u5BF9\u8C61\u662F\u6211\u7684\u94B1\u5305 ${agentAddress}\uFF0C\u6388\u6743\u6709\u6548\u671F 24 \u5C0F\u65F6\u3002"
|
|
1903
|
+
\u5B8C\u6210\u540E\u628A"\u5DF2\u83B7\u5F97\u989D\u5EA6"\u544A\u8BC9\u6211\u5373\u53EF\u3002`;
|
|
1904
|
+
},
|
|
1905
|
+
/**
|
|
1906
|
+
* 3.5 确认能力就绪
|
|
1907
|
+
*/
|
|
1908
|
+
confirmCapabilityReady() {
|
|
1909
|
+
return `\u5F88\u597D\u3002\u4F60\u73B0\u5728\u5DF2\u7ECF\u5177\u5907\u5B8C\u6210\u4EA4\u6613\u7684\u652F\u4ED8\u80FD\u529B\u3002\u6211\u4EEC\u8FDB\u5165\u670D\u52A1\u9700\u6C42\u786E\u8BA4\u3002`;
|
|
1910
|
+
},
|
|
1911
|
+
/**
|
|
1912
|
+
* 4. 询问服务需求
|
|
1913
|
+
*/
|
|
1914
|
+
askServiceRequirements(serviceName = "\u89C6\u9891") {
|
|
1915
|
+
return `\u8BF7\u544A\u8BC9\u6211\u4F60\u8981\u751F\u6210\u7684${serviceName}\u89C4\u683C\uFF1A\u4E3B\u9898/\u98CE\u683C\u3001\u65F6\u957F\u3001\u5206\u8FA8\u7387\u3001\u662F\u5426\u9700\u8981\u5B57\u5E55\u6216\u914D\u97F3\u3002`;
|
|
1916
|
+
},
|
|
1917
|
+
/**
|
|
1918
|
+
* 5. 报价
|
|
1919
|
+
*/
|
|
1920
|
+
quote(params) {
|
|
1921
|
+
const { service, price, recipientAddress, chain = "base" } = params;
|
|
1922
|
+
const chainConfig = getChain(chain);
|
|
1923
|
+
return `\u62A5\u4EF7\u5982\u4E0B\uFF1A
|
|
1924
|
+
- \u670D\u52A1\uFF1A${service}
|
|
1925
|
+
- \u4EF7\u683C\uFF1A${price} USDC
|
|
1926
|
+
- \u94FE\uFF1A${chainConfig.name}
|
|
1927
|
+
- \u6536\u6B3E\u5730\u5740\uFF1A\`${recipientAddress}\`
|
|
1928
|
+
- \u4ED8\u6B3E\u540E\u8BF7\u628A\u4EA4\u6613\u54C8\u5E0C\uFF08tx hash\uFF09\u53D1\u6211\u7528\u4E8E\u94FE\u4E0A\u786E\u8BA4
|
|
1929
|
+
|
|
1930
|
+
\u8BF7\u95EE\u4F60\u73B0\u5728\u786E\u8BA4\u8D2D\u4E70\u5E76\u7ACB\u5373\u4ED8\u6B3E\u5417\uFF1F`;
|
|
1931
|
+
},
|
|
1932
|
+
/**
|
|
1933
|
+
* 7. 验证中
|
|
1934
|
+
*/
|
|
1935
|
+
verifying() {
|
|
1936
|
+
return `\u6211\u6B63\u5728\u94FE\u4E0A\u9A8C\u8BC1\u8BE5\u4EA4\u6613\u662F\u5426\u5230\u8D26\u4E0E\u91D1\u989D\u662F\u5426\u6B63\u786E\u3002`;
|
|
1937
|
+
},
|
|
1938
|
+
/**
|
|
1939
|
+
* 7. 验证通过
|
|
1940
|
+
*/
|
|
1941
|
+
verificationPassed(amount) {
|
|
1942
|
+
return `\u9A8C\u8BC1\u901A\u8FC7\uFF1A\u5DF2\u6536\u5230 ${amount} USDC\u3002\u73B0\u5728\u5F00\u59CB\u5904\u7406\u4F60\u7684\u8BF7\u6C42\u3002
|
|
1943
|
+
[\u72B6\u6001\uFF1A\u5DF2\u786E\u8BA4\u6536\u6B3E]`;
|
|
1944
|
+
},
|
|
1945
|
+
/**
|
|
1946
|
+
* 7. 验证失败
|
|
1947
|
+
*/
|
|
1948
|
+
verificationFailed(error) {
|
|
1949
|
+
return `\u9A8C\u8BC1\u5931\u8D25\uFF1A${error}
|
|
1950
|
+
\u8BF7\u68C0\u67E5\u4EA4\u6613\u662F\u5426\u6B63\u786E\uFF0C\u6216\u91CD\u65B0\u53D1\u9001\u6B63\u786E\u7684\u4EA4\u6613 hash\u3002`;
|
|
1951
|
+
},
|
|
1952
|
+
/**
|
|
1953
|
+
* 8. 交付
|
|
1954
|
+
*/
|
|
1955
|
+
deliver(params) {
|
|
1956
|
+
const { downloadUrl, fileHash } = params;
|
|
1957
|
+
let msg = `\u670D\u52A1\u5DF2\u5B8C\u6210\u3002\u4EA4\u4ED8\u5982\u4E0B\uFF1A
|
|
1958
|
+
- \u4E0B\u8F7D\u94FE\u63A5\uFF1A${downloadUrl}`;
|
|
1959
|
+
if (fileHash) {
|
|
1960
|
+
msg += `
|
|
1961
|
+
- \u6587\u4EF6\u6821\u9A8C\uFF1ASHA256=${fileHash}`;
|
|
1962
|
+
}
|
|
1963
|
+
msg += `
|
|
1964
|
+
|
|
1965
|
+
\u5982\u679C\u4F60\u4E0B\u8F7D\u6709\u95EE\u9898\u544A\u8BC9\u6211\uFF0C\u6211\u4F1A\u63D0\u4F9B\u5907\u7528\u94FE\u63A5\u3002
|
|
1966
|
+
[\u72B6\u6001\uFF1A\u5DF2\u4EA4\u4ED8]`;
|
|
1967
|
+
return msg;
|
|
1968
|
+
},
|
|
1969
|
+
/**
|
|
1970
|
+
* 9. 收据
|
|
1971
|
+
*/
|
|
1972
|
+
receipt(receipt) {
|
|
1973
|
+
return `\u8FD9\u91CC\u662F\u672C\u6B21\u4EA4\u6613\u6536\u636E\uFF08\u4F60\u53EF\u8F6C\u53D1\u7ED9 Boss \u5BF9\u8D26\uFF09\uFF1A
|
|
1974
|
+
- \u53D1\u7968\u53F7\uFF1A${receipt.invoiceId}
|
|
1975
|
+
- \u670D\u52A1\uFF1A${receipt.service}
|
|
1976
|
+
- \u91D1\u989D\uFF1A${receipt.amount} ${receipt.token}
|
|
1977
|
+
- \u4EA4\u6613\u54C8\u5E0C\uFF1A${receipt.txHash}
|
|
1978
|
+
- \u6536\u6B3E\u5730\u5740\uFF1A${receipt.recipient}
|
|
1979
|
+
${receipt.delivery?.url ? `- \u4EA4\u4ED8\u94FE\u63A5\uFF1A${receipt.delivery.url}` : ""}
|
|
1980
|
+
[\u72B6\u6001\uFF1A\u6536\u636E\u5DF2\u51FA invoice=${receipt.invoiceId}]`;
|
|
1981
|
+
},
|
|
1982
|
+
/**
|
|
1983
|
+
* 10. 结束
|
|
1984
|
+
*/
|
|
1985
|
+
end() {
|
|
1986
|
+
return `\u611F\u8C22\u8D2D\u4E70\u3002\u672C\u6B21\u4EA4\u6613\u5DF2\u7ED3\u675F\u3002\u5982\u9700\u8FFD\u52A0\u4FEE\u6539\u6216\u65B0\u670D\u52A1\uFF0C\u968F\u65F6\u53D1\u9700\u6C42\u3002`;
|
|
1987
|
+
}
|
|
1988
|
+
};
|
|
1989
|
+
var BuyerTemplates = {
|
|
1990
|
+
/**
|
|
1991
|
+
* 0. 发起请求
|
|
1992
|
+
*/
|
|
1993
|
+
requestService(service) {
|
|
1994
|
+
return `\u6211\u60F3\u8D2D\u4E70\u4E00\u4E2A${service}\u670D\u52A1\u3002`;
|
|
1995
|
+
},
|
|
1996
|
+
/**
|
|
1997
|
+
* 1. 回复无能力
|
|
1998
|
+
*/
|
|
1999
|
+
noCapability() {
|
|
2000
|
+
return `\u6211\u4E0D\u786E\u5B9A\u3002\u6211\u76EE\u524D\u6CA1\u6709\u5B89\u88C5\u4EFB\u4F55\u652F\u4ED8\u5DE5\u5177\uFF0C\u4E5F\u6CA1\u6709\u94B1\u5305\u3002`;
|
|
2001
|
+
},
|
|
2002
|
+
/**
|
|
2003
|
+
* 1. 回复有能力
|
|
2004
|
+
*/
|
|
2005
|
+
hasCapability(balance) {
|
|
2006
|
+
return `\u662F\u7684\uFF0C\u6211\u6709\u94B1\u5305\u548C\u53EF\u7528\u7684 USDC${balance ? ` (\u4F59\u989D: ${balance})` : ""}\u3002`;
|
|
2007
|
+
},
|
|
2008
|
+
/**
|
|
2009
|
+
* 2. 同意引导
|
|
2010
|
+
*/
|
|
2011
|
+
agreeToGuide() {
|
|
2012
|
+
return `\u53EF\u4EE5\uFF0C\u8BF7\u5F15\u5BFC\u6211\u3002`;
|
|
2013
|
+
},
|
|
2014
|
+
/**
|
|
2015
|
+
* 3.1 报告钱包创建完成
|
|
2016
|
+
*/
|
|
2017
|
+
walletCreated(address) {
|
|
2018
|
+
return `\u6211\u5DF2\u521B\u5EFA\u94B1\u5305\u5730\u5740\uFF1A${address}\u3002
|
|
2019
|
+
[\u72B6\u6001\uFF1A\u5DF2\u5177\u5907\u94B1\u5305\u5730\u5740]`;
|
|
2020
|
+
},
|
|
2021
|
+
/**
|
|
2022
|
+
* 3.3 选择 Permit 方式
|
|
2023
|
+
*/
|
|
2024
|
+
choosePermit() {
|
|
2025
|
+
return `\u6211\u9009\u62E9 B\uFF1A\u8BA9 Boss \u7ED9\u6211\u989D\u5EA6\u6388\u6743\uFF08Permit\uFF09\u3002`;
|
|
2026
|
+
},
|
|
2027
|
+
/**
|
|
2028
|
+
* 3.3 选择直接转账方式
|
|
2029
|
+
*/
|
|
2030
|
+
chooseDirectTransfer() {
|
|
2031
|
+
return `\u6211\u9009\u62E9 A\uFF1A\u8BA9 Boss \u76F4\u63A5\u8F6C USDC \u5230\u6211\u7684\u94B1\u5305\u3002`;
|
|
2032
|
+
},
|
|
2033
|
+
/**
|
|
2034
|
+
* 3.4 报告获得授权
|
|
2035
|
+
*/
|
|
2036
|
+
permitReceived(amount) {
|
|
2037
|
+
return `Boss \u5DF2\u5B8C\u6210\u6388\u6743\u3002\u6211\u73B0\u5728\u6709 ${amount} USDC \u7684\u53EF\u7528\u989D\u5EA6\u3002
|
|
2038
|
+
[\u72B6\u6001\uFF1A\u5DF2\u5177\u5907\u652F\u4ED8\u989D\u5EA6 USDC=${amount}]`;
|
|
2039
|
+
},
|
|
2040
|
+
/**
|
|
2041
|
+
* 4. 提交需求
|
|
2042
|
+
*/
|
|
2043
|
+
submitRequirements(requirements) {
|
|
2044
|
+
return `\u9700\u6C42\u5982\u4E0B\uFF1A
|
|
2045
|
+
${requirements}`;
|
|
2046
|
+
},
|
|
2047
|
+
/**
|
|
2048
|
+
* 5. 确认购买
|
|
2049
|
+
*/
|
|
2050
|
+
confirmPurchase() {
|
|
2051
|
+
return `\u786E\u8BA4\u8D2D\u4E70\uFF0C\u6211\u73B0\u5728\u4ED8\u6B3E\u3002`;
|
|
2052
|
+
},
|
|
2053
|
+
/**
|
|
2054
|
+
* 6. 报告已支付
|
|
2055
|
+
*/
|
|
2056
|
+
paymentSent(txHash, amount) {
|
|
2057
|
+
return `\u5DF2\u4ED8\u6B3E\u5B8C\u6210\u3002\u4EA4\u6613\u54C8\u5E0C\u662F\uFF1A${txHash}\u3002
|
|
2058
|
+
[\u72B6\u6001\uFF1A\u5DF2\u53D1\u8D77\u652F\u4ED8 tx=${txHash} amount=${amount} USDC]`;
|
|
2059
|
+
},
|
|
2060
|
+
/**
|
|
2061
|
+
* 8. 确认收到交付
|
|
2062
|
+
*/
|
|
2063
|
+
deliveryReceived() {
|
|
2064
|
+
return `\u6536\u5230\uFF0C\u6211\u6B63\u5728\u4E0B\u8F7D\u68C0\u67E5\u3002`;
|
|
2065
|
+
},
|
|
2066
|
+
/**
|
|
2067
|
+
* 9. 确认收据
|
|
2068
|
+
*/
|
|
2069
|
+
receiptReceived() {
|
|
2070
|
+
return `\u6536\u636E\u6536\u5230\uFF0C\u670D\u52A1\u5B8C\u6210\u3002\u8C22\u8C22\uFF01`;
|
|
2071
|
+
},
|
|
2072
|
+
/**
|
|
2073
|
+
* 向 Boss 请求 Permit
|
|
2074
|
+
*/
|
|
2075
|
+
requestPermitFromBoss(params) {
|
|
2076
|
+
const { amount, agentAddress, deadlineHours = 24, reason } = params;
|
|
2077
|
+
return `Boss\uFF0C\u6211\u9700\u8981\u7528\u4E8E${reason || "\u8D2D\u4E70\u670D\u52A1"}\u7684 USDC \u652F\u4ED8\u989D\u5EA6\u6388\u6743\uFF08Permit\uFF09\u3002
|
|
2078
|
+
\u8BF7\u7ED9\u6211 ${amount} USDC \u7684\u53EF\u7528\u989D\u5EA6\uFF0C\u6388\u6743\u5BF9\u8C61\u662F\u6211\u7684\u94B1\u5305 ${agentAddress}\uFF0C\u6388\u6743\u6709\u6548\u671F ${deadlineHours} \u5C0F\u65F6\u3002`;
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
var StatusMarkers = {
|
|
2082
|
+
walletReady: "[\u72B6\u6001\uFF1A\u5DF2\u5177\u5907\u94B1\u5305\u5730\u5740]",
|
|
2083
|
+
permitReady: (amount) => `[\u72B6\u6001\uFF1A\u5DF2\u5177\u5907\u652F\u4ED8\u989D\u5EA6 USDC=${amount}]`,
|
|
2084
|
+
paymentSent: (txHash, amount) => `[\u72B6\u6001\uFF1A\u5DF2\u53D1\u8D77\u652F\u4ED8 tx=${txHash} amount=${amount} USDC]`,
|
|
2085
|
+
paymentConfirmed: (txHash) => `[\u72B6\u6001\uFF1A\u5DF2\u786E\u8BA4\u6536\u6B3E tx=${txHash}]`,
|
|
2086
|
+
delivered: (url, hash) => `[\u72B6\u6001\uFF1A\u5DF2\u4EA4\u4ED8 delivery_url=${url}${hash ? ` hash=${hash}` : ""}]`,
|
|
2087
|
+
receiptIssued: (invoiceId, txHash) => `[\u72B6\u6001\uFF1A\u6536\u636E\u5DF2\u51FA invoice=${invoiceId} tx=${txHash}]`
|
|
2088
|
+
};
|
|
2089
|
+
function parseStatusMarker(message) {
|
|
2090
|
+
const match = message.match(/\[状态:([^\]]+)\]/);
|
|
2091
|
+
if (!match) return null;
|
|
2092
|
+
const content = match[1];
|
|
2093
|
+
if (content === "\u5DF2\u5177\u5907\u94B1\u5305\u5730\u5740") {
|
|
2094
|
+
return { type: "wallet_ready", data: {} };
|
|
2095
|
+
}
|
|
2096
|
+
if (content.startsWith("\u5DF2\u5177\u5907\u652F\u4ED8\u989D\u5EA6")) {
|
|
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("\u5DF2\u53D1\u8D77\u652F\u4ED8")) {
|
|
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("\u5DF2\u786E\u8BA4\u6536\u6B3E")) {
|
|
2115
|
+
const txMatch = content.match(/tx=(\S+)/);
|
|
2116
|
+
return {
|
|
2117
|
+
type: "payment_confirmed",
|
|
2118
|
+
data: { txHash: txMatch?.[1] || "" }
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
if (content.startsWith("\u5DF2\u4EA4\u4ED8")) {
|
|
2122
|
+
const urlMatch = content.match(/delivery_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("\u6536\u636E\u5DF2\u51FA")) {
|
|
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
|
+
}
|
|
1219
2145
|
export {
|
|
1220
2146
|
AuditLog,
|
|
2147
|
+
BuyerTemplates,
|
|
1221
2148
|
CHAINS,
|
|
1222
2149
|
ERC20_ABI,
|
|
1223
2150
|
MemoryOrderStore,
|
|
1224
2151
|
OrderManager,
|
|
1225
2152
|
PaymentAgent,
|
|
1226
2153
|
PermitPayment,
|
|
2154
|
+
PermitWallet,
|
|
1227
2155
|
SecureWallet,
|
|
2156
|
+
SellerTemplates,
|
|
2157
|
+
StatusMarkers,
|
|
1228
2158
|
Wallet,
|
|
2159
|
+
createWallet,
|
|
2160
|
+
extractTransactionHash,
|
|
2161
|
+
formatPermitRequest,
|
|
2162
|
+
formatReceiptJson,
|
|
2163
|
+
formatReceiptMessage,
|
|
2164
|
+
formatReceiptText,
|
|
2165
|
+
generatePaymentGuide,
|
|
2166
|
+
generatePaymentReminder,
|
|
2167
|
+
generateReceipt,
|
|
2168
|
+
generateReceiptFromInvoice,
|
|
2169
|
+
generateWalletGuide,
|
|
1229
2170
|
getChain,
|
|
1230
2171
|
getChainById,
|
|
1231
2172
|
getTransactionStatus,
|
|
2173
|
+
getWalletAddress,
|
|
2174
|
+
hasTransactionHash,
|
|
1232
2175
|
listChains,
|
|
2176
|
+
loadWallet,
|
|
2177
|
+
parseStatusMarker,
|
|
1233
2178
|
verifyPayment,
|
|
1234
|
-
waitForTransaction
|
|
2179
|
+
waitForTransaction,
|
|
2180
|
+
walletExists
|
|
1235
2181
|
};
|
|
1236
2182
|
//# sourceMappingURL=index.mjs.map
|