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 +1 -1
- package/package.json +1 -1
- package/scripts/propose.mjs +34 -22
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
|
|
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
package/scripts/propose.mjs
CHANGED
|
@@ -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
|
|
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> -
|
|
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 =
|
|
59
|
+
RPC_URL = net.rpcUrl;
|
|
54
60
|
CHAIN_ID = String(net.chainId);
|
|
55
61
|
} else {
|
|
56
|
-
|
|
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 =
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
|
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 =
|
|
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;
|