nodpay 0.2.14 → 0.2.16

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/SKILL.md CHANGED
@@ -17,7 +17,7 @@ You propose payments, your human approves with one tap. 2-of-3 multisig — you
17
17
  - **You can only propose.** Execution requires human co-sign (passkey). No single party can move funds.
18
18
  - **Wallet info is public key material.** Safe address, passkey X/Y, recovery signer — all safe to store.
19
19
  - **Recovery key is user-held.** The 12-word phrase generates a third signer the user controls. If the agent key or passkey is lost, the user can still recover funds — the agent never has unilateral access.
20
- - **NodPay server is stateless.** It relays signed operations — no private keys, no custody, no accounts. If the server goes offline, funds stay safe on-chain.
20
+ - **NodPay server is a stateless relay.** It forwards signed operations to the chain — no private keys, no custody, no accounts. Self-hostable via `OP_STORE_URL`. If the server goes offline, funds stay safe on-chain.
21
21
  - **The web app is a convenience layer.** It helps users create wallets and approve transactions. All crypto operations happen client-side; nothing sensitive is stored server-side.
22
22
  - **Verify the agent address matches yours** before storing wallet info. Mismatch = wrong key binding.
23
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodpay",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "NodPay CLI — propose on-chain payments from agent-human shared wallets",
5
5
  "type": "module",
