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 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,
@@ -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
- const client = createAgentClient(store);
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, tokenBalance } from "../chain.js";
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 rawAmount = parseUnits(amount, token.decimals);
30
- // Gas is always paid in pathUSD — a non-pathUSD transfer with an empty
31
- // pathUSD balance can never land.
32
- if (token.address.toLowerCase() !== PATHUSD.address.toLowerCase()) {
33
- const gasBalance = await tokenBalance(client, PATHUSD.address, userAddress).catch(() => null);
34
- if (gasBalance !== null && gasBalance <= 0n) {
35
- console.error(`✗ pathUSD balance is 0 — gas is paid in pathUSD, so this ${token.symbol} transfer would fail. Receive some pathUSD first.`);
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: "0xfe76fedcfb6a7dfe7f10ecc518150ace70bd6994",
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: "0xadda91c4b336285ec732da4ae905f5ab3b4333b3",
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.1",
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": {