nodpay 0.2.3 → 0.2.5

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 CHANGED
@@ -28,11 +28,29 @@ NODPAY_AGENT_KEY=0x... npx nodpay propose \
28
28
  3. Agent proposes transactions with `npx nodpay propose`
29
29
  4. User approves/rejects on their phone
30
30
 
31
+ ## Key generation
32
+
33
+ ```bash
34
+ npx nodpay keygen --env-file .env
35
+ ```
36
+
37
+ Outputs the agent's **public address only**. The private key is written directly to `.env` — it never appears in stdout, logs, or the agent's context window.
38
+
39
+ If a key already exists, it reuses it and prints the address.
40
+
41
+ ### Security design
42
+
43
+ The agent (LLM) **never sees the private key**. `keygen` writes the secret directly to disk; the `propose` command reads it from the environment at runtime. This means:
44
+
45
+ - No private key in conversation history or context window
46
+ - No risk of leaking the key through prompt injection
47
+ - The agent only needs the public address (for wallet links)
48
+
31
49
  ## Env
32
50
 
33
51
  | Variable | Required | Description |
34
52
  |----------|----------|-------------|
35
- | `NODPAY_AGENT_KEY` | ✅ | Agent's private key (hex) |
53
+ | `NODPAY_AGENT_KEY` | ✅ | Agent's private key use `npx nodpay keygen` to generate securely |
36
54
 
37
55
  ## Supported chains
38
56
 
