@thirdfy/agent-cli 0.2.80 → 0.2.81
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/CHANGELOG.md +14 -0
- package/README.md +4 -3
- package/package.json +1 -1
- package/src/cli/help.mjs +3 -3
- package/src/cli/manifest.mjs +5 -3
- package/src/commands/bootstrap.mjs +2 -2
- package/src/commands/doctor.mjs +22 -8
- package/src/commands/wallet.mjs +23 -4
- package/src/core/chainRegistry.mjs +82 -0
- package/src/core/signedTx.mjs +23 -0
- package/src/runtime/actions/selection.mjs +1 -0
- package/src/runtime/execution/runners.mjs +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## [0.2.81] - 2026-10-01
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`dogeos-laika-sell-token`** is inferred as provider `dogeos-laika` for catalog rows without a `providerId`, like `dogeos-laika-buy-token`. The action ships in Thirdfy API 3.18.1 (`tokenAddress`, `tokenAmountRaw` in raw 18-decimal units, optional `minDogeWei`, `maxSlippageBps` default 100).
|
|
12
|
+
- **`src/core/chainRegistry.mjs`**: verified chain facts only: Base 8453 (`https://mainnet.base.org`) and DogeOS 6281971 (RPC `https://rpc.testnet.dogeos.com`, user-paid DOGE gas, faucet link). Other chains need `--rpc-url` or `RPC_URL`.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **`wallet submit` broadcast on Base for every chain.** It used `https://mainnet.base.org` (or `BASE_RPC_URL` / `RPC_URL`) whatever `--chain-id` said, and `--chain-id` itself was rejected as an unknown option although help listed it. It now accepts `--chain-id`; without it, the chain comes from the signed transaction, and a `--chain-id` that differs from the signed transaction fails with `CHAIN_ID_MISMATCH`. The RPC is `--rpc-url` first. On Base (or no chain) the order is unchanged: `BASE_RPC_URL`, `BASE_RPC_HTTP_URL`, `RPC_URL`, then `https://mainnet.base.org`. On any other chain it is `RPC_URL`, then the chain registry; `BASE_RPC_URL` is never used off Base. With none of these it fails with `RPC_URL_REQUIRED`. `doctor self` resolves its RPC the same way, accepts `--chain-id`, and checks that the RPC is on that chain.
|
|
17
|
+
- **`run --run-mode self --broadcast` / `self-exec` signed and broadcast through a Base RPC on every chain.** It now resolves the RPC for the transaction's chain the same way as `wallet submit`.
|
|
18
|
+
- **Gas note was always "Network gas may be sponsored".** `help onboarding` and `doctor auth` now take `--chain-id`; on a user-pays chain (DogeOS 6281971) they say gas is paid in native DOGE by the sending wallet and point to the faucet. Without a chain, the note names the user-pays chains. Pin: `test/dogeos-onboarding.test.cjs`.
|
|
19
|
+
- **DogeOS quickstart and provider runbook** now start with email onboarding (`login email`, then `--code <otp> --accept-terms --key-name`, which saves the agent key), warn not to use `managed wallet init` for `agent_wallet` (it creates a separate custodial delegation wallet), say the faucet also gives 100 test USDC, explain that Barkswap swaps are ERC-20 only so the first swap is USDC to WDOGE, and add a `dogeos-laika-sell-token` example after the buy.
|
|
20
|
+
|
|
7
21
|
## [0.2.80] - 2026-10-01
|
|
8
22
|
|
|
9
23
|
### Fixed
|
package/README.md
CHANGED
|
@@ -40,10 +40,11 @@ Run without global install:
|
|
|
40
40
|
npx @thirdfy/agent-cli --help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
## What's new in v0.2.
|
|
43
|
+
## What's new in v0.2.81
|
|
44
44
|
|
|
45
|
-
-
|
|
46
|
-
- **
|
|
45
|
+
- **DogeOS onboarding**: the quickstart starts with `login email`, warns not to use `managed wallet init` for `agent_wallet`, funds gas and test USDC from the DogeOS faucet, starts with a USDC→WDOGE swap, and adds `dogeos-laika-sell-token`. See [docs/dogeos-quickstart.md](./docs/dogeos-quickstart.md).
|
|
46
|
+
- **Chain-aware gas notes**: `help onboarding --chain-id` and `doctor auth --chain-id` say when the sending wallet pays gas (DogeOS) and link the faucet.
|
|
47
|
+
- **`wallet submit --chain-id` works**, reads the chain from the signed transaction when `--chain-id` is omitted, and picks the RPC for that chain. Base keeps its order (`BASE_RPC_URL`, `BASE_RPC_HTTP_URL`, `RPC_URL`); other chains use `--rpc-url`, then `RPC_URL`, then the built-in DogeOS RPC. Self-mode broadcast (`self-exec`) uses the same rule. A Base RPC is never used on DogeOS.
|
|
47
48
|
- See [CHANGELOG.md](./CHANGELOG.md) and GitHub Releases for older versions.
|
|
48
49
|
|
|
49
50
|
## Quick start
|
package/package.json
CHANGED
package/src/cli/help.mjs
CHANGED
|
@@ -11,7 +11,7 @@ Core commands:
|
|
|
11
11
|
thirdfy-agent login email <address> [--code <otp>] [--accept-terms] [--key-name <name>] [--agent-key <key>] [--not-interactive|--ni] [--json]
|
|
12
12
|
thirdfy-agent logout [--all] [--clear-agent-key] [--json]
|
|
13
13
|
thirdfy-agent whoami [--json]
|
|
14
|
-
thirdfy-agent help onboarding [--json]
|
|
14
|
+
thirdfy-agent help onboarding [--chain-id <id>] [--json]
|
|
15
15
|
thirdfy-agent catalogs list [--json]
|
|
16
16
|
thirdfy-agent actions [--catalog <id>] [--provider <id>] [--agent-api-key <key>] [--chain-id <id>] [--run-mode <mode>] [--json]
|
|
17
17
|
thirdfy-agent data summary --agent-key <key> [--chain-id <id>] [--network <id>] [--category trading|yield|prediction] [--limit <n>] [--json]
|
|
@@ -69,8 +69,8 @@ Core commands:
|
|
|
69
69
|
thirdfy-agent preflight --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--json]
|
|
70
70
|
thirdfy-agent run --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--broadcast] [--idempotency-key <key>] [--json]
|
|
71
71
|
thirdfy-agent self-exec --agent-api-key <key> --action <id> --params <json> --wallet-address <addr> --ows-wallet-name <name> [--rpc-url <url>] [--effect-check strict|relaxed|off] [--json]
|
|
72
|
-
thirdfy-agent doctor self [--wallet-address <addr>] [--ows-wallet-name <name>] [--rpc-url <url>] [--json]
|
|
73
|
-
thirdfy-agent doctor auth [--json]
|
|
72
|
+
thirdfy-agent doctor self [--wallet-address <addr>] [--ows-wallet-name <name>] [--chain-id <id>] [--rpc-url <url>] [--json]
|
|
73
|
+
thirdfy-agent doctor auth [--chain-id <id>] [--json]
|
|
74
74
|
thirdfy-agent doctor certify execution [--no-write] [--run-mode <mode>] [--schema-digest <digest>] [--required-actions <csv>] [--json]
|
|
75
75
|
thirdfy-agent jeff preflight --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--json]
|
|
76
76
|
thirdfy-agent jeff trade --agent-api-key <key> --action <id> --params <json> [--run-mode <mode>] [--json]
|
package/src/cli/manifest.mjs
CHANGED
|
@@ -60,8 +60,8 @@ export const CLI_MANIFEST = [
|
|
|
60
60
|
},
|
|
61
61
|
{ path: ['logout'], handler: 'commandLogout', options: ['bootstrap'], description: 'Logout and clear local credentials' },
|
|
62
62
|
{ path: ['whoami'], handler: 'commandWhoami', description: 'Show masked local identity' },
|
|
63
|
-
{ path: ['doctor', 'self'], handler: 'commandDoctorSelf', options: ['wallet'], description: 'Self-mode wallet diagnostics' },
|
|
64
|
-
{ path: ['doctor', 'auth'], handler: 'commandDoctorAuth', description: 'Auth and onboarding diagnostics' },
|
|
63
|
+
{ path: ['doctor', 'self'], handler: 'commandDoctorSelf', options: ['wallet', 'chains'], description: 'Self-mode wallet diagnostics' },
|
|
64
|
+
{ path: ['doctor', 'auth'], handler: 'commandDoctorAuth', options: ['chains'], description: 'Auth and onboarding diagnostics' },
|
|
65
65
|
{
|
|
66
66
|
path: ['doctor', 'certify', 'execution'],
|
|
67
67
|
handler: 'commandDoctorCertifyExecution',
|
|
@@ -72,11 +72,13 @@ export const CLI_MANIFEST = [
|
|
|
72
72
|
{
|
|
73
73
|
path: ['help', 'onboarding'],
|
|
74
74
|
handler: 'commandHelpOnboarding',
|
|
75
|
+
options: ['chains'],
|
|
75
76
|
description: 'Structured onboarding help (JSON envelope)',
|
|
76
77
|
},
|
|
77
78
|
{
|
|
78
79
|
path: ['onboarding', 'help'],
|
|
79
80
|
handler: 'commandHelpOnboarding',
|
|
81
|
+
options: ['chains'],
|
|
80
82
|
description: 'Structured onboarding help (JSON envelope)',
|
|
81
83
|
},
|
|
82
84
|
{ path: ['wallet', 'list'], handler: 'commandWalletList', description: 'List configured wallets' },
|
|
@@ -592,7 +594,7 @@ export const CLI_MANIFEST = [
|
|
|
592
594
|
path: ['wallet', 'submit'],
|
|
593
595
|
handler: 'commandWalletSubmit',
|
|
594
596
|
tier: 'online',
|
|
595
|
-
options: ['wallet'],
|
|
597
|
+
options: ['wallet', 'chains'],
|
|
596
598
|
description: 'Submit signed transaction',
|
|
597
599
|
},
|
|
598
600
|
{
|
|
@@ -153,12 +153,12 @@ async function commandConfigSet(ctx, flags) {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
|
|
156
|
-
async function commandHelpOnboarding(ctx,
|
|
156
|
+
async function commandHelpOnboarding(ctx, flags) {
|
|
157
157
|
printEnvelope({
|
|
158
158
|
success: true,
|
|
159
159
|
code: 'ONBOARDING_HELP_OK',
|
|
160
160
|
message: 'Thirdfy onboarding help',
|
|
161
|
-
data: buildOnboardingHelp(ctx),
|
|
161
|
+
data: buildOnboardingHelp(ctx, flags),
|
|
162
162
|
meta: { apiBase: ctx.apiBase },
|
|
163
163
|
});
|
|
164
164
|
}
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { requireFlag } from '../core/args.mjs';
|
|
|
3
3
|
import { apiGet, requestWithRouteFallback } from '../core/http.mjs';
|
|
4
4
|
import { loadProfileConfig, getProfileConfigPath, getCachedActionsCatalog } from '../core/context.mjs';
|
|
5
5
|
import { DEFAULT_API_BASE, PROFILE_DEFAULTS } from '../core/constants.mjs';
|
|
6
|
+
import { describeChainGas, parseChainIdFlag, resolveChainRpcUrl } from '../core/chainRegistry.mjs';
|
|
6
7
|
import { exitAfterOutput } from '../core/envelope.mjs';
|
|
7
8
|
import { normalizeRunMode, normalizeManagedRunMode } from '../core/runMode.mjs';
|
|
8
9
|
import { buildExecutionCertificationReport } from '../runtime/certification/executionCertify.mjs';
|
|
@@ -25,9 +26,7 @@ export function createDoctorCommands({
|
|
|
25
26
|
async function commandDoctorSelf(ctx, flags) {
|
|
26
27
|
const walletAddress = String(flags.walletAddress || process.env.MARKET_MAKER_WALLET_ADDRESS || '').trim();
|
|
27
28
|
const owsWalletName = String(flags.owsWalletName || process.env.OWS_WALLET_NAME || '').trim();
|
|
28
|
-
const rpcUrl =
|
|
29
|
-
flags.rpcUrl || process.env.BASE_RPC_URL || process.env.BASE_RPC_HTTP_URL || process.env.RPC_URL || 'https://mainnet.base.org'
|
|
30
|
-
).trim();
|
|
29
|
+
const rpcUrl = resolveChainRpcUrl({ chainId: flags.chainId, rpcUrl: flags.rpcUrl });
|
|
31
30
|
const passphrasePresent = Boolean(String(process.env.OWS_PASSPHRASE || process.env.OWS_API_KEY || '').trim());
|
|
32
31
|
const checks = [];
|
|
33
32
|
|
|
@@ -62,10 +61,23 @@ async function commandDoctorSelf(ctx, flags) {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
try {
|
|
64
|
+
if (!rpcUrl) throw new Error(`no known RPC for chain ${flags.chainId}; pass --rpc-url <url>`);
|
|
65
65
|
const ethers = await loadEthers();
|
|
66
66
|
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
67
67
|
const block = await provider.getBlockNumber();
|
|
68
68
|
checks.push({ name: 'rpc-connectivity', ok: Number.isFinite(block), detail: `latestBlock=${block}` });
|
|
69
|
+
const expectedChainId = parseChainIdFlag(flags.chainId);
|
|
70
|
+
if (expectedChainId) {
|
|
71
|
+
const network = await provider.getNetwork();
|
|
72
|
+
const rpcChainId = Number(network.chainId);
|
|
73
|
+
checks.push({
|
|
74
|
+
name: 'rpc-chain-id',
|
|
75
|
+
ok: rpcChainId === expectedChainId,
|
|
76
|
+
detail: rpcChainId === expectedChainId
|
|
77
|
+
? `chainId=${rpcChainId}`
|
|
78
|
+
: `RPC is on chain ${rpcChainId}, expected ${expectedChainId}; pass --rpc-url for chain ${expectedChainId}`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
69
81
|
} catch (error) {
|
|
70
82
|
checks.push({ name: 'rpc-connectivity', ok: false, detail: error?.message || 'rpc check failed' });
|
|
71
83
|
}
|
|
@@ -91,7 +103,8 @@ async function commandDoctorSelf(ctx, flags) {
|
|
|
91
103
|
if (!ok) await exitAfterOutput(1);
|
|
92
104
|
}
|
|
93
105
|
|
|
94
|
-
function buildOnboardingHelp(ctx) {
|
|
106
|
+
function buildOnboardingHelp(ctx, flags = {}) {
|
|
107
|
+
const chainId = Number(flags?.chainId || 0) || null;
|
|
95
108
|
return {
|
|
96
109
|
title: 'Thirdfy Agent CLI onboarding',
|
|
97
110
|
summary: 'Use email OTP, wallet-sign, or existing credentials to create owner auth, wallets, and hidden agent execution credentials. Fresh solo profiles default to agent_wallet.',
|
|
@@ -140,8 +153,9 @@ function buildOnboardingHelp(ctx) {
|
|
|
140
153
|
'thirdfy-agent login email user@example.com --code 123456 --accept-terms --ni (place --ni after the subcommand so it is not parsed as a flag value)',
|
|
141
154
|
rule: 'Commands must fail fast instead of waiting for prompts when required input is missing.',
|
|
142
155
|
},
|
|
156
|
+
gas: describeChainGas(chainId),
|
|
143
157
|
executionModes: {
|
|
144
|
-
agent_wallet:
|
|
158
|
+
agent_wallet: `Solo managed server-wallet execution through /execution-wallet + /execute; no subscriber binding when owner userDid matches. ${describeChainGas(chainId)}`,
|
|
145
159
|
self: 'BYOW/OWS local signing through build-tx, wallet sign, and wallet submit.',
|
|
146
160
|
hybrid: 'Primary self or agent_wallet execution with optional Thirdfy mirror validation.',
|
|
147
161
|
thirdfy: 'Publisher/fanout execute-intent; requires active subscriber/delegation bindings for writes.',
|
|
@@ -159,7 +173,7 @@ function buildOnboardingHelp(ctx) {
|
|
|
159
173
|
};
|
|
160
174
|
}
|
|
161
175
|
|
|
162
|
-
async function commandDoctorAuth(ctx,
|
|
176
|
+
async function commandDoctorAuth(ctx, flags = {}) {
|
|
163
177
|
const config = loadProfileConfig();
|
|
164
178
|
const auth = config.auth && typeof config.auth === 'object' ? config.auth : {};
|
|
165
179
|
const agent = config.agent && typeof config.agent === 'object' ? config.agent : {};
|
|
@@ -220,7 +234,7 @@ async function commandDoctorAuth(ctx, _flags) {
|
|
|
220
234
|
name: 'thirdfy-credits-vs-gas-sponsorship',
|
|
221
235
|
optional: true,
|
|
222
236
|
ok: true,
|
|
223
|
-
detail:
|
|
237
|
+
detail: `Thirdfy credits/free quota gate action access; Privy gas sponsorship only pays network gas when chain/action sponsorship is enabled. ${describeChainGas(flags?.chainId)}`,
|
|
224
238
|
},
|
|
225
239
|
];
|
|
226
240
|
const ok = checks.every((entry) => entry.optional || entry.ok);
|
|
@@ -230,7 +244,7 @@ async function commandDoctorAuth(ctx, _flags) {
|
|
|
230
244
|
message: ok ? 'Auth profile is ready' : 'Auth profile needs setup',
|
|
231
245
|
data: {
|
|
232
246
|
checks,
|
|
233
|
-
help: buildOnboardingHelp(ctx),
|
|
247
|
+
help: buildOnboardingHelp(ctx, flags),
|
|
234
248
|
},
|
|
235
249
|
meta: { apiBase: ctx.apiBase },
|
|
236
250
|
});
|
package/src/commands/wallet.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { requireFlag } from '../core/args.mjs';
|
|
2
2
|
import { createCliError, exitAfterOutput } from '../core/envelope.mjs';
|
|
3
3
|
import { loadProfileConfig } from '../core/context.mjs';
|
|
4
|
+
import { DEFAULT_CHAIN_ID, parseChainIdFlag, resolveChainRpcUrl } from '../core/chainRegistry.mjs';
|
|
5
|
+
import { readSignedTxChainId } from '../core/signedTx.mjs';
|
|
4
6
|
|
|
5
7
|
export function createWalletCommands(deps) {
|
|
6
8
|
const {
|
|
@@ -85,10 +87,27 @@ async function commandWalletSubmit(ctx, flags, capabilities) {
|
|
|
85
87
|
if (!signedTxHex) {
|
|
86
88
|
throw createCliError('MISSING_SIGNED_TX', 'wallet submit requires --signed-tx-hex');
|
|
87
89
|
}
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
const flagChainId = parseChainIdFlag(flags.chainId);
|
|
91
|
+
if (Number.isNaN(flagChainId)) {
|
|
92
|
+
throw createCliError('INVALID_CHAIN_ID', `wallet submit --chain-id must be a positive integer (got ${flags.chainId})`);
|
|
93
|
+
}
|
|
94
|
+
// The signed tx carries its chain id (EIP-155 / typed tx). Use it when --chain-id is absent,
|
|
95
|
+
// and refuse a mismatch so a DogeOS-signed tx is never sent to a Base RPC (or vice versa).
|
|
96
|
+
const txChainId = readSignedTxChainId(signedTxHex);
|
|
97
|
+
if (flagChainId && txChainId && flagChainId !== txChainId) {
|
|
98
|
+
throw createCliError(
|
|
99
|
+
'CHAIN_ID_MISMATCH',
|
|
100
|
+
`wallet submit --chain-id ${flagChainId} does not match the signed transaction chain id ${txChainId}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const chainId = flagChainId || txChainId || DEFAULT_CHAIN_ID;
|
|
104
|
+
const rpcUrl = resolveChainRpcUrl({ chainId, rpcUrl: flags.rpcUrl });
|
|
105
|
+
if (!rpcUrl) {
|
|
106
|
+
throw createCliError(
|
|
107
|
+
'RPC_URL_REQUIRED',
|
|
108
|
+
`wallet submit has no known RPC for chain ${chainId}; pass --rpc-url <url> for that chain`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
92
111
|
const txHash = await submitSignedTx({ rpcUrl, chainId, signedTxHex });
|
|
93
112
|
printEnvelope({
|
|
94
113
|
success: true,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Chain facts the CLI needs locally: a public RPC for BYOW submit / balance reads, and
|
|
2
|
+
// whether network gas is paid by the sending wallet (no sponsorship on that chain).
|
|
3
|
+
// The API capabilities document stays the source of truth for lane support.
|
|
4
|
+
|
|
5
|
+
export const DEFAULT_CHAIN_ID = 8453;
|
|
6
|
+
|
|
7
|
+
// Only chains whose RPC we have verified. Other chains need --rpc-url or RPC_URL.
|
|
8
|
+
export const CHAIN_REGISTRY = {
|
|
9
|
+
8453: { chainKey: 'base-mainnet', displayName: 'Base', rpcUrl: 'https://mainnet.base.org', nativeSymbol: 'ETH' },
|
|
10
|
+
6281971: {
|
|
11
|
+
chainKey: 'dogeos-testnet',
|
|
12
|
+
displayName: 'DogeOS Chikyu testnet',
|
|
13
|
+
rpcUrl: 'https://rpc.testnet.dogeos.com',
|
|
14
|
+
nativeSymbol: 'DOGE',
|
|
15
|
+
userPaysGas: true,
|
|
16
|
+
faucetUrl: 'https://faucet.testnet.dogeos.com',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function positiveChainId(value) {
|
|
21
|
+
const n = Number(value);
|
|
22
|
+
if (!Number.isFinite(n) || n <= 0) return undefined;
|
|
23
|
+
return Math.trunc(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getChainInfo(chainId) {
|
|
27
|
+
const id = positiveChainId(chainId);
|
|
28
|
+
return id ? CHAIN_REGISTRY[id] || null : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Chains where the sending wallet pays network gas in the native token (never sponsored). */
|
|
32
|
+
export function chainUserPaysGas(chainId) {
|
|
33
|
+
return Boolean(getChainInfo(chainId)?.userPaysGas);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Chain ids where the sending wallet pays gas. */
|
|
37
|
+
export const USER_PAYS_GAS_CHAIN_IDS = Object.entries(CHAIN_REGISTRY)
|
|
38
|
+
.filter(([, info]) => info.userPaysGas)
|
|
39
|
+
.map(([id]) => Number(id));
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* One-line gas note for a chain. Without a chain id it describes the generic case.
|
|
43
|
+
*/
|
|
44
|
+
export function describeChainGas(chainId) {
|
|
45
|
+
const info = getChainInfo(chainId);
|
|
46
|
+
if (info?.userPaysGas) {
|
|
47
|
+
const faucet = info.faucetUrl ? ` Fund it from ${info.faucetUrl}.` : '';
|
|
48
|
+
return `Network gas on ${info.displayName} (${positiveChainId(chainId)}) is paid in native ${info.nativeSymbol} by the sending wallet; it is not sponsored.${faucet}`;
|
|
49
|
+
}
|
|
50
|
+
const userPays = USER_PAYS_GAS_CHAIN_IDS.map((id) => `${CHAIN_REGISTRY[id].displayName} ${id}`).join(', ');
|
|
51
|
+
return `Network gas may be sponsored on supported chains, but Thirdfy credits/free-quota gates still apply. On user-pays chains (${userPays}) the sending wallet pays gas in the native token.`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* RPC for a chain. The --rpc-url flag always wins. Then:
|
|
56
|
+
* - Base (or no chain given): BASE_RPC_URL, BASE_RPC_HTTP_URL, RPC_URL, then the registry.
|
|
57
|
+
* This is the pre-0.2.81 order, so existing Base setups keep the same RPC.
|
|
58
|
+
* - Any other chain: RPC_URL, then the registry. BASE_RPC_URL / BASE_RPC_HTTP_URL are never used.
|
|
59
|
+
* Returns '' when nothing matches; the caller asks for --rpc-url.
|
|
60
|
+
*/
|
|
61
|
+
export function resolveChainRpcUrl({ chainId, rpcUrl, env = process.env } = {}) {
|
|
62
|
+
const explicit = String(rpcUrl || '').trim();
|
|
63
|
+
if (explicit) return explicit;
|
|
64
|
+
const id = positiveChainId(chainId) || DEFAULT_CHAIN_ID;
|
|
65
|
+
const generic = String(env.RPC_URL || '').trim();
|
|
66
|
+
if (id === DEFAULT_CHAIN_ID) {
|
|
67
|
+
const baseEnv = String(env.BASE_RPC_URL || env.BASE_RPC_HTTP_URL || '').trim();
|
|
68
|
+
if (baseEnv) return baseEnv;
|
|
69
|
+
}
|
|
70
|
+
if (generic) return generic;
|
|
71
|
+
return getChainInfo(id)?.rpcUrl || '';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Parse a --chain-id value. Returns null when absent, a positive integer when valid,
|
|
76
|
+
* and NaN when present but not a positive integer (the caller rejects it).
|
|
77
|
+
*/
|
|
78
|
+
export function parseChainIdFlag(value) {
|
|
79
|
+
if (value === undefined || value === null || String(value).trim() === '') return null;
|
|
80
|
+
const n = Number(String(value).trim());
|
|
81
|
+
return Number.isInteger(n) && n > 0 ? n : NaN;
|
|
82
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Chain id encoded in a signed raw transaction (EIP-155 / typed tx), or null when the
|
|
7
|
+
* hex does not parse or carries no chain id (legacy pre-EIP-155).
|
|
8
|
+
*/
|
|
9
|
+
export function readSignedTxChainId(signedTxHex) {
|
|
10
|
+
let Transaction;
|
|
11
|
+
try {
|
|
12
|
+
({ Transaction } = require('ethers'));
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const tx = Transaction.from(String(signedTxHex || '').trim());
|
|
18
|
+
const id = Number(tx.chainId);
|
|
19
|
+
return Number.isInteger(id) && id > 0 ? id : null;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -77,6 +77,7 @@ function inferActionProvider(action) {
|
|
|
77
77
|
'dogeos-laika-execute-raw': 'dogeos-laika',
|
|
78
78
|
'dogeos-laika-launch-token': 'dogeos-laika',
|
|
79
79
|
'dogeos-laika-buy-token': 'dogeos-laika',
|
|
80
|
+
'dogeos-laika-sell-token': 'dogeos-laika',
|
|
80
81
|
'dogeos-laika-claim': 'dogeos-laika',
|
|
81
82
|
'dogeos-laika-token-state': 'dogeos-laika',
|
|
82
83
|
'get-governance-v2-overview': 'governance-v2',
|
|
@@ -14,6 +14,7 @@ import { idempotentReplayFields } from '../../core/hostedRuntimeIdempotency.mjs'
|
|
|
14
14
|
import { withActionTimeout } from '../../core/actionTimeouts.mjs';
|
|
15
15
|
import { isReadOnlyResolvedAction } from '../../core/readOnlyActions.mjs';
|
|
16
16
|
import { resolveWalletExecuteChainId } from '../../core/walletExecuteChainId.mjs';
|
|
17
|
+
import { resolveChainRpcUrl } from '../../core/chainRegistry.mjs';
|
|
17
18
|
import { isEmptyQueuedExecuteIntent, shouldFallbackManagedReadToExecuteIntent } from './managedReadExecuteRail.mjs';
|
|
18
19
|
import { createRequire } from 'module';
|
|
19
20
|
|
|
@@ -850,9 +851,10 @@ async function performSelfBroadcast(ctx, flags, resolvedAction, selfResult) {
|
|
|
850
851
|
const chainId = Number(txd.chainId || flags.chainId || 8453);
|
|
851
852
|
const walletAddress = String(flags.walletAddress || process.env.MARKET_MAKER_WALLET_ADDRESS || '').trim();
|
|
852
853
|
const owsWalletName = String(flags.owsWalletName || process.env.OWS_WALLET_NAME || '').trim();
|
|
853
|
-
const rpcUrl =
|
|
854
|
-
|
|
855
|
-
|
|
854
|
+
const rpcUrl = resolveChainRpcUrl({ chainId, rpcUrl: flags.rpcUrl });
|
|
855
|
+
if (!rpcUrl) {
|
|
856
|
+
throw createCliError('RPC_URL_REQUIRED', `self-exec has no known RPC for chain ${chainId}; pass --rpc-url <url> for that chain`);
|
|
857
|
+
}
|
|
856
858
|
if (!walletAddress) {
|
|
857
859
|
throw createCliError('SIGNING_FAILED', 'Missing --wallet-address (or MARKET_MAKER_WALLET_ADDRESS) for self-exec signing.');
|
|
858
860
|
}
|