nodpay 0.2.39 → 0.2.41
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/package.json +1 -1
- package/scripts/propose.mjs +83 -15
package/package.json
CHANGED
package/scripts/propose.mjs
CHANGED
|
@@ -113,6 +113,18 @@ if (_remoteWalletUrl) {
|
|
|
113
113
|
}
|
|
114
114
|
AGENT_ADDRESS = wallets[0].agentSigner;
|
|
115
115
|
|
|
116
|
+
// remote_wallet: extract passkey/recovery from wallet data for approvalContext
|
|
117
|
+
// (CLI args take precedence if provided, but agents in remote_wallet mode don't pass them)
|
|
118
|
+
const rw = wallets[0];
|
|
119
|
+
if (!passkeyX && rw.humanSignerPasskeyX) {
|
|
120
|
+
passkeyX = rw.humanSignerPasskeyX;
|
|
121
|
+
passkeyY = rw.humanSignerPasskeyY;
|
|
122
|
+
isPasskey = true;
|
|
123
|
+
}
|
|
124
|
+
if (!recoverySigner && rw.recoverySigner) {
|
|
125
|
+
recoverySigner = rw.recoverySigner;
|
|
126
|
+
}
|
|
127
|
+
|
|
116
128
|
signHash = async (hash) => {
|
|
117
129
|
const signRes = await fetch(`${_remoteWalletUrl.replace(/\/$/, '')}/sign`, {
|
|
118
130
|
method: 'POST',
|
|
@@ -159,12 +171,12 @@ const humanSigner = getArg('--human-signer-eoa');
|
|
|
159
171
|
const salt = getArg('--salt') || '1001';
|
|
160
172
|
|
|
161
173
|
// Passkey support
|
|
162
|
-
|
|
163
|
-
|
|
174
|
+
let passkeyX = getArg('--human-signer-passkey-x');
|
|
175
|
+
let passkeyY = getArg('--human-signer-passkey-y');
|
|
164
176
|
const passkeyRawId = getArg('--passkey-raw-id');
|
|
165
177
|
const passkeyVerifier = getArg('--passkey-verifier') || '0x445a0683e494ea0c5AF3E83c5159fBE47Cf9e765';
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
let recoverySigner = getArg('--recovery-signer');
|
|
179
|
+
let isPasskey = !!(passkeyX && passkeyY);
|
|
168
180
|
|
|
169
181
|
// remote_wallet + --human-signer-eoa is not supported
|
|
170
182
|
if (_remoteWalletUrl && humanSigner) {
|
|
@@ -177,19 +189,53 @@ if (!to) {
|
|
|
177
189
|
process.exit(1);
|
|
178
190
|
}
|
|
179
191
|
|
|
192
|
+
// ENS support: if --to is not a raw 0x address, try resolving as ENS name.
|
|
193
|
+
// ENS registry lives on Ethereum mainnet — always resolve against mainnet RPC,
|
|
194
|
+
// regardless of the target --chain.
|
|
195
|
+
let resolvedTo = to;
|
|
180
196
|
if (!ethers.isAddress(to)) {
|
|
181
|
-
|
|
182
|
-
|
|
197
|
+
if (!to.includes('.')) {
|
|
198
|
+
console.error(JSON.stringify({ error: `Invalid recipient address: ${to} (not a valid address or ENS name)` }));
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
const ensRpcUrl = NETWORKS.mainnet.ethereum?.rpcUrl;
|
|
203
|
+
if (!ensRpcUrl) {
|
|
204
|
+
console.error(JSON.stringify({ error: `Cannot resolve ENS name "${to}": no Ethereum mainnet RPC available` }));
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
const ensProvider = new ethers.JsonRpcProvider(ensRpcUrl);
|
|
208
|
+
const resolved = await ensProvider.resolveName(to);
|
|
209
|
+
if (!resolved) {
|
|
210
|
+
console.error(JSON.stringify({ error: `Cannot resolve ENS name: ${to}` }));
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
resolvedTo = resolved;
|
|
214
|
+
console.error(`[INFO] Resolved ENS ${to} → ${resolved}`);
|
|
215
|
+
} catch (e) {
|
|
216
|
+
console.error(JSON.stringify({ error: `Cannot resolve ENS name "${to}": ${e.message}` }));
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
183
219
|
}
|
|
184
220
|
|
|
185
221
|
const SAFE_ADDRESS = safeOverride || DEFAULT_SAFE;
|
|
186
222
|
|
|
187
223
|
// Read optional wallet JSON for rpId (SafeClaw cross-origin passkey support)
|
|
188
224
|
// In remote_wallet mode, wallets were already fetched — use that data.
|
|
225
|
+
// rpId MUST be present in approveUrl when available, otherwise the web app
|
|
226
|
+
// defaults to rpId=nodpay.ai which causes passkey mismatch errors.
|
|
189
227
|
let walletRpId = null;
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
if (
|
|
228
|
+
if (_remoteWalletUrl && _remoteWalletsCache) {
|
|
229
|
+
// Try exact safe match first
|
|
230
|
+
if (SAFE_ADDRESS) {
|
|
231
|
+
const match = _remoteWalletsCache.find(w => w.safe?.toLowerCase() === SAFE_ADDRESS.toLowerCase());
|
|
232
|
+
if (match?.rpId) walletRpId = match.rpId;
|
|
233
|
+
}
|
|
234
|
+
// Fallback: use rpId from any wallet in cache (remote proxy typically serves one wallet set)
|
|
235
|
+
if (!walletRpId) {
|
|
236
|
+
const withRpId = _remoteWalletsCache.find(w => w.rpId);
|
|
237
|
+
if (withRpId) walletRpId = withRpId.rpId;
|
|
238
|
+
}
|
|
193
239
|
}
|
|
194
240
|
if (!walletRpId && SAFE_ADDRESS) {
|
|
195
241
|
const walletJsonPath = join(HOME, '.nodpay', 'wallets', `${SAFE_ADDRESS}.json`);
|
|
@@ -478,7 +524,7 @@ try {
|
|
|
478
524
|
|
|
479
525
|
// Create the transaction as a SafeOperation (UserOp wrapper)
|
|
480
526
|
const safeOperation = await safe4337Pack.createTransaction({
|
|
481
|
-
transactions: [{ to, value, data: '0x' }],
|
|
527
|
+
transactions: [{ to: resolvedTo, value, data: '0x' }],
|
|
482
528
|
options: txOptions,
|
|
483
529
|
});
|
|
484
530
|
|
|
@@ -533,7 +579,7 @@ try {
|
|
|
533
579
|
userOpHash: entryPointUserOpHash,
|
|
534
580
|
safeOpHash,
|
|
535
581
|
shortId,
|
|
536
|
-
to,
|
|
582
|
+
to: resolvedTo,
|
|
537
583
|
value,
|
|
538
584
|
valueEth,
|
|
539
585
|
safeAddress,
|
|
@@ -548,12 +594,32 @@ try {
|
|
|
548
594
|
writeFileSync(join(PENDING_DIR, `4337-${shortId}.json`), JSON.stringify(result, null, 2));
|
|
549
595
|
|
|
550
596
|
// Store to op-store API for hash-based web app lookup
|
|
551
|
-
//
|
|
552
|
-
|
|
597
|
+
// Build approvalContext: server-side approval context (replaces localStorage dependency)
|
|
598
|
+
const approvalContext = isPasskey
|
|
599
|
+
? {
|
|
600
|
+
version: 1,
|
|
601
|
+
signerType: 'passkey',
|
|
602
|
+
rpId: walletRpId || 'nodpay.ai',
|
|
603
|
+
passkeyX,
|
|
604
|
+
passkeyY,
|
|
605
|
+
recoveryAddress: recoverySigner || null,
|
|
606
|
+
safeAddress,
|
|
607
|
+
chainId: parseInt(CHAIN_ID, 10),
|
|
608
|
+
}
|
|
609
|
+
: humanSigner
|
|
610
|
+
? {
|
|
611
|
+
version: 1,
|
|
612
|
+
signerType: 'eoa',
|
|
613
|
+
humanSignerAddress: humanSigner,
|
|
614
|
+
safeAddress,
|
|
615
|
+
chainId: parseInt(CHAIN_ID, 10),
|
|
616
|
+
}
|
|
617
|
+
: null;
|
|
618
|
+
|
|
553
619
|
const storePayload = {
|
|
554
620
|
safeOperationJson,
|
|
555
621
|
userOpHash: entryPointUserOpHash,
|
|
556
|
-
to,
|
|
622
|
+
to: resolvedTo,
|
|
557
623
|
value,
|
|
558
624
|
valueEth,
|
|
559
625
|
safeAddress,
|
|
@@ -562,10 +628,12 @@ try {
|
|
|
562
628
|
agent: AGENT_ADDRESS,
|
|
563
629
|
agentSignature: safeOperationJson.signatures,
|
|
564
630
|
createdAt: new Date().toISOString(),
|
|
565
|
-
//
|
|
631
|
+
// Legacy fields (kept for URL-param fallback during transition)
|
|
566
632
|
passkeyX: passkeyX || null,
|
|
567
633
|
passkeyY: passkeyY || null,
|
|
568
634
|
recoveryAddress: recoverySigner || null,
|
|
635
|
+
// Server-side approval context
|
|
636
|
+
approvalContext,
|
|
569
637
|
};
|
|
570
638
|
|
|
571
639
|
// Extract raw agent signature for server auth
|