nodpay 0.2.32 → 0.2.34

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
@@ -31,7 +31,7 @@ You propose payments, your human approves with one tap. 2-of-3 multisig — you
31
31
  | **Zero Trust** | **End-to-end verification:** no party is implicitly trusted. Server validates signatures; client and CLI independently verify server responses (decode calldata → recompute hash → recover signer → check owner set). The blockchain serves as the canonical source of truth. |
32
32
  | **Disaster Recovery** | **Key redundancy & continuity:** uses a locally-stored 12-word mnemonic as recovery signer. Any two of the three signers can reconstruct authority to unlock the wallet, ensuring the user is never locked out by a single lost credential. |
33
33
  | **Hardened Key Isolation** | `keygen` writes to `~/.nodpay/.env` (chmod 600). The CLI reads the key via file I/O at runtime — not passed through CLI arguments, environment variables, or stdout. Only the public address is returned to the caller. |
34
- | **Keyless & Non-Custodial Server** | **Stateless relayer:** the server stores no private keys and maintains no session state that could compromise assets. All signing happens locally. Funds stay on-chain if the server goes offline. |
34
+ | **Keyless & Non-Custodial Server** | **Stateless relayer:** the server stores no private keys, no passkey credentials, and no session state. Identity verification uses CREATE2 address prediction + cryptographic signatures — zero trusted storage. Funds stay on-chain if the server goes offline. |
35
35
 
36
36
  All wallet parameters (Safe address, passkey X/Y, recovery signer address) are public key material — safe to store, pass in URLs, and include in CLI flags.
37
37
 
@@ -181,16 +181,18 @@ Wallet address is the same across all chains. **Ask which chain if not specified
181
181
 
182
182
  ## Reconnect
183
183
 
184
- Browser data cleared? Build a reconnect link from the wallet's stored parameters (all public — no secrets):
184
+ Browser data cleared? Build a reconnect link from the wallet's stored parameters (all public — no secrets).
185
+
186
+ **You MUST include `signerType`** — the client uses it to show the correct verification flow.
185
187
 
186
188
  **Passkey:**
187
189
  ```
188
- https://nodpay.ai/?agent=AGENT_SIGNER&safe=SAFE_ADDRESS&recovery=RECOVERY_SIGNER&x=PASSKEY_X&y=PASSKEY_Y
190
+ https://nodpay.ai/?agent=AGENT_SIGNER&safe=SAFE_ADDRESS&recovery=RECOVERY_SIGNER&signerType=passkey&x=PASSKEY_X&y=PASSKEY_Y
189
191
  ```
190
192
 
191
193
  **EOA:**
192
194
  ```
193
- https://nodpay.ai/?agent=AGENT_SIGNER&safe=SAFE_ADDRESS&recovery=RECOVERY_SIGNER&eoa=HUMAN_SIGNER_EOA
195
+ https://nodpay.ai/?agent=AGENT_SIGNER&safe=SAFE_ADDRESS&recovery=RECOVERY_SIGNER&signerType=eoa&eoa=HUMAN_SIGNER_EOA
194
196
  ```
195
197
 
196
- User opens → verifies identity → wallet restored.
198
+ User opens → verifies identity (passkey assertion or MetaMask connect) → wallet restored.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodpay",
3
- "version": "0.2.32",
3
+ "version": "0.2.34",
4
4
  "description": "NodPay CLI — propose on-chain payments from agent-human shared wallets",
5
5
  "type": "module",
6
6
  "bin": {
@@ -309,7 +309,52 @@ try {
309
309
 
310
310
  const safe4337Pack = await Safe4337Pack.init(initOptions);
311
311
 
312
- const safeAddress = await safe4337Pack.protocolKit.getAddress();
312
+ let safeAddress = await safe4337Pack.protocolKit.getAddress();
313
+
314
+ // --- Ethereum mainnet: force L2 singleton for cross-chain address consistency ---
315
+ //
316
+ // Safe SDK defaults to the L1 singleton on chainId 1, but all other chains use the L2
317
+ // singleton. Different singleton → different CREATE2 address → cross-chain mismatch.
318
+ //
319
+ // The L2 singleton (0x29fc...) IS deployed on Ethereum mainnet. Using it there costs
320
+ // ~2.3% more gas per tx (~$0.14 at 15 Gwei) due to extra event emissions — acceptable.
321
+ //
322
+ // Trade-off: safe.global may not fully index L2-singleton Safes on mainnet (their L1
323
+ // indexer uses trace-based filtering by known singleton addresses). NodPay uses its own
324
+ // op-store, so this doesn't affect us.
325
+ //
326
+ // Long-term plan: support separate L1/L2 wallet families — user chooses at creation time.
327
+ // For now, we unify on L2 singleton across all chains for address consistency.
328
+ if (
329
+ isCounterfactual &&
330
+ CHAIN_ID === '1' &&
331
+ SAFE_ADDRESS &&
332
+ safeAddress.toLowerCase() !== SAFE_ADDRESS.toLowerCase()
333
+ ) {
334
+ const Safe = (await import('@safe-global/protocol-kit')).default;
335
+ const L2_SINGLETON = '0x29fcB43b46531BcA003ddC8FCB67FFE91900C762';
336
+ // getPredictedSafe() returns the full config Safe4337Pack assembled — owners, threshold,
337
+ // module setup (to/data), fallbackHandler, salt, etc. We re-init with the same config
338
+ // but force L2 singleton via contractNetworks (SDK public API).
339
+ const predictedSafe = safe4337Pack.protocolKit.getPredictedSafe();
340
+ safe4337Pack.protocolKit = await Safe.init({
341
+ provider: RPC_URL,
342
+ signer: initOptions.signer,
343
+ isL1SafeSingleton: false,
344
+ contractNetworks: { [CHAIN_ID]: { safeSingletonAddress: L2_SINGLETON } },
345
+ predictedSafe,
346
+ });
347
+ safeAddress = await safe4337Pack.protocolKit.getAddress();
348
+ }
349
+
350
+ // Safety check: if --safe was explicitly given, the computed address MUST match.
351
+ // Mismatch means owner set / salt / SDK version differs from wallet creation — abort.
352
+ if (SAFE_ADDRESS && safeAddress.toLowerCase() !== SAFE_ADDRESS.toLowerCase()) {
353
+ console.error(JSON.stringify({
354
+ error: `Address mismatch: --safe ${SAFE_ADDRESS} but SDK computed ${safeAddress}. Check --human-signer-eoa, --recovery-signer, and --salt match the original wallet creation params.`
355
+ }));
356
+ process.exit(1);
357
+ }
313
358
 
314
359
  // Auto-detect deployment status: if Safe is already deployed, drop counterfactual
315
360
  if (isCounterfactual) {