6
6
  "bin": {
@@ -6,13 +6,16 @@
6
6
  * The agent signs first (1 of 2). The serialized SafeOperation is
7
7
  * output so the web app can have the user co-sign and submit.
8
8
  *
9
- * Agent key is read from .nodpay/.env (run `npx nodpay keygen` to generate).
9
+ * Agent key: read from .nodpay/.env (never from env vars or CLI args).
10
+ * Chain config: resolved via --chain from @nodpay/core networks registry.
11
+ * Bundler: NodPay public proxy (override with OP_STORE_URL for self-hosted).
10
12
  *
11
13
  * Args:
14
+ * --chain <name> - Chain name (ethereum, base, sepolia, etc.)
12
15
  * --to <address> - Recipient address
13
16
  * --value-eth <amount> - Value in ETH (default: 0)
14
17
  * --purpose <text> - Human-readable purpose
15
- * --safe <address> - Override SAFE_ADDRESS
18
+ * --safe <address> - Wallet (Safe) address
16
19
  * --counterfactual - Safe not yet deployed; include deployment in UserOp
17
20
  * --user-signer <address> - User's signer address (required for counterfactual)
18
21
  * --salt <nonce> - Salt nonce (required for counterfactual)
@@ -43,6 +46,9 @@ const chainArg = process.argv.includes('--chain')
43
46
  ? process.argv[process.argv.indexOf('--chain') + 1]
44
47
  : null;
45
48
 
49
+ // CHAIN RESOLUTION: --chain flag looks up RPC and chain ID from @nodpay/core's
50
+ // network registry (public RPC endpoints). No secrets involved — these are the
51
+ // same public endpoints listed on chainlist.org.
46
52
  let RPC_URL, CHAIN_ID;
47
53
  if (chainArg) {
48
54
  const net = allChains[chainArg];
@@ -50,14 +56,10 @@ if (chainArg) {
50
56
  console.error(`Error: Unknown chain "${chainArg}". Supported: ${Object.keys(allChains).join(', ')}`);
51
57
  process.exit(1);
52
58
  }
53
- RPC_URL = process.env.RPC_URL || net.rpcUrl;
59
+ RPC_URL = net.rpcUrl;
54
60
  CHAIN_ID = String(net.chainId);
55
61
  } else {
56
- RPC_URL = process.env.RPC_URL;
57
- CHAIN_ID = process.env.CHAIN_ID;
58
- }
59
- if (!RPC_URL || !CHAIN_ID) {
60
- console.error('Error: Specify --chain <name> or set RPC_URL + CHAIN_ID env vars.\nSupported chains: ' + Object.keys(allChains).join(', '));
62
+ console.error('Error: --chain is required.\nSupported: ' + Object.keys(allChains).join(', '));
61
63
  process.exit(1);
62
64
  }
63
65
  const ENTRYPOINT_ADDRESS = ENTRYPOINT;
@@ -81,17 +83,28 @@ function loadAgentKey() {
81
83
  return null;
82
84
  }
83
85
  const NODPAY_AGENT_KEY = loadAgentKey();
84
- const DEFAULT_SAFE = process.env.SAFE_ADDRESS;
85
-
86
- // Safe4337Pack.init requires a bundlerUrl it calls eth_chainId during init.
87
- // Use the NodPay server's bundler proxy so agents don't need their own bundler key.
88
- // Fallback to pimlico if PIMLICO_API_KEY is set (for local dev/testing).
89
- const PIMLICO_API_KEY = process.env.PIMLICO_API_KEY;
90
- const isProd = (process.env.NODE_ENV || 'production') === 'production';
91
- const opStoreBase = process.env.OP_STORE_URL || (isProd ? 'https://nodpay.ai/api' : 'http://localhost:8766');
92
- const BUNDLER_URL = PIMLICO_API_KEY
93
- ? `https://api.pimlico.io/v2/${CHAIN_ID}/rpc?apikey=${PIMLICO_API_KEY}`
94
- : `${opStoreBase}/bundler/${CHAIN_ID}`;
86
+ const DEFAULT_SAFE = null; // always use --safe flag
87
+
88
+ // BUNDLER: NodPay provides a public bundler proxy at nodpay.ai/api/bundler so
89
+ // agents don't need their own bundler API key. This is a thin relay — it
90
+ // forwards the UserOp to a bundler service and returns the result. The proxy
91
+ // only sees the already-signed (partial) UserOp; it cannot modify or execute it.
92
+ // For self-hosted setups, set OP_STORE_URL in .nodpay/.env.
93
+ function loadDotEnvVar(name, fallback) {
94
+ try {
95
+ const envPath = join(process.cwd(), '.nodpay', '.env');
96
+ const lines = readFileSync(envPath, 'utf8').split('\n');
97
+ for (const line of lines) {
98
+ const trimmed = line.trim();
99
+ if (trimmed.startsWith('#') || !trimmed.includes('=')) continue;
100
+ const [k, ...rest] = trimmed.split('=');
101
+ if (k.trim() === name) return rest.join('=').trim().replace(/^["']|["']$/g, '');
102
+ }
103
+ } catch {}
104
+ return fallback;
105
+ }
106
+ const opStoreBase = loadDotEnvVar('OP_STORE_URL', 'https://nodpay.ai/api');
107
+ const BUNDLER_URL = `${opStoreBase}/bundler/${CHAIN_ID}`;
95
108
 
96
109
  if (!NODPAY_AGENT_KEY) {
97
110
  console.error(JSON.stringify({ error: 'Missing NODPAY_AGENT_KEY in .nodpay/.env — run npx nodpay keygen first' }));
@@ -320,8 +333,7 @@ try {
320
333
  const customNonceArg = getArg('--nonce');
321
334
  const reuseGasFrom = getArg('--reuse-gas-from'); // shortHash of a previous op to copy gas values from
322
335
  let txOptions = {};
323
- const isProd = (process.env.NODE_ENV || 'production') === 'production';
324
- const opStoreUrl = process.env.OP_STORE_URL || (isProd ? 'https://nodpay.ai/api' : 'http://localhost:8766');
336
+ const opStoreUrl = opStoreBase;
325
337
  const safeAddr = await safe4337Pack.protocolKit.getAddress();
326
338
 
327
339
  // Determine nonce: on-chain nonce is the source of truth.
@@ -507,7 +519,7 @@ try {
507
519
  result.opStoreError = storeData.error || `HTTP ${storeRes.status}`;
508
520
  }
509
521
  if (storeData.shortHash) {
510
- const webBase = process.env.WEB_APP_URL || (isProd ? 'https://nodpay.ai' : 'https://bot.xhyumiracle.com/nodpay/index.html');
522
+ const webBase = loadDotEnvVar('WEB_APP_URL', 'https://nodpay.ai/');
511
523
  const purposeParam = purpose && purpose !== 'Unspecified' ? `&purpose=${encodeURIComponent(purpose)}` : '';
512
524
  approveUrl = `${webBase}approve?safeOpHash=${storeData.safeOpHash}${purposeParam}`;
513
525
  result.approveUrl = approveUrl;