@x402api/agent-wallet-core 0.1.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 +12 -0
- package/dist/balances.d.ts +32 -0
- package/dist/balances.d.ts.map +1 -0
- package/dist/balances.js +278 -0
- package/dist/balances.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +73 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications.d.ts +71 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +209 -0
- package/dist/notifications.js.map +1 -0
- package/dist/payment/attempt-store.d.ts +49 -0
- package/dist/payment/attempt-store.d.ts.map +1 -0
- package/dist/payment/attempt-store.js +283 -0
- package/dist/payment/attempt-store.js.map +1 -0
- package/dist/payment/authorize.d.ts +22 -0
- package/dist/payment/authorize.d.ts.map +1 -0
- package/dist/payment/authorize.js +386 -0
- package/dist/payment/authorize.js.map +1 -0
- package/dist/payment/contracts.d.ts +36 -0
- package/dist/payment/contracts.d.ts.map +1 -0
- package/dist/payment/contracts.js +132 -0
- package/dist/payment/contracts.js.map +1 -0
- package/dist/protocol/base.d.ts +78 -0
- package/dist/protocol/base.d.ts.map +1 -0
- package/dist/protocol/base.js +459 -0
- package/dist/protocol/base.js.map +1 -0
- package/dist/protocol/external-recipient.d.ts +22 -0
- package/dist/protocol/external-recipient.d.ts.map +1 -0
- package/dist/protocol/external-recipient.js +149 -0
- package/dist/protocol/external-recipient.js.map +1 -0
- package/dist/protocol/http.d.ts +52 -0
- package/dist/protocol/http.d.ts.map +1 -0
- package/dist/protocol/http.js +283 -0
- package/dist/protocol/http.js.map +1 -0
- package/dist/protocol/solana.d.ts +147 -0
- package/dist/protocol/solana.d.ts.map +1 -0
- package/dist/protocol/solana.js +549 -0
- package/dist/protocol/solana.js.map +1 -0
- package/dist/protocol/tron.d.ts +130 -0
- package/dist/protocol/tron.d.ts.map +1 -0
- package/dist/protocol/tron.js +539 -0
- package/dist/protocol/tron.js.map +1 -0
- package/dist/storage/keystore.d.ts +51 -0
- package/dist/storage/keystore.d.ts.map +1 -0
- package/dist/storage/keystore.js +317 -0
- package/dist/storage/keystore.js.map +1 -0
- package/dist/storage/paths.d.ts +8 -0
- package/dist/storage/paths.d.ts.map +1 -0
- package/dist/storage/paths.js +22 -0
- package/dist/storage/paths.js.map +1 -0
- package/dist/storage/private-files.d.ts +8 -0
- package/dist/storage/private-files.d.ts.map +1 -0
- package/dist/storage/private-files.js +83 -0
- package/dist/storage/private-files.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { type JsonObject, type PaymentPayload, type PaymentRequired, type PaymentRequirement } from "./http.js";
|
|
2
|
+
export type TronTransactionParts = {
|
|
3
|
+
txID: string;
|
|
4
|
+
raw_data_hex: string;
|
|
5
|
+
signature?: string[];
|
|
6
|
+
walletDocument?: JsonObject;
|
|
7
|
+
};
|
|
8
|
+
export interface TronTransactionBuilder {
|
|
9
|
+
buildTransfer(args: {
|
|
10
|
+
ownerAddress: string;
|
|
11
|
+
tokenContract: string;
|
|
12
|
+
recipient: string;
|
|
13
|
+
amountAtomic: string;
|
|
14
|
+
challengeCommitment: string;
|
|
15
|
+
maxTimeoutSeconds: number;
|
|
16
|
+
}): Promise<TronTransactionParts>;
|
|
17
|
+
}
|
|
18
|
+
export interface TronWalletProvider {
|
|
19
|
+
requestAccounts(): Promise<string[]>;
|
|
20
|
+
getNetwork(): Promise<TronWalletNetwork>;
|
|
21
|
+
signTransaction(transaction: TronTransactionParts): Promise<TronTransactionParts>;
|
|
22
|
+
disconnect(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export type TronWalletNetwork = {
|
|
25
|
+
networkType: "Mainnet" | "Shasta" | "Nile" | "Unknown";
|
|
26
|
+
chainId: string;
|
|
27
|
+
};
|
|
28
|
+
export declare const TRON_MAINNET_CHAIN_ID = "0x2b6653dc";
|
|
29
|
+
export declare const TRON_USDT_MAINNET_CONTRACT = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t";
|
|
30
|
+
export type SelfFundedTronPaymentLabAdmission = {
|
|
31
|
+
kind: "self-funded-tron-payment-lab-v1";
|
|
32
|
+
payer: string;
|
|
33
|
+
recipient: string;
|
|
34
|
+
asset: typeof TRON_USDT_MAINNET_CONTRACT;
|
|
35
|
+
amountAtomic: string;
|
|
36
|
+
resourceUrl: string;
|
|
37
|
+
expiresAt: string;
|
|
38
|
+
maxFeeLimitSun: number;
|
|
39
|
+
};
|
|
40
|
+
export interface TronWebLike {
|
|
41
|
+
transactionBuilder: {
|
|
42
|
+
triggerSmartContract(contractAddress: string, functionSelector: string, options: {
|
|
43
|
+
feeLimit: number;
|
|
44
|
+
callValue: number;
|
|
45
|
+
txLocal?: boolean;
|
|
46
|
+
}, parameters: {
|
|
47
|
+
type: string;
|
|
48
|
+
value: string;
|
|
49
|
+
}[], ownerAddress: string): Promise<{
|
|
50
|
+
result?: {
|
|
51
|
+
result?: boolean;
|
|
52
|
+
message?: string;
|
|
53
|
+
};
|
|
54
|
+
transaction?: JsonObject;
|
|
55
|
+
}>;
|
|
56
|
+
addUpdateData(transaction: JsonObject, memo: string, dataFormat: "hex", options?: {
|
|
57
|
+
txLocal: boolean;
|
|
58
|
+
}): Promise<JsonObject>;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export interface TronWalletAdapterLike {
|
|
62
|
+
address: string | null;
|
|
63
|
+
connected?: boolean;
|
|
64
|
+
connect(): Promise<void>;
|
|
65
|
+
disconnect(): Promise<void>;
|
|
66
|
+
network(): Promise<TronWalletNetwork>;
|
|
67
|
+
signTransaction(transaction: JsonObject): Promise<JsonObject>;
|
|
68
|
+
}
|
|
69
|
+
export declare class TronWebTransactionBuilder implements TronTransactionBuilder {
|
|
70
|
+
#private;
|
|
71
|
+
constructor(options: {
|
|
72
|
+
tronWeb: TronWebLike;
|
|
73
|
+
feeLimitSun: number;
|
|
74
|
+
buildLocally?: boolean;
|
|
75
|
+
});
|
|
76
|
+
buildTransfer(args: {
|
|
77
|
+
ownerAddress: string;
|
|
78
|
+
tokenContract: string;
|
|
79
|
+
recipient: string;
|
|
80
|
+
amountAtomic: string;
|
|
81
|
+
challengeCommitment: string;
|
|
82
|
+
maxTimeoutSeconds: number;
|
|
83
|
+
}): Promise<TronTransactionParts>;
|
|
84
|
+
}
|
|
85
|
+
export declare class TronWalletAdapterProvider implements TronWalletProvider {
|
|
86
|
+
#private;
|
|
87
|
+
constructor(adapter: TronWalletAdapterLike);
|
|
88
|
+
requestAccounts(): Promise<string[]>;
|
|
89
|
+
getNetwork(): Promise<TronWalletNetwork>;
|
|
90
|
+
signTransaction(transaction: TronTransactionParts): Promise<TronTransactionParts>;
|
|
91
|
+
disconnect(): Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
export type InspectedTronTransfer = {
|
|
94
|
+
txID: string;
|
|
95
|
+
payerCanonicalHex: string;
|
|
96
|
+
tokenCanonicalHex: string;
|
|
97
|
+
recipientCanonicalHex: string;
|
|
98
|
+
amountAtomic: string;
|
|
99
|
+
timestampMs: number;
|
|
100
|
+
expirationMs: number;
|
|
101
|
+
feeLimitSun: number;
|
|
102
|
+
challengeCommitmentHex: string;
|
|
103
|
+
};
|
|
104
|
+
export declare function inspectTronTransfer(transaction: TronTransactionParts): Promise<InspectedTronTransfer>;
|
|
105
|
+
export declare function createTronUsdtPayment(options: {
|
|
106
|
+
builder: TronTransactionBuilder;
|
|
107
|
+
provider: TronWalletProvider;
|
|
108
|
+
paymentRequired: PaymentRequired;
|
|
109
|
+
accepted: PaymentRequirement;
|
|
110
|
+
buyerPaymentIdentifier: string;
|
|
111
|
+
trustedAssetContracts: readonly string[];
|
|
112
|
+
maxFeeLimitSun: number;
|
|
113
|
+
nowMs?: number;
|
|
114
|
+
}): Promise<PaymentPayload>;
|
|
115
|
+
/**
|
|
116
|
+
* Construct one bounded, self-funded TRON Mainnet payment without relying on
|
|
117
|
+
* a separately distributed recipient checkpoint. This entry point is for the
|
|
118
|
+
* private payment lab only: the caller must pin every economically relevant
|
|
119
|
+
* value from the operator's signed same-day canary admission.
|
|
120
|
+
*/
|
|
121
|
+
export declare function createTronUsdtLabPayment(options: {
|
|
122
|
+
builder: TronTransactionBuilder;
|
|
123
|
+
provider: TronWalletProvider;
|
|
124
|
+
paymentRequired: PaymentRequired;
|
|
125
|
+
accepted: PaymentRequirement;
|
|
126
|
+
buyerPaymentIdentifier: string;
|
|
127
|
+
admission: SelfFundedTronPaymentLabAdmission;
|
|
128
|
+
nowMs?: number;
|
|
129
|
+
}): Promise<PaymentPayload>;
|
|
130
|
+
//# sourceMappingURL=tron.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tron.d.ts","sourceRoot":"","sources":["../../src/protocol/tron.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import { canonicalJson, createPaymentPayload, } from "./http.js";
|
|
2
|
+
import { verifyExternalRecipientDeclaration } from "./external-recipient.js";
|
|
3
|
+
export const TRON_MAINNET_CHAIN_ID = "0x2b6653dc";
|
|
4
|
+
export const TRON_USDT_MAINNET_CONTRACT = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; // pragma: allowlist secret
|
|
5
|
+
export class TronWebTransactionBuilder {
|
|
6
|
+
#tronWeb;
|
|
7
|
+
#feeLimitSun;
|
|
8
|
+
#buildLocally;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
if (!Number.isSafeInteger(options.feeLimitSun) || options.feeLimitSun < 1) {
|
|
11
|
+
throw new Error("TRON fee limit is invalid");
|
|
12
|
+
}
|
|
13
|
+
this.#tronWeb = options.tronWeb;
|
|
14
|
+
this.#feeLimitSun = options.feeLimitSun;
|
|
15
|
+
this.#buildLocally = options.buildLocally ?? false;
|
|
16
|
+
}
|
|
17
|
+
async buildTransfer(args) {
|
|
18
|
+
if (!/^sha256:[0-9a-f]{64}$/.test(args.challengeCommitment)) {
|
|
19
|
+
throw new Error("TRON challenge commitment is invalid");
|
|
20
|
+
}
|
|
21
|
+
const wrapper = await this.#tronWeb.transactionBuilder.triggerSmartContract(args.tokenContract, "transfer(address,uint256)", {
|
|
22
|
+
feeLimit: this.#feeLimitSun,
|
|
23
|
+
callValue: 0,
|
|
24
|
+
txLocal: this.#buildLocally,
|
|
25
|
+
}, [
|
|
26
|
+
{ type: "address", value: args.recipient },
|
|
27
|
+
{ type: "uint256", value: args.amountAtomic },
|
|
28
|
+
], args.ownerAddress);
|
|
29
|
+
if (wrapper.result?.result !== true || !wrapper.transaction) {
|
|
30
|
+
throw new Error(`TRON transaction builder rejected transfer${wrapper.result?.message ? `: ${wrapper.result.message}` : ""}`);
|
|
31
|
+
}
|
|
32
|
+
const memoHex = `4b31583401${args.challengeCommitment.slice("sha256:".length)}`;
|
|
33
|
+
const updated = await this.#tronWeb.transactionBuilder.addUpdateData(wrapper.transaction, memoHex, "hex", { txLocal: this.#buildLocally });
|
|
34
|
+
return transactionParts(updated, false);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export class TronWalletAdapterProvider {
|
|
38
|
+
#adapter;
|
|
39
|
+
constructor(adapter) {
|
|
40
|
+
this.#adapter = adapter;
|
|
41
|
+
}
|
|
42
|
+
async requestAccounts() {
|
|
43
|
+
if (!this.#adapter.connected)
|
|
44
|
+
await this.#adapter.connect();
|
|
45
|
+
return this.#adapter.address ? [this.#adapter.address] : [];
|
|
46
|
+
}
|
|
47
|
+
async getNetwork() {
|
|
48
|
+
const network = await this.#adapter.network();
|
|
49
|
+
if (typeof network !== "object" ||
|
|
50
|
+
network === null ||
|
|
51
|
+
!["Mainnet", "Shasta", "Nile", "Unknown"].includes(network.networkType) ||
|
|
52
|
+
typeof network.chainId !== "string" ||
|
|
53
|
+
network.chainId.length < 1 ||
|
|
54
|
+
network.chainId.length > 128) {
|
|
55
|
+
throw new Error("TRON wallet returned malformed network information");
|
|
56
|
+
}
|
|
57
|
+
return network;
|
|
58
|
+
}
|
|
59
|
+
async signTransaction(transaction) {
|
|
60
|
+
if (!transaction.walletDocument) {
|
|
61
|
+
throw new Error("TRON wallet document is unavailable");
|
|
62
|
+
}
|
|
63
|
+
const signed = await this.#adapter.signTransaction(transaction.walletDocument);
|
|
64
|
+
return transactionParts(signed, true);
|
|
65
|
+
}
|
|
66
|
+
async disconnect() {
|
|
67
|
+
await this.#adapter.disconnect();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
71
|
+
const HEX64 = /^[0-9a-f]{64}$/;
|
|
72
|
+
const RAW_HEX = /^(?:[0-9a-f]{2})+$/;
|
|
73
|
+
const SIGNATURE = /^[0-9a-f]{130}$/;
|
|
74
|
+
const DECIMAL = /^(?:0|[1-9][0-9]{0,77})$/;
|
|
75
|
+
function transactionParts(value, signatureRequired) {
|
|
76
|
+
const txID = value.txID;
|
|
77
|
+
const rawDataHex = value.raw_data_hex;
|
|
78
|
+
const signatures = value.signature;
|
|
79
|
+
if (typeof txID !== "string" ||
|
|
80
|
+
typeof rawDataHex !== "string" ||
|
|
81
|
+
(signatures !== undefined &&
|
|
82
|
+
(!Array.isArray(signatures) ||
|
|
83
|
+
signatures.some((entry) => typeof entry !== "string"))) ||
|
|
84
|
+
(signatureRequired && !Array.isArray(signatures))) {
|
|
85
|
+
throw new Error("TronWeb returned a malformed transaction");
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
txID,
|
|
89
|
+
raw_data_hex: rawDataHex,
|
|
90
|
+
...(Array.isArray(signatures) ? { signature: signatures } : {}),
|
|
91
|
+
walletDocument: value,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function hex(bytes) {
|
|
95
|
+
return [...bytes]
|
|
96
|
+
.map((value) => value.toString(16).padStart(2, "0"))
|
|
97
|
+
.join("");
|
|
98
|
+
}
|
|
99
|
+
function fromHex(value, name) {
|
|
100
|
+
if (!RAW_HEX.test(value))
|
|
101
|
+
throw new Error(`${name} is not canonical lowercase hex`);
|
|
102
|
+
return Uint8Array.from(value.match(/../g).map((pair) => Number.parseInt(pair, 16)));
|
|
103
|
+
}
|
|
104
|
+
function arrayBuffer(bytes) {
|
|
105
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
106
|
+
copy.set(bytes);
|
|
107
|
+
return copy.buffer;
|
|
108
|
+
}
|
|
109
|
+
async function sha256(value) {
|
|
110
|
+
return new Uint8Array(await crypto.subtle.digest("SHA-256", arrayBuffer(value)));
|
|
111
|
+
}
|
|
112
|
+
function encodeVarint(value) {
|
|
113
|
+
if (value < 0n)
|
|
114
|
+
throw new Error("negative protobuf varint");
|
|
115
|
+
const result = [];
|
|
116
|
+
do {
|
|
117
|
+
let byte = Number(value & 0x7fn);
|
|
118
|
+
value >>= 7n;
|
|
119
|
+
if (value > 0n)
|
|
120
|
+
byte |= 0x80;
|
|
121
|
+
result.push(byte);
|
|
122
|
+
} while (value > 0n);
|
|
123
|
+
return Uint8Array.from(result);
|
|
124
|
+
}
|
|
125
|
+
function readVarint(data, start) {
|
|
126
|
+
let value = 0n;
|
|
127
|
+
let shift = 0n;
|
|
128
|
+
let offset = start;
|
|
129
|
+
while (offset < data.length && shift <= 63n) {
|
|
130
|
+
const byte = data[offset++];
|
|
131
|
+
value |= BigInt(byte & 0x7f) << shift;
|
|
132
|
+
if (byte < 0x80) {
|
|
133
|
+
if (hex(data.slice(start, offset)) !== hex(encodeVarint(value))) {
|
|
134
|
+
throw new Error("protobuf varint is not minimally encoded");
|
|
135
|
+
}
|
|
136
|
+
return [value, offset];
|
|
137
|
+
}
|
|
138
|
+
shift += 7n;
|
|
139
|
+
}
|
|
140
|
+
throw new Error("protobuf varint is truncated or too large");
|
|
141
|
+
}
|
|
142
|
+
function parseFields(data) {
|
|
143
|
+
const fields = [];
|
|
144
|
+
let offset = 0;
|
|
145
|
+
let previous = 0;
|
|
146
|
+
while (offset < data.length) {
|
|
147
|
+
let tag;
|
|
148
|
+
[tag, offset] = readVarint(data, offset);
|
|
149
|
+
const number = Number(tag >> 3n);
|
|
150
|
+
const wireType = Number(tag & 7n);
|
|
151
|
+
if (number < 1 || number < previous)
|
|
152
|
+
throw new Error("protobuf fields are not ordered");
|
|
153
|
+
previous = number;
|
|
154
|
+
if (wireType === 0) {
|
|
155
|
+
let value;
|
|
156
|
+
[value, offset] = readVarint(data, offset);
|
|
157
|
+
fields.push({ number, wireType, value });
|
|
158
|
+
}
|
|
159
|
+
else if (wireType === 2) {
|
|
160
|
+
let length;
|
|
161
|
+
[length, offset] = readVarint(data, offset);
|
|
162
|
+
if (length > BigInt(data.length - offset))
|
|
163
|
+
throw new Error("protobuf field is truncated");
|
|
164
|
+
const end = offset + Number(length);
|
|
165
|
+
fields.push({ number, wireType, value: data.slice(offset, end) });
|
|
166
|
+
offset = end;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
throw new Error("unsupported protobuf wire type");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return fields;
|
|
173
|
+
}
|
|
174
|
+
function fieldMap(fields, allowed, repeated = new Set()) {
|
|
175
|
+
const result = new Map();
|
|
176
|
+
for (const field of fields) {
|
|
177
|
+
if (!allowed.has(field.number))
|
|
178
|
+
throw new Error(`prohibited protobuf field ${field.number}`);
|
|
179
|
+
const values = result.get(field.number) ?? [];
|
|
180
|
+
values.push(field);
|
|
181
|
+
result.set(field.number, values);
|
|
182
|
+
}
|
|
183
|
+
for (const [number, values] of result) {
|
|
184
|
+
if (values.length !== 1 && !repeated.has(number)) {
|
|
185
|
+
throw new Error("duplicate singular protobuf field");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
function one(fields, number, wireType, name) {
|
|
191
|
+
const values = fields.get(number);
|
|
192
|
+
if (!values || values.length !== 1 || values[0].wireType !== wireType) {
|
|
193
|
+
throw new Error(`missing or malformed ${name}`);
|
|
194
|
+
}
|
|
195
|
+
return values[0].value;
|
|
196
|
+
}
|
|
197
|
+
function bytes(value, name) {
|
|
198
|
+
if (!(value instanceof Uint8Array))
|
|
199
|
+
throw new Error(`${name} is not bytes`);
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
function safeNumber(value, name) {
|
|
203
|
+
if (typeof value !== "bigint" ||
|
|
204
|
+
value < 1n ||
|
|
205
|
+
value > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
206
|
+
throw new Error(`${name} is outside the safe positive integer range`);
|
|
207
|
+
}
|
|
208
|
+
return Number(value);
|
|
209
|
+
}
|
|
210
|
+
async function decodeBase58Check(value) {
|
|
211
|
+
if (value.length < 26 || value.length > 36)
|
|
212
|
+
throw new Error("TRON address length is invalid");
|
|
213
|
+
let numeric = 0n;
|
|
214
|
+
for (const character of value) {
|
|
215
|
+
const index = BASE58_ALPHABET.indexOf(character);
|
|
216
|
+
if (index < 0)
|
|
217
|
+
throw new Error("TRON address is not Base58");
|
|
218
|
+
numeric = numeric * 58n + BigInt(index);
|
|
219
|
+
}
|
|
220
|
+
const decoded = [];
|
|
221
|
+
while (numeric > 0n) {
|
|
222
|
+
decoded.push(Number(numeric & 0xffn));
|
|
223
|
+
numeric >>= 8n;
|
|
224
|
+
}
|
|
225
|
+
decoded.reverse();
|
|
226
|
+
const leading = value.match(/^1*/)?.[0].length ?? 0;
|
|
227
|
+
const complete = Uint8Array.from([...new Array(leading).fill(0), ...decoded]);
|
|
228
|
+
if (complete.length !== 25)
|
|
229
|
+
throw new Error("TRON address has the wrong byte length");
|
|
230
|
+
const payload = complete.slice(0, 21);
|
|
231
|
+
const checksum = (await sha256(await sha256(payload))).slice(0, 4);
|
|
232
|
+
if (hex(checksum) !== hex(complete.slice(21)) || payload[0] !== 0x41) {
|
|
233
|
+
throw new Error("TRON address checksum or network prefix is invalid");
|
|
234
|
+
}
|
|
235
|
+
return payload;
|
|
236
|
+
}
|
|
237
|
+
export async function inspectTronTransfer(transaction) {
|
|
238
|
+
if (!HEX64.test(transaction.txID) ||
|
|
239
|
+
!RAW_HEX.test(transaction.raw_data_hex)) {
|
|
240
|
+
throw new Error("TRON transaction identity or bytes are not canonical hex");
|
|
241
|
+
}
|
|
242
|
+
const raw = fromHex(transaction.raw_data_hex, "TRON raw data");
|
|
243
|
+
if (hex(await sha256(raw)) !== transaction.txID) {
|
|
244
|
+
throw new Error("TRON txID is not SHA-256(raw_data)");
|
|
245
|
+
}
|
|
246
|
+
const rawFields = fieldMap(parseFields(raw), new Set([1, 3, 4, 8, 9, 10, 11, 12, 14, 18]), new Set([9, 11]));
|
|
247
|
+
if (rawFields.has(9) || rawFields.has(12))
|
|
248
|
+
throw new Error("TRON auth/scripts are forbidden");
|
|
249
|
+
if ((rawFields.get(11)?.length ?? 0) !== 1)
|
|
250
|
+
throw new Error("TRON transaction needs one contract");
|
|
251
|
+
const refBlockBytes = bytes(one(rawFields, 1, 2, "reference-block bytes"), "reference-block bytes");
|
|
252
|
+
const refBlockHash = bytes(one(rawFields, 4, 2, "reference-block hash"), "reference-block hash");
|
|
253
|
+
if (refBlockBytes.length !== 2 || refBlockHash.length !== 8) {
|
|
254
|
+
throw new Error("TRON reference-block identity is malformed");
|
|
255
|
+
}
|
|
256
|
+
if (rawFields.has(3)) {
|
|
257
|
+
safeNumber(one(rawFields, 3, 0, "reference-block number"), "reference-block number");
|
|
258
|
+
}
|
|
259
|
+
const commitment = bytes(one(rawFields, 10, 2, "challenge commitment"), "commitment");
|
|
260
|
+
if (commitment.length !== 37 ||
|
|
261
|
+
hex(commitment.slice(0, 5)) !== "4b31583401") {
|
|
262
|
+
throw new Error("TRON challenge commitment is not K1X4 v1");
|
|
263
|
+
}
|
|
264
|
+
const contractFields = fieldMap(parseFields(bytes(one(rawFields, 11, 2, "contract"), "contract")), new Set([1, 2, 3, 4, 5]));
|
|
265
|
+
if (one(contractFields, 1, 0, "contract type") !== 31n ||
|
|
266
|
+
contractFields.has(3) ||
|
|
267
|
+
contractFields.has(4) ||
|
|
268
|
+
contractFields.has(5)) {
|
|
269
|
+
throw new Error("TRON contract is not owner-permission TriggerSmartContract");
|
|
270
|
+
}
|
|
271
|
+
const anyFields = fieldMap(parseFields(bytes(one(contractFields, 2, 2, "contract parameter"), "parameter")), new Set([1, 2]));
|
|
272
|
+
const typeUrl = new TextDecoder("utf-8", { fatal: true }).decode(bytes(one(anyFields, 1, 2, "type URL"), "type URL"));
|
|
273
|
+
if (typeUrl !== "type.googleapis.com/protocol.TriggerSmartContract") {
|
|
274
|
+
throw new Error("TRON Any type is not TriggerSmartContract");
|
|
275
|
+
}
|
|
276
|
+
const trigger = fieldMap(parseFields(bytes(one(anyFields, 2, 2, "trigger value"), "trigger")), new Set([1, 2, 3, 4, 5, 6]));
|
|
277
|
+
if (trigger.has(3) || trigger.has(5) || trigger.has(6)) {
|
|
278
|
+
throw new Error("TRON trigger value/token fields are forbidden");
|
|
279
|
+
}
|
|
280
|
+
const payer = bytes(one(trigger, 1, 2, "owner"), "owner");
|
|
281
|
+
const token = bytes(one(trigger, 2, 2, "token contract"), "token");
|
|
282
|
+
const callData = bytes(one(trigger, 4, 2, "call data"), "call data");
|
|
283
|
+
if (payer.length !== 21 ||
|
|
284
|
+
token.length !== 21 ||
|
|
285
|
+
callData.length !== 68 ||
|
|
286
|
+
hex(callData.slice(0, 4)) !== "a9059cbb" ||
|
|
287
|
+
callData.slice(4, 16).some((value) => value !== 0)) {
|
|
288
|
+
throw new Error("TRON transfer call is not canonical transfer(address,uint256)");
|
|
289
|
+
}
|
|
290
|
+
const amount = BigInt(`0x${hex(callData.slice(36))}`);
|
|
291
|
+
if (amount < 1n)
|
|
292
|
+
throw new Error("TRON transfer amount is zero");
|
|
293
|
+
return {
|
|
294
|
+
txID: transaction.txID,
|
|
295
|
+
payerCanonicalHex: hex(payer),
|
|
296
|
+
tokenCanonicalHex: hex(token),
|
|
297
|
+
recipientCanonicalHex: `41${hex(callData.slice(16, 36))}`,
|
|
298
|
+
amountAtomic: amount.toString(),
|
|
299
|
+
timestampMs: safeNumber(one(rawFields, 14, 0, "timestamp"), "timestamp"),
|
|
300
|
+
expirationMs: safeNumber(one(rawFields, 8, 0, "expiration"), "expiration"),
|
|
301
|
+
feeLimitSun: safeNumber(one(rawFields, 18, 0, "fee limit"), "fee limit"),
|
|
302
|
+
challengeCommitmentHex: hex(commitment),
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
async function verifyAgainstRequirement(options) {
|
|
306
|
+
const { transaction, accepted, payer, trustedAssetContracts, maxFeeLimitSun, nowMs, } = options;
|
|
307
|
+
validateRequirement({
|
|
308
|
+
accepted,
|
|
309
|
+
trustedAssetContracts,
|
|
310
|
+
maxFeeLimitSun,
|
|
311
|
+
nowMs,
|
|
312
|
+
});
|
|
313
|
+
const inspected = await inspectTronTransfer(transaction);
|
|
314
|
+
const [payerBytes, tokenBytes, recipientBytes] = await Promise.all([
|
|
315
|
+
decodeBase58Check(payer),
|
|
316
|
+
decodeBase58Check(accepted.asset),
|
|
317
|
+
decodeBase58Check(accepted.payTo),
|
|
318
|
+
]);
|
|
319
|
+
if (inspected.payerCanonicalHex !== hex(payerBytes) ||
|
|
320
|
+
inspected.tokenCanonicalHex !== hex(tokenBytes) ||
|
|
321
|
+
inspected.recipientCanonicalHex !== hex(recipientBytes) ||
|
|
322
|
+
inspected.amountAtomic !== accepted.amount ||
|
|
323
|
+
inspected.challengeCommitmentHex !==
|
|
324
|
+
`4b31583401${String(accepted.extra.challengeCommitment).slice("sha256:".length)}` ||
|
|
325
|
+
inspected.feeLimitSun > maxFeeLimitSun ||
|
|
326
|
+
inspected.timestampMs > nowMs + 10_000 ||
|
|
327
|
+
inspected.timestampMs < nowMs - 60_000 ||
|
|
328
|
+
inspected.expirationMs <= nowMs ||
|
|
329
|
+
inspected.expirationMs - nowMs > accepted.maxTimeoutSeconds * 1_000) {
|
|
330
|
+
throw new Error("signed TRON transaction does not match the exact payment");
|
|
331
|
+
}
|
|
332
|
+
return inspected;
|
|
333
|
+
}
|
|
334
|
+
function validateRequirement(options) {
|
|
335
|
+
const { accepted, trustedAssetContracts, maxFeeLimitSun, nowMs } = options;
|
|
336
|
+
if (accepted.scheme !== "exact" ||
|
|
337
|
+
accepted.network !== "tron:mainnet" ||
|
|
338
|
+
accepted.extra.assetTransferMethod !== "signed_trc20_transaction" ||
|
|
339
|
+
accepted.extra.payloadProfile !== "com.k1hub.x402.tron-exact.v1" ||
|
|
340
|
+
accepted.extra.transactionContractType !== "TriggerSmartContract" ||
|
|
341
|
+
accepted.extra.function !== "transfer(address,uint256)" ||
|
|
342
|
+
typeof accepted.extra.challengeCommitment !== "string" ||
|
|
343
|
+
!/^sha256:[0-9a-f]{64}$/.test(accepted.extra.challengeCommitment) ||
|
|
344
|
+
!DECIMAL.test(accepted.amount) ||
|
|
345
|
+
accepted.amount === "0" ||
|
|
346
|
+
accepted.asset !== TRON_USDT_MAINNET_CONTRACT ||
|
|
347
|
+
!trustedAssetContracts.includes(TRON_USDT_MAINNET_CONTRACT) ||
|
|
348
|
+
!Number.isSafeInteger(maxFeeLimitSun) ||
|
|
349
|
+
maxFeeLimitSun < 1 ||
|
|
350
|
+
!Number.isSafeInteger(nowMs) ||
|
|
351
|
+
nowMs < 1) {
|
|
352
|
+
throw new Error("requirement is not an approved TRON exact profile");
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
export async function createTronUsdtPayment(options) {
|
|
356
|
+
const { builder, provider, paymentRequired, accepted, buyerPaymentIdentifier, trustedAssetContracts, maxFeeLimitSun, } = options;
|
|
357
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
358
|
+
if (!Number.isSafeInteger(nowMs) || nowMs < 1) {
|
|
359
|
+
throw new Error("current time is invalid");
|
|
360
|
+
}
|
|
361
|
+
await verifyExternalRecipientDeclaration({
|
|
362
|
+
paymentRequired,
|
|
363
|
+
accepted,
|
|
364
|
+
});
|
|
365
|
+
validateRequirement({
|
|
366
|
+
accepted,
|
|
367
|
+
trustedAssetContracts,
|
|
368
|
+
maxFeeLimitSun,
|
|
369
|
+
nowMs,
|
|
370
|
+
});
|
|
371
|
+
await Promise.all([
|
|
372
|
+
decodeBase58Check(accepted.asset),
|
|
373
|
+
decodeBase58Check(accepted.payTo),
|
|
374
|
+
]);
|
|
375
|
+
const accounts = await provider.requestAccounts();
|
|
376
|
+
if (accounts.length < 1 || typeof accounts[0] !== "string") {
|
|
377
|
+
throw new Error("TRON wallet returned no account");
|
|
378
|
+
}
|
|
379
|
+
const payer = accounts[0];
|
|
380
|
+
const network = await provider.getNetwork();
|
|
381
|
+
if (network.networkType !== "Mainnet" ||
|
|
382
|
+
network.chainId.toLowerCase() !== TRON_MAINNET_CHAIN_ID) {
|
|
383
|
+
throw new Error("TRON wallet is not connected to Mainnet");
|
|
384
|
+
}
|
|
385
|
+
await decodeBase58Check(payer);
|
|
386
|
+
const transaction = await builder.buildTransfer({
|
|
387
|
+
ownerAddress: payer,
|
|
388
|
+
tokenContract: accepted.asset,
|
|
389
|
+
recipient: accepted.payTo,
|
|
390
|
+
amountAtomic: accepted.amount,
|
|
391
|
+
challengeCommitment: String(accepted.extra.challengeCommitment),
|
|
392
|
+
maxTimeoutSeconds: accepted.maxTimeoutSeconds,
|
|
393
|
+
});
|
|
394
|
+
await verifyAgainstRequirement({
|
|
395
|
+
transaction,
|
|
396
|
+
accepted,
|
|
397
|
+
payer,
|
|
398
|
+
trustedAssetContracts,
|
|
399
|
+
maxFeeLimitSun,
|
|
400
|
+
nowMs,
|
|
401
|
+
});
|
|
402
|
+
const signed = await provider.signTransaction(transaction);
|
|
403
|
+
if (signed.txID !== transaction.txID ||
|
|
404
|
+
signed.raw_data_hex !== transaction.raw_data_hex ||
|
|
405
|
+
!Array.isArray(signed.signature) ||
|
|
406
|
+
signed.signature.length !== 1 ||
|
|
407
|
+
!SIGNATURE.test(signed.signature[0])) {
|
|
408
|
+
throw new Error("TRON wallet changed transaction bytes or returned an invalid signature");
|
|
409
|
+
}
|
|
410
|
+
await verifyAgainstRequirement({
|
|
411
|
+
transaction: signed,
|
|
412
|
+
accepted,
|
|
413
|
+
payer,
|
|
414
|
+
trustedAssetContracts,
|
|
415
|
+
maxFeeLimitSun,
|
|
416
|
+
nowMs,
|
|
417
|
+
});
|
|
418
|
+
return createPaymentPayload({
|
|
419
|
+
paymentRequired,
|
|
420
|
+
accepted,
|
|
421
|
+
paymentIdentifier: buyerPaymentIdentifier,
|
|
422
|
+
schemePayload: {
|
|
423
|
+
transaction: {
|
|
424
|
+
txID: signed.txID,
|
|
425
|
+
raw_data_hex: signed.raw_data_hex,
|
|
426
|
+
signature: signed.signature,
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
async function validateSelfFundedLabAdmission(options) {
|
|
432
|
+
const { admission, paymentRequired, accepted, nowMs } = options;
|
|
433
|
+
const expiresAtMs = Date.parse(admission.expiresAt);
|
|
434
|
+
if (admission.kind !== "self-funded-tron-payment-lab-v1" ||
|
|
435
|
+
admission.asset !== TRON_USDT_MAINNET_CONTRACT ||
|
|
436
|
+
admission.recipient !== accepted.payTo ||
|
|
437
|
+
admission.amountAtomic !== accepted.amount ||
|
|
438
|
+
admission.resourceUrl !== paymentRequired.resource.url ||
|
|
439
|
+
accepted.asset !== admission.asset ||
|
|
440
|
+
!DECIMAL.test(admission.amountAtomic) ||
|
|
441
|
+
admission.amountAtomic === "0" ||
|
|
442
|
+
!Number.isSafeInteger(admission.maxFeeLimitSun) ||
|
|
443
|
+
admission.maxFeeLimitSun < 1_000_000 ||
|
|
444
|
+
admission.maxFeeLimitSun > 15_000_000_000 ||
|
|
445
|
+
!Number.isFinite(expiresAtMs) ||
|
|
446
|
+
!admission.expiresAt.endsWith("Z") ||
|
|
447
|
+
expiresAtMs <= nowMs ||
|
|
448
|
+
expiresAtMs - nowMs > 86_400_000 ||
|
|
449
|
+
!paymentRequired.accepts.some((candidate) => canonicalJson(candidate) === canonicalJson(accepted))) {
|
|
450
|
+
throw new Error("payment is outside the exact self-funded TRON lab admission");
|
|
451
|
+
}
|
|
452
|
+
await Promise.all([
|
|
453
|
+
decodeBase58Check(admission.payer),
|
|
454
|
+
decodeBase58Check(admission.recipient),
|
|
455
|
+
decodeBase58Check(admission.asset),
|
|
456
|
+
]);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Construct one bounded, self-funded TRON Mainnet payment without relying on
|
|
460
|
+
* a separately distributed recipient checkpoint. This entry point is for the
|
|
461
|
+
* private payment lab only: the caller must pin every economically relevant
|
|
462
|
+
* value from the operator's signed same-day canary admission.
|
|
463
|
+
*/
|
|
464
|
+
export async function createTronUsdtLabPayment(options) {
|
|
465
|
+
const { builder, provider, paymentRequired, accepted, buyerPaymentIdentifier, admission, } = options;
|
|
466
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
467
|
+
if (!Number.isSafeInteger(nowMs) || nowMs < 1) {
|
|
468
|
+
throw new Error("current time is invalid");
|
|
469
|
+
}
|
|
470
|
+
await validateSelfFundedLabAdmission({
|
|
471
|
+
admission,
|
|
472
|
+
paymentRequired,
|
|
473
|
+
accepted,
|
|
474
|
+
nowMs,
|
|
475
|
+
});
|
|
476
|
+
validateRequirement({
|
|
477
|
+
accepted,
|
|
478
|
+
trustedAssetContracts: [TRON_USDT_MAINNET_CONTRACT],
|
|
479
|
+
maxFeeLimitSun: admission.maxFeeLimitSun,
|
|
480
|
+
nowMs,
|
|
481
|
+
});
|
|
482
|
+
const accounts = await provider.requestAccounts();
|
|
483
|
+
if (accounts.length < 1 ||
|
|
484
|
+
typeof accounts[0] !== "string" ||
|
|
485
|
+
accounts[0] !== admission.payer) {
|
|
486
|
+
throw new Error("TRON wallet account is outside the exact lab admission");
|
|
487
|
+
}
|
|
488
|
+
const payer = accounts[0];
|
|
489
|
+
const network = await provider.getNetwork();
|
|
490
|
+
if (network.networkType !== "Mainnet" ||
|
|
491
|
+
network.chainId.toLowerCase() !== TRON_MAINNET_CHAIN_ID) {
|
|
492
|
+
throw new Error("TRON wallet is not connected to Mainnet");
|
|
493
|
+
}
|
|
494
|
+
const transaction = await builder.buildTransfer({
|
|
495
|
+
ownerAddress: payer,
|
|
496
|
+
tokenContract: accepted.asset,
|
|
497
|
+
recipient: accepted.payTo,
|
|
498
|
+
amountAtomic: accepted.amount,
|
|
499
|
+
challengeCommitment: String(accepted.extra.challengeCommitment),
|
|
500
|
+
maxTimeoutSeconds: accepted.maxTimeoutSeconds,
|
|
501
|
+
});
|
|
502
|
+
await verifyAgainstRequirement({
|
|
503
|
+
transaction,
|
|
504
|
+
accepted,
|
|
505
|
+
payer,
|
|
506
|
+
trustedAssetContracts: [TRON_USDT_MAINNET_CONTRACT],
|
|
507
|
+
maxFeeLimitSun: admission.maxFeeLimitSun,
|
|
508
|
+
nowMs,
|
|
509
|
+
});
|
|
510
|
+
const signed = await provider.signTransaction(transaction);
|
|
511
|
+
if (signed.txID !== transaction.txID ||
|
|
512
|
+
signed.raw_data_hex !== transaction.raw_data_hex ||
|
|
513
|
+
!Array.isArray(signed.signature) ||
|
|
514
|
+
signed.signature.length !== 1 ||
|
|
515
|
+
!SIGNATURE.test(signed.signature[0])) {
|
|
516
|
+
throw new Error("TRON wallet changed transaction bytes or returned an invalid signature");
|
|
517
|
+
}
|
|
518
|
+
await verifyAgainstRequirement({
|
|
519
|
+
transaction: signed,
|
|
520
|
+
accepted,
|
|
521
|
+
payer,
|
|
522
|
+
trustedAssetContracts: [TRON_USDT_MAINNET_CONTRACT],
|
|
523
|
+
maxFeeLimitSun: admission.maxFeeLimitSun,
|
|
524
|
+
nowMs,
|
|
525
|
+
});
|
|
526
|
+
return createPaymentPayload({
|
|
527
|
+
paymentRequired,
|
|
528
|
+
accepted,
|
|
529
|
+
paymentIdentifier: buyerPaymentIdentifier,
|
|
530
|
+
schemePayload: {
|
|
531
|
+
transaction: {
|
|
532
|
+
txID: signed.txID,
|
|
533
|
+
raw_data_hex: signed.raw_data_hex,
|
|
534
|
+
signature: signed.signature,
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
//# sourceMappingURL=tron.js.map
|