package/SKILL.md CHANGED
@@ -42,9 +42,9 @@ The wallet is a **Safe 2-of-3 multisig**. You hold 1 key, the user holds 2 (pass
42
42
  npx nodpay keygen --env-file .env
43
43
  ```
44
44
 
45
- This outputs your agent's **public address** (stdout). The private key is written directly to `.env` — it never appears in your output or context.
45
+ Outputs your agent's **public address** only. The private key is written directly to `.env` — it never appears in your output or context.
46
46
 
47
- If `NODPAY_AGENT_KEY` already exists in the file, it reuses the existing key and prints the address.
47
+ If a key already exists in the file, it reuses it and prints the address.
48
48
 
49
49
  **After keygen**, trigger a gateway restart so the env var takes effect. Send your wallet link message to the user BEFORE restarting — the restart will interrupt your session.
50
50
 
@@ -102,8 +102,6 @@ Each wallet file:
102
102
  }
103
103
  ```
104
104
 
105
- `agentSigner` is your agent's EOA address (derived from `NODPAY_AGENT_KEY`).
106
-
107
105
  For EOA wallets, replace passkey fields with `"userSigner": "0x..."`.
108
106
 
109
107
  One agent key serves all wallets — multi-wallet is handled user-side (different passkeys/recovery keys → different Safe addresses, same agent).
@@ -113,8 +111,8 @@ One agent key serves all wallets — multi-wallet is handled user-side (differen
113
111
  ### Propose a transaction
114
112
 
115
113
  ```bash
116
- NODPAY_AGENT_KEY=0x... \
117
114
  npx nodpay propose \
115
+ --chain <CHAIN_NAME> \
118
116
  --safe <WALLET_ADDRESS> \
119
117
  --to <RECIPIENT> \
120
118
  --value-eth <AMOUNT> \
@@ -133,12 +131,8 @@ The script outputs JSON with an `approveUrl`. Send it to the user:
133
131
 
134
132
  ### Check balance
135
133
 
136
- Use the RPC URL for the wallet's chain (see `references/networks.json`):
137
-
138
134
  ```bash
139
- curl -s -X POST <RPC_URL> \
140
- -H "Content-Type: application/json" \
141
- -d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["<WALLET_ADDRESS>","latest"],"id":1}'
135
+ npx nodpay propose --chain <CHAIN_NAME> --safe <WALLET_ADDRESS> --check-balance
142
136
  ```
143
137
 
144
138
  If balance is 0, remind the user to deposit before proposing.
@@ -153,12 +147,11 @@ Always check before proposing — this tells you the current nonce, pending ops,
153
147
 
154
148
  ---
155
149
 
156
- ## Script Reference
157
-
158
- ### Flags
150
+ ## Flags
159
151
 
160
152
  | Flag | Required | Description |
161
153
  |------|----------|-------------|
154
+ | `--chain` | ✅ | Chain name (e.g. `ethereum`, `base`, `sepolia`) |
162
155
  | `--safe` | ✅ | Wallet (Safe) address |
163
156
  | `--to` | ✅ | Recipient address |
164
157
  | `--value-eth` | ✅ | Amount in ETH |
@@ -170,16 +163,6 @@ Always check before proposing — this tells you the current nonce, pending ops,
170
163
  | `--nonce` | optional | Force nonce (for replacements) |
171
164
  | `--purpose` | optional | Human-readable label |
172
165
 
173
- ### Environment
174
-
175
- Only one env var is required:
176
-
177
- | Var | Description |
178
- |-----|-------------|
179
- | `NODPAY_AGENT_KEY` | Agent signing key (required) |
180
-
181
- Chain config (RPC, bundler, explorer) is auto-resolved from `references/networks.json`. No need to set `RPC_URL`, `CHAIN_ID`, or bundler keys.
182
-
183
166
  ### Supported Chains
184
167
 
185
168
  `ethereum`, `base`, `arbitrum`, `optimism`, `polygon`, `sepolia`, `base_sepolia`
@@ -190,11 +173,11 @@ The wallet address is the same across all chains (counterfactual). Chain is only
190
173
 
191
174
  ## Transaction Patterns
192
175
 
193
- **Sequential**: Just call propose multiple times. Nonces auto-increment (script handles this).
176
+ **Sequential**: Just call propose multiple times. Nonces auto-increment.
194
177
 
195
- **Replace**: To replace a pending tx, propose with `--nonce N` where N is the nonce of the tx you want to replace. Check pending nonces via `GET /api/txs?safe=<ADDRESS>` — each tx in the response includes its `nonce`.
178
+ **Replace**: Propose with `--nonce N` to replace a pending tx at nonce N. Check pending nonces via `GET /api/txs?safe=<ADDRESS>`.
196
179
 
197
- **Cascade**: Rejecting tx at nonce N auto-invalidates all tx with nonce > N. This is irreversible.
180
+ **Cascade**: Rejecting tx at nonce N invalidates all tx with nonce > N. Irreversible.
198
181
 
199
182
  ⚠️ **Never propose a new nonce then reject an older one** — the cascade will destroy your new tx too.
200
183
 
@@ -202,7 +185,7 @@ The wallet address is the same across all chains (counterfactual). Chain is only
202
185
 
203
186
  ## Reconnect (Wallet Recovery)
204
187
 
205
- If the user cleared their browser data, the wallet still exists on-chain. Build a reconnect link:
188
+ If the user cleared their browser data, build a reconnect link:
206
189
 
207
190
  ```
208
191
  https://nodpay.ai/?agent=YOUR_AGENT_ADDRESS&safe=WALLET_ADDRESS&recovery=RECOVERY_SIGNER&x=PASSKEY_X&y=PASSKEY_Y
@@ -232,7 +215,7 @@ User opens → verifies passkey → wallet restored. No on-chain action needed.
232
215
  | User says | Action |
233
216
  |-----------|--------|
234
217
  | "create a wallet" | Send `https://nodpay.ai/?agent=YOUR_ADDRESS` |
235
- | "send 0.1 ETH to 0x..." | Propose transaction |
236
- | "balance" | RPC `eth_getBalance` on Safe address |
218
+ | "send 0.1 ETH to 0x..." | Propose transaction with `--chain` |
219
+ | "balance" | Check balance on the relevant chain |
237
220
  | "pending?" | `GET /api/txs?safe=...` |
238
221
  | "wallet disappeared" | Send reconnect link |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodpay",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "NodPay CLI — propose on-chain payments from agent-human shared wallets",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,10 +37,31 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
37
37
  const PENDING_DIR = join(__dirname, '..', '.pending-txs');
38
38
  mkdirSync(PENDING_DIR, { recursive: true });
39
39
 
40
- const RPC_URL = process.env.RPC_URL;
41
- const CHAIN_ID = process.env.CHAIN_ID;
40
+ // Resolve chain config: --chain flag auto-resolves from networks.json, env vars as fallback
41
+ import { createRequire } from 'module';
42
+ const require = createRequire(import.meta.url);
43
+ const NETWORKS = require('@nodpay/core/networks');
44
+ const allChains = { ...NETWORKS.mainnet, ...NETWORKS.testnet };
45
+
46
+ const chainArg = process.argv.includes('--chain')
47
+ ? process.argv[process.argv.indexOf('--chain') + 1]
48
+ : null;
49
+
50
+ let RPC_URL, CHAIN_ID;
51
+ if (chainArg) {
52
+ const net = allChains[chainArg];
53
+ if (!net) {
54
+ console.error(`Error: Unknown chain "${chainArg}". Supported: ${Object.keys(allChains).join(', ')}`);
55
+ process.exit(1);
56
+ }
57
+ RPC_URL = process.env.RPC_URL || net.rpcUrl;
58
+ CHAIN_ID = String(net.chainId);
59
+ } else {
60
+ RPC_URL = process.env.RPC_URL;
61
+ CHAIN_ID = process.env.CHAIN_ID;
62
+ }
42
63
  if (!RPC_URL || !CHAIN_ID) {
43
- console.error('Error: RPC_URL and CHAIN_ID environment variables are required.\nSet them for your target chain. See references/networks.json for supported chains.');
64
+ console.error('Error: Specify --chain <name> or set RPC_URL + CHAIN_ID env vars.\nSupported chains: ' + Object.keys(allChains).join(', '));
44
65
  process.exit(1);
45
66
  }
46
67
  const ENTRYPOINT_ADDRESS = ENTRYPOINT;