thaifi-wallet-cli 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chain.js +17 -2
- package/dist/commands/request.js +23 -2
- package/dist/commands/transfer.js +19 -9
- package/dist/index.js +2 -0
- package/dist/tokens.js +4 -2
- package/package.json +1 -1
package/dist/chain.js
CHANGED
|
@@ -3,6 +3,7 @@ import { http, defineChain } from "viem";
|
|
|
3
3
|
import { createClient, tempoActions } from "viem/tempo";
|
|
4
4
|
import { Actions, Account, Addresses, Chain as TempoChains } from "viem/tempo";
|
|
5
5
|
import { CONFIG } from "./config.js";
|
|
6
|
+
import { PATHUSD, TOKENS } from "./tokens.js";
|
|
6
7
|
/** ThaiFi chain 17 — clone of the tempo chain definition (formatters/serializers) with our ids. */
|
|
7
8
|
export const thaiFi = defineChain({
|
|
8
9
|
...TempoChains.mainnet,
|
|
@@ -17,7 +18,7 @@ export const thaiFi = defineChain({
|
|
|
17
18
|
});
|
|
18
19
|
/** The account keychain precompile (access keys, TIP-1011). */
|
|
19
20
|
export const accountKeychain = Addresses.accountKeychain;
|
|
20
|
-
export function createAgentClient(store) {
|
|
21
|
+
export function createAgentClient(store, feeToken) {
|
|
21
22
|
if (!store.userAddress)
|
|
22
23
|
throw new Error("Not paired yet — run `thaifi login` first.");
|
|
23
24
|
const account = Account.fromP256(store.privateKey, {
|
|
@@ -26,10 +27,24 @@ export function createAgentClient(store) {
|
|
|
26
27
|
return createClient({
|
|
27
28
|
account,
|
|
28
29
|
chain: thaiFi,
|
|
29
|
-
feeToken: CONFIG.pathUsd,
|
|
30
|
+
feeToken: feeToken ?? CONFIG.pathUsd,
|
|
30
31
|
transport: http(store.rpcUrl),
|
|
31
32
|
}).extend(tempoActions());
|
|
32
33
|
}
|
|
34
|
+
/** Min balance that makes a token usable as gas (~0.01; gas ≈ 0.003/tx). */
|
|
35
|
+
const GAS_MIN_UNITS = 10000n;
|
|
36
|
+
/**
|
|
37
|
+
* Pick the gas token by balance: pathUSD → THCFI → THCOC. The chain swaps the
|
|
38
|
+
* chosen token to the validator's pathUSD through the protocol FeeAMM.
|
|
39
|
+
*/
|
|
40
|
+
export async function pickFeeToken(client, userAddress) {
|
|
41
|
+
for (const t of TOKENS) {
|
|
42
|
+
const bal = await tokenBalance(client, t.address, userAddress).catch(() => 0n);
|
|
43
|
+
if (bal >= GAS_MIN_UNITS)
|
|
44
|
+
return t;
|
|
45
|
+
}
|
|
46
|
+
return PATHUSD;
|
|
47
|
+
}
|
|
33
48
|
export async function tokenBalance(client, token, address) {
|
|
34
49
|
return client.readContract({
|
|
35
50
|
address: token,
|
package/dist/commands/request.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** `thaifi request <url>` — curl-like HTTP client with MPP/x402 auto-payments. */
|
|
2
2
|
import { concatHex, keccak256, slice, stringToBytes } from "viem";
|
|
3
3
|
import { loadStore } from "../store.js";
|
|
4
|
-
import { createAgentClient } from "../chain.js";
|
|
4
|
+
import { createAgentClient, pickFeeToken } from "../chain.js";
|
|
5
5
|
import { buildAgentAuthCredential } from "../agentAuth.js";
|
|
6
6
|
import { CONFIG, PATHUSD_DECIMALS } from "../config.js";
|
|
7
|
+
import { PATHUSD, resolveToken } from "../tokens.js";
|
|
7
8
|
function b64uDecode(s) {
|
|
8
9
|
return Buffer.from(s.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8");
|
|
9
10
|
}
|
|
@@ -250,7 +251,27 @@ export async function request(url, options) {
|
|
|
250
251
|
}
|
|
251
252
|
const token = (challenge.token ?? CONFIG.pathUsd);
|
|
252
253
|
console.log("402 — paying $" + usd + " to " + challenge.payTo + " …");
|
|
253
|
-
|
|
254
|
+
// Gas token: --fee-token to force one, otherwise auto-pick by balance
|
|
255
|
+
// (pathUSD → THCFI → THCOC; the chain swaps it to pathUSD via FeeAMM).
|
|
256
|
+
const baseClient = createAgentClient(store);
|
|
257
|
+
let fee = PATHUSD;
|
|
258
|
+
if (options.feeToken) {
|
|
259
|
+
try {
|
|
260
|
+
fee = resolveToken(options.feeToken);
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
264
|
+
process.exitCode = 1;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
fee = await pickFeeToken(baseClient, store.userAddress);
|
|
270
|
+
}
|
|
271
|
+
const client = fee.address.toLowerCase() === PATHUSD.address.toLowerCase()
|
|
272
|
+
? baseClient
|
|
273
|
+
: createAgentClient(store, fee.address);
|
|
274
|
+
console.log("Gas: " + fee.symbol);
|
|
254
275
|
const hash = await client.token.transfer({
|
|
255
276
|
to: challenge.payTo,
|
|
256
277
|
amount: challenge.amount,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** `thaifi transfer <to> <amount> [--token <symbol|address>]` — TIP-20 transfer signed by the agent access key. */
|
|
2
2
|
import { parseUnits } from "viem";
|
|
3
3
|
import { loadStore } from "../store.js";
|
|
4
|
-
import { createAgentClient,
|
|
4
|
+
import { createAgentClient, pickFeeToken } from "../chain.js";
|
|
5
5
|
import { PATHUSD, resolveToken } from "../tokens.js";
|
|
6
6
|
export async function transfer(to, amount, options = {}) {
|
|
7
7
|
const store = loadStore();
|
|
@@ -24,19 +24,29 @@ export async function transfer(to, amount, options = {}) {
|
|
|
24
24
|
process.exitCode = 1;
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
|
-
const client = createAgentClient(store);
|
|
28
27
|
const userAddress = store.userAddress;
|
|
29
|
-
const
|
|
30
|
-
// Gas
|
|
31
|
-
// pathUSD
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
const baseClient = createAgentClient(store);
|
|
29
|
+
// Gas token: --fee-token to force one, otherwise auto-pick by balance
|
|
30
|
+
// (pathUSD → THCFI → THCOC; the chain swaps it to pathUSD via FeeAMM).
|
|
31
|
+
let fee = PATHUSD;
|
|
32
|
+
if (options.feeToken) {
|
|
33
|
+
try {
|
|
34
|
+
fee = resolveToken(options.feeToken);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
36
38
|
process.exitCode = 1;
|
|
37
39
|
return;
|
|
38
40
|
}
|
|
39
41
|
}
|
|
42
|
+
else {
|
|
43
|
+
fee = await pickFeeToken(baseClient, userAddress);
|
|
44
|
+
}
|
|
45
|
+
const client = fee.address.toLowerCase() === PATHUSD.address.toLowerCase()
|
|
46
|
+
? baseClient
|
|
47
|
+
: createAgentClient(store, fee.address);
|
|
48
|
+
const rawAmount = parseUnits(amount, token.decimals);
|
|
49
|
+
console.log("Gas: " + fee.symbol);
|
|
40
50
|
console.log("Token: " + token.symbol + " (" + token.address + ")");
|
|
41
51
|
console.log("To: " + to);
|
|
42
52
|
console.log("Amount: " + amount + " (raw " + rawAmount + ")");
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ program
|
|
|
43
43
|
.argument("<to>", "Recipient address (0x…)")
|
|
44
44
|
.argument("<amount>", "Amount in human units (e.g. 1.5)")
|
|
45
45
|
.option("--token <symbol|address>", "Token symbol or contract address (default: pathUSD)")
|
|
46
|
+
.option("--fee-token <symbol|address>", "Gas token (default: auto — pathUSD, else THCFI/THCOC by balance)")
|
|
46
47
|
.action(transfer);
|
|
47
48
|
program
|
|
48
49
|
.command("fund")
|
|
@@ -59,6 +60,7 @@ program
|
|
|
59
60
|
.option("--json <json>", "JSON body shorthand (sets method POST + content-type)")
|
|
60
61
|
.option("--dry-run", "Print the request without sending")
|
|
61
62
|
.option("--max-spend <usd>", "Maximum automatic payment (USD)", Number)
|
|
63
|
+
.option("--fee-token <symbol|address>", "Gas token for the payment tx (default: auto — pathUSD, else THCFI/THCOC by balance)")
|
|
62
64
|
.action(async (url, opts) => {
|
|
63
65
|
const headers = {};
|
|
64
66
|
for (const h of opts.header ?? []) {
|
package/dist/tokens.js
CHANGED
|
@@ -6,14 +6,16 @@ export const PATHUSD = {
|
|
|
6
6
|
name: "pathUSD",
|
|
7
7
|
decimals: 6,
|
|
8
8
|
};
|
|
9
|
+
// System TIP-20 tokens (native factory, 2026-09-24) — can pay gas fees via
|
|
10
|
+
// FeeAMM (FeeManager.setUserToken). Old factory tokens are test-only deprecated.
|
|
9
11
|
export const THCFI = {
|
|
10
|
-
address: "
|
|
12
|
+
address: "0x20c000000000000000000000c82102FFe7064362",
|
|
11
13
|
symbol: "THCFI",
|
|
12
14
|
name: "ThaiCoin ThaiFi",
|
|
13
15
|
decimals: 6,
|
|
14
16
|
};
|
|
15
17
|
export const THCOC = {
|
|
16
|
-
address: "
|
|
18
|
+
address: "0x20C0000000000000000000007c24a0c628e8A940",
|
|
17
19
|
symbol: "THCOC",
|
|
18
20
|
name: "ThaiCoin OpenCraft",
|
|
19
21
|
decimals: 6,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thaifi-wallet-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "ThaiFi Wallet CLI for AI agents — pair with wallet.thaifi.com, sign with on-chain access keys (spending limits enforced on-chain), x402/MPP payments